Permalink
Browse files

basic ugen and synthdef encoding, problems with control names

  • Loading branch information...
1 parent 9f3e942 commit bb83d980e04ebff9cef3a399b176cb04820dc68e Macario committed Nov 3, 2008
@@ -1,7 +1,7 @@
module Scruby
module Audio
class ControlName
- attr_accessor :name, :value, :rate, :index
+ attr_accessor :name, :value, :rate, :index,
RATES = { 'n_' => :noncontrol, 'i_' => :scalar, 'k_' => :control, 't_' => :trigger }
def initialize( name, value, rate, index )
@@ -0,0 +1,83 @@
+class Env
+
+ attr_accessor :levels, :times, :curves, :release_node, :array
+ SHAPE_NAMES = {
+ :step => 0,
+ :lin => 1,
+ :linear => 1,
+ :exp => 2,
+ :exponential => 2,
+ :sin => 3,
+ :sine => 3,
+ :wel => 4,
+ :welch => 4,
+ :sqr => 6,
+ :squared => 6,
+ :cub => 7,
+ :cubed => 7
+ }
+
+ def initialize( levels, times, curves = :lin, release_node = nil, loop_node = nil )
+ @levels, @times, @curves, @release_node, @loop_node = levels, times, curves.to_array, release_node, loop_node
+ raise ArgumentError, "levels and times should be array" unless levels.instance_of?(Array) and times.instance_of?(Array)
+ end
+
+ class << self
+ # fixed duration envelopes
+ def triangle(dur = 1.0, level = 1.0)
+ dur = (dur * 0.5)
+ new( [0, level, 0], [dur, dur] )
+ end
+
+ def sine(dur = 1.0, level = 1.0)
+ dur = (dur * 0.5)
+ new( [0, level, 0], [dur, dur], :sine)
+ end
+
+ def perc( attackTime=0.01, releaseTime=1.0, level=1.0, curve = -4.0 )
+ new( [0, level, 0], [attackTime, releaseTime], curve )
+ end
+
+ def linen( attackTime=0.01, sustainTime=1.0, releaseTime=1.0, level=1.0, curve = :lin )
+ new [0, level, level, 0], [attackTime, sustainTime, releaseTime], curve
+ end
+
+ def cutoff( releaseTime = 0.1, level = 1.0, curve = :lin )
+ new( [level, 0], [releaseTime], curve, 0 )
+ end
+
+ def dadsr( delayTime=0.1, attackTime=0.01, decayTime=0.3, sustainLevel=0.5, releaseTime=1.0, peakLevel=1.0, curve = -4.0, bias = 0.0 )
+ new( [0, 0, peakLevel, peakLevel * sustainLevel, 0].collect{ |e| e + bias }, [delayTime, attackTime, decayTime, releaseTime], curve, 3 )
+ end
+
+ def adsr( attackTime=0.01, decayTime=0.3, sustainLevel=0.5, releaseTime=1.0, peakLevel=1.0, curve = -4.0, bias = 0.0 )
+ new( [0, peakLevel, peakLevel * sustainLevel, 0].collect{ |e| e + bias }, [attackTime, decayTime, releaseTime], curve, 2 )
+ end
+
+ def asr( attackTime=0.01, sustainLevel=1.0, releaseTime=1.0, curve = -4.0 )
+ new( [0, sustainLevel, 0], [attackTime, releaseTime], curve, 1 )
+ end
+
+ named_args_for :triangle, :sine, :perc, :linen, :cutoff, :dadsr, :adsr, :asr
+ end
+
+
+ def to_array
+ *contents = levels[0], times.size, @release_node || -99, @loop_node || -99
+ contents + levels[1..-1].wrap_and_zip( times, shape_numbers, curve_values ).flatten
+ end
+
+ def shape_numbers
+ curves.collect do |curve|
+ curve.valid_ugen_input? ? 5 : SHAPE_NAMES[curve]
+ end
+ end
+
+ def curve_values
+ curves.collect do |curve|
+ curve.valid_ugen_input? ? curve : 0
+ end
+ end
+
+
+end
@@ -13,13 +13,31 @@ def initialize( name, options = {}, &block )
@control_names = collect_control_names( block, values, rates )
build_ugen_graph( block, @control_names )
@constants = collect_constants( @children )
-
+
+ @variants = [] #stub!!
+
warn( 'A SynthDef without a block is useless' ) unless block_given?
end
+ #Sending
+ def encode
+ controls = @control_names.reject { |cn| cn.non_control? }
+ encoded_controls = [controls.size].pack('n') + controls.collect{ |c| c.name.encode + [c].index.pack('n') }.to_s
+
+ init_stream + name.encode + constants.encode_floats + values.flatten.encode_floats + encoded_controls +
+ [children.size].pack('n') + children.collect{ |u| u.encode }.join('') +
+ [@variants.size].pack('n') #stub!!!
+ end
+
+ def init_stream(file_version = 1, number_of_synths = 1) #:nodoc:
+ 'SCgf' + [file_version].pack('N') + [number_of_synths].pack('n')
+ end
+
+ def values
+ @control_names.collect{ |control| control.value }
+ end
+
private
- attr_writer :name, :children, :constants, :control_names
-
def collect_control_names( function, values, rates ) #:nodoc:
return [] if (names = function.argument_names).empty?
names.zip( values, rates ).collect_with_index{ |array, index| ControlName.new *(array << index) }
@@ -43,7 +61,6 @@ def collect_constants( children ) #:nodoc:
children.send( :collect_constants ).flatten.uniq
end
- #to ugen
end
end
end
@@ -0,0 +1,72 @@
+module Scruby
+ module Audio
+ module Ugens
+
+ class EnvGen < Ugen
+ class << self
+ def ar( envelope, gate = 1.0, levelScale = 1.0, levelBias = 0.0, timeScale = 1.0, doneAction = 0 )
+ new(:audio, gate, levelScale, levelBias, timeScale, doneAction, *envelope.to_array)
+ end
+
+ def kr( envelope, gate = 1.0, levelScale = 1.0, levelBias = 0.0, timeScale = 1.0, doneAction = 0 )
+ new(:control, gate, levelScale, levelBias, timeScale, doneAction, *envelope.to_array)
+ end
+ end
+ end
+
+ class Done < Ugen
+ def self.kr( src )
+ self.init( :control, src )
+ end
+ end
+
+ class FreeSelf < Ugen
+ def self.kr( input )
+ self.init(:control, input)
+ input
+ end
+ end
+
+ class PauseSelf < Ugen
+ def self.kr(input)
+ self.init(:control, input)
+ input
+ end
+ end
+
+ class FreeSelfWhenDone < Ugen
+ def self.kr(src)
+ self.init(:control, src)
+ end
+ end
+
+ class PauseSelfWhenDone < Ugen
+ def self.kr(src)
+ self.init(:control, src)
+ end
+ end
+
+ class Pause < Ugen
+ def self.kr(gate, id)
+ self.init(:control, gate, id)
+ end
+ end
+
+ class Free < Ugen
+ def self.kr(trig, id)
+ self.init(:control, trig, id)
+ end
+ end
+
+ class Linen < Ugen
+ class << self
+ def kr( gate = 1.0, attackTime = 0.01, susLevel = 1.0, releaseTime = 1.0, doneAction = 0 )
+ new( :control, gate, attackTime, susLevel, releaseTime, doneAction )
+ end
+ named_args_for :kr
+ end
+ end
+
+ end
+ end
+end
@@ -2,16 +2,23 @@ module Scruby
module Audio
module Ugens
class Ugen
- attr_reader :inputs, :rate, :index
-
- RATES = [ :scalar, :trigger, :demand, :control, :audio ]
+ attr_reader :inputs, :rate, :index, :special_index, :output_index, :outputs
+
+ RATES = [ :scalar, :trigger, :demand, :control, :audio ]
+ E_RATES = [ :scalar, :control, :audio, :demand ]
@@synthdef = nil
include UgenOperations
def initialize( rate, *inputs)
@rate, @inputs = rate, inputs
- @index = add_to_synthdef
+
+ #defaults
+ @special_index ||= 0
+ @output_index ||= 0
+ @outputs ||= [1]
+
+ @index = add_to_synthdef || 0
end
def muladd( mul, add )
@@ -26,6 +33,10 @@ def ugen?
true
end
+ def encode
+ self.class.to_s.encode + [E_RATES.index(rate)].pack('w') + [inputs.size, outputs.size, special_index, collect_input_specs].flatten.pack('n*') + [output_specs].pack('w')
+ end
+
private
def synthdef
@synthdef ||= self.class.synthdef
@@ -38,6 +49,18 @@ def add_to_synthdef #:nodoc:
def collect_constants
@inputs.send( :collect_constants )
end
+
+ def input_specs( synthdef )
+ [index, output_index]
+ end
+
+ def collect_input_specs
+ @inputs.collect{ |i| i.send( :input_specs, synthdef ) }
+ end
+
+ def output_specs
+ E_RATES.index(rate)
+ end
class << self
def new( rate, *inputs ) #:nodoc:
@@ -17,17 +17,34 @@ class Numeric
def rate
:scalar
end
+
+ def max( other )
+ self > other ? self : other
+ end
+
+ def min( other )
+ self < other ? self : other
+ end
end
class Numeric
private
def collect_constants #:nodoc:
self
end
+
+ def input_specs( synthdef )
+ [-1, synthdef.constants.index(self)]
+ end
end
class Fixnum
include Scruby::Audio::Ugens::UgenOperations
+
+ def encode
+ [self >> 8,0,self,0]
+ end
+
end
class Float
@@ -54,11 +71,21 @@ def wrap_to!( size )
size.times { |i| self[ i ] = self[ i % original_size ] }
self
end
+
+ def wrap_and_zip( *args )
+ max = args.map{ |a| instance_of?(Array) ? a.size : 0 }.max.max( self.size )
+ args = args.collect{ |a| a.to_array.wrap_to( max ) }
+ self.wrap_to( max ).zip( *args )
+ end
def to_array
self
end
+ def encode_floats
+ [self.size].pack('n') + self.pack('g*')
+ end
+
private
def collect_constants #:nodoc:
self.collect{ |e| e.send( :collect_constants ) }
@@ -75,3 +102,9 @@ def argument_names
end
end
+class String
+ def encode
+ [self.size & 255].pack('C*') + self[0..255]
+ end
+end
+
@@ -0,0 +1,23 @@
+require File.join( File.expand_path(File.dirname(__FILE__)), '..', "helper")
+
+require 'named_arguments'
+require "#{LIB_DIR}/audio/ugens/ugen_operations"
+require "#{LIB_DIR}/audio/ugens/ugen"
+require "#{LIB_DIR}/extensions"
+require "#{LIB_DIR}/audio/env"
+require "#{LIB_DIR}/audio/ugens/env_gen"
+
+include Scruby
+include Audio
+include Ugens
+
+
+describe EnvGen do
+
+ it "should have correct inputs" do
+ envgen = EnvGen.kr( Env.adsr )
+ envgen.rate.should == :control
+ envgen.inputs.should == [ 1, 1, 0, 1, 0, 0, 3, 2, -99, 1, 0.01, 5, -4, 0.5, 0.3, 5, -4, 0, 1, 5, -4 ].collect{ |i| i.to_f }
+ end
+
+end
@@ -1,8 +1,12 @@
require File.join( File.expand_path(File.dirname(__FILE__)), '..',"helper")
+require "named_arguments"
+
require "#{LIB_DIR}/audio/ugens/ugen_operations"
require "#{LIB_DIR}/audio/ugens/ugen"
+
require "#{LIB_DIR}/audio/ugens/operation_ugens"
+require "#{LIB_DIR}/audio/ugens/ugens"
require "#{LIB_DIR}/audio/control_name"
require "#{LIB_DIR}/audio/synthdef"
require "#{LIB_DIR}/extensions"
@@ -29,7 +33,22 @@
@sdef.send :build_ugen_graph, function, []
Ugen.new( :audio, 100, 200 ).send( :synthdef ).should eql( nil )
end
-
+end
-end
+describe "encoding examples" do
+
+ before :all do
+ @sdef =
+ SynthDef.new(:cacahuate, :values => [1, 456, 0.34, 0.45]) do |gate, freq, ancho, amp|
+ # sig = Pulse.ar( freq, ancho, amp, 0 )
+ # env = EnvGen.kr( Env.asr(2, 1, 1), gate, :doneAction => 2 )
+ # Out.ar( 0, sig*env )
+ end
+ end
+
+ it do
+ end
+
+
+end
Oops, something went wrong.

0 comments on commit bb83d98

Please sign in to comment.