forked from maca/scruby
/
synthdef.rb
109 lines (97 loc) · 4.18 KB
/
synthdef.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
module Scruby
class SynthDef
attr_reader :name, :children, :constants, :control_names
# Creates a new SynthDef instance
# An "ugen graph" block should be passed:
#
# SynthDef.new('simple') do |rate|
# Out.ar( 0, SinOsc.ar(rate) )
# end
#
# Default values and rates for the block can be passed with the <tt>:values => []</tt> and <tt>:rates => []</tt> options:
# E.g.
# SynthDef.new( :am, :values => [1, 1000, 10, 1] ) do |gate, portadora, moduladora, amp|
# modulacion = SinOsc.kr( moduladora, 0, 0.5, 0.5 )
# sig = SinOsc.ar( portadora, 0, modulacion )
# env = EnvGen.kr( Env.asr(2,1,2), gate, :doneAction => 2 )
# Out.ar( 0, sig*env*amp )
# end
#
# is equivalent to the Sclang SynthDef
# SynthDef(\am, {|gate=1, portadora=1000, moduladora=10, amp=1|
# var modulacion, sig, env;
# modulacion = SinOsc.kr(moduladora, 0, 0.5, 0.5);
# sig = SinOsc.ar(portadora, 0, modulacion);
# env = EnvGen.kr(Env.asr(2,1,2), gate, doneAction:2);
# Out.ar(0, sig*env*amp);
# }).send(s)
#
def initialize name, options = {}, &block
@name, @children = name.to_s, []
raise( ArgumentError.new('An UGen graph (block) must be passed') ) unless block_given?
values = options.delete( :values ) || []
rates = options.delete( :rates ) || []
@control_names = collect_control_names block, values, rates
build_ugen_graph block, @control_names
@constants = collect_constants @children
@variants = [] #stub!!!
end
# Returns a string representing the encoded SynthDef in a way scsynth can interpret and generate.
# This method is called by a server instance when sending the synthdef via OSC.
#
# For complex synthdefs the encoded synthdef can vary a little bit from what SClang would generate
# but the results will be interpreted in the same way
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') }.join
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 #:nodoc:
@control_names.collect{ |control| control.value }
end
alias :send_msg :send
# Sends itself to the given servers. One or more servers or an array of servers can be passed.
# If no arguments are given the synthdef gets sent to all instantiated servers
# E.g.
# s = Server.new('localhost', 5114)
# s.boot
# r = Server.new('127.1.1.2', 5114)
#
# SynthDef.new('sdef'){ Out.ar(0, SinOsc.ar(220)) }.send(s)
# # this synthdef is only sent to s
#
# SynthDef.new('sdef2'){ Out.ar(1, SinOsc.ar(222)) }.send
# # this synthdef is sent to both s and r
#
def send *servers
servers.peel!
(servers.empty? ? Server.all : servers).each{ |s| s.send_synth_def( self ) }
self
end
private
def collect_control_names function, values, rates
names = function.arguments
names.zip( values, rates ).collect_with_index{ |array, index| ControlName.new *(array << index) }
end
def build_controls control_names
# control_names.select{ |c| c.rate == :noncontrol }.sort_by{ |c| c.control_name.index } +
[:scalar, :trigger, :control].collect do |rate|
same_rate_array = control_names.select{ |control| control.rate == rate }
Control.and_proxies_from( same_rate_array ) unless same_rate_array.empty?
end.flatten.compact.sort_by{ |proxy| proxy.control_name.index }
end
def build_ugen_graph function, control_names
Ugen.synthdef = self
function.call *build_controls(control_names)
Ugen.synthdef = nil
end
def collect_constants children
children.send( :collect_constants ).flatten.compact.uniq
end
end
end