maca-Scruby 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/README.rdoc +26 -0
  2. data/Rakefile +10 -0
  3. data/Scruby.gemspec +36 -0
  4. data/bin/live_session.rb +12 -0
  5. data/changes +1 -0
  6. data/lib/live/session.rb +144 -0
  7. data/lib/scruby.rb +60 -0
  8. data/lib/scruby/audio/control_name.rb +29 -0
  9. data/lib/scruby/audio/env.rb +97 -0
  10. data/lib/scruby/audio/node.rb +20 -0
  11. data/lib/scruby/audio/server.rb +112 -0
  12. data/lib/scruby/audio/synth.rb +15 -0
  13. data/lib/scruby/audio/synthdef.rb +114 -0
  14. data/lib/scruby/audio/ugens/env_gen.rb +18 -0
  15. data/lib/scruby/audio/ugens/in_out.rb +43 -0
  16. data/lib/scruby/audio/ugens/multi_out_ugens.rb +48 -0
  17. data/lib/scruby/audio/ugens/operation_indices.yaml +92 -0
  18. data/lib/scruby/audio/ugens/operation_ugens.rb +64 -0
  19. data/lib/scruby/audio/ugens/ugen.rb +154 -0
  20. data/lib/scruby/audio/ugens/ugen_defs.yaml +3421 -0
  21. data/lib/scruby/audio/ugens/ugen_operations.rb +44 -0
  22. data/lib/scruby/audio/ugens/ugens.rb +34 -0
  23. data/lib/scruby/control/metro.rb +6 -0
  24. data/lib/scruby/extensions.rb +109 -0
  25. data/lib/scruby/typed_array.rb +64 -0
  26. data/spec/audio/env_gen_specs.rb +25 -0
  27. data/spec/audio/in_out_spec.rb +107 -0
  28. data/spec/audio/integration_spec.rb +106 -0
  29. data/spec/audio/lib_spec.rb +14 -0
  30. data/spec/audio/multiout_ugen_spec.rb +112 -0
  31. data/spec/audio/node_spec.rb +60 -0
  32. data/spec/audio/operation_ugens_spec.rb +189 -0
  33. data/spec/audio/server_spec.rb +68 -0
  34. data/spec/audio/synth_spec.rb +46 -0
  35. data/spec/audio/synthdef_spec.rb +275 -0
  36. data/spec/audio/ugen_operations_spec.rb +146 -0
  37. data/spec/audio/ugen_spec.rb +333 -0
  38. data/spec/audio/ugens_spec.rb +61 -0
  39. data/spec/env_spec.rb +64 -0
  40. data/spec/extensions_spec.rb +133 -0
  41. data/spec/helper.rb +11 -0
  42. data/spec/typed_array_spec.rb +95 -0
  43. metadata +129 -0
@@ -0,0 +1,112 @@
1
+ require 'singleton'
2
+
3
+ module Scruby
4
+ module Audio
5
+ class UDPSender < OSC::UDPServer #:nodoc:
6
+ include Singleton
7
+
8
+ alias :udp_send :send
9
+ def send( command, host, port, *args )
10
+ udp_send( OSC::Message.new( command, type_tag(args), *args ), 0, host, port )
11
+ end
12
+
13
+ def send_message( message, host, port )
14
+ udp_send( message, 0, host, port )
15
+ end
16
+
17
+ def type_tag(*args)
18
+ args = *args
19
+ args.collect{ |msg| OSC::Packet.tag( msg ) }.to_s
20
+ end
21
+ end
22
+ $UDP_Sender = UDPSender.instance
23
+
24
+ class Server
25
+ attr_reader :host, :port
26
+ @@sc_path = '/Applications/SuperCollider/scsynth'
27
+ @@servers = []
28
+
29
+ # Initializes and registers a new Server instance and sets the host and port for it.
30
+ # The server is a Ruby representation of scsynth which can be a local binary or a remote
31
+ # server already running.
32
+ # Server class keeps an array with all the instantiated servers
33
+ def initialize( host = 'localhost', port = 57111)
34
+ @host, @port = host, port
35
+ @@servers << self
36
+ end
37
+
38
+ # Boots the local binary of the scsynth forking a process, it will rise a SCError if the scsynth
39
+ # binary is not found in /Applications/SuperCollider/scsynt (default Mac OS path) or given path.
40
+ # The default path can be overriden using Server.scsynt_path=('path')
41
+ def boot
42
+ raise SCError.new('Scsynth not found in the given path') unless File.exists?( @@sc_path )
43
+ Thread.new do
44
+ path = @@sc_path.scan(/[^\/]+/)
45
+ @server_pipe = IO.popen( "cd /#{ path[0..-2].join('/') }; ./#{ path.last } -u #{ @port }" )
46
+ loop { p Special.new(@server_pipe.gets.chomp) }
47
+ end unless @server_pipe
48
+ end
49
+
50
+ # Sends the /quit OSC signal to the scsynth server and kills the forked process if the scsynth
51
+ # server is running locally
52
+ def stop
53
+ send '/quit'
54
+ Process.kill 'KILL', @server_pipe.pid if @server_pipe
55
+ @server_pipe = nil
56
+ end
57
+
58
+ # Sends an OSC command to the scsyth server.
59
+ # E.g. <tt>server.send('/dumpOSC', 1)</tt>
60
+ def send( command, *args )
61
+ return $UDP_Sender.send( command, @host, @port, *args )
62
+ end
63
+
64
+ # Encodes and sends a SynthDef to the scsynth server
65
+ def send_synth_def( synth_def )
66
+ *blob = OSC::Blob.new( synth_def.encode ), 0
67
+ def_message = OSC::Message.new( '/d_recv', $UDP_Sender.type_tag( blob ), *blob )
68
+ send_message( OSC::Bundle.new( nil, def_message ) )
69
+ end
70
+
71
+ private
72
+ def send_message( message ) #:nodoc:
73
+ $UDP_Sender.send_message( message, @host, @port )
74
+ end
75
+
76
+ public
77
+ class << self
78
+
79
+ # Specify the scsynth binary path
80
+ def sc_path=( path )
81
+ @@sc_path = path
82
+ end
83
+
84
+ # Get the scsynth path
85
+ def sc_path
86
+ @@sc_path
87
+ end
88
+
89
+ # Returns an array with all the registered servers
90
+ def all
91
+ @@servers
92
+ end
93
+
94
+ # Return a server corresponding to the specified index of the registered servers array
95
+ def [](index)
96
+ @@servers[index]
97
+ end
98
+
99
+ # Set a server to the specified index of the registered servers array
100
+ def []=(index)
101
+ @@servers[index]
102
+ @@servers.uniq!
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ class SCError < StandardError
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ module Scruby
2
+ class Synth < Node
3
+
4
+ def initialize( name, opts = {} )
5
+ super( name, opts.delete(:servers) )
6
+ @servers.each{ |s| s.send '/s_new', *([@name, self.id, 0, 1] + opts.to_a.flatten) }
7
+ end
8
+
9
+ def set( args = {} )
10
+ @servers.each{ |s| s.send '/n_set', *([self.id] + args.to_a.flatten) }
11
+ self
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,114 @@
1
+ module Scruby
2
+ module Audio
3
+ class SynthDef
4
+ attr_reader :name, :children, :constants, :control_names
5
+ # Creates a new SynthDef instance
6
+ # An "ugen graph" block should be passed:
7
+ #
8
+ # SynthDef.new('simple') do |rate|
9
+ # Out.ar( 0, SinOsc.ar(rate) )
10
+ # end
11
+ #
12
+ # Default values and rates for the block can be passed with the <tt>:values => []</tt> and <tt>:rates => []</tt> options:
13
+ # E.g.
14
+ # SynthDef.new( :am, :values => [1, 1000, 10, 1] ) do |gate, portadora, moduladora, amp|
15
+ # modulacion = SinOsc.kr( moduladora, 0, 0.5, 0.5 )
16
+ # sig = SinOsc.ar( portadora, 0, modulacion )
17
+ # env = EnvGen.kr( Env.asr(2,1,2), gate, :doneAction => 2 )
18
+ # Out.ar( 0, sig*env*amp )
19
+ # end
20
+ #
21
+ # is equivalent to the Sclang SynthDef
22
+ # SynthDef(\am, {|gate=1, portadora=1000, moduladora=10, amp=1|
23
+ # var modulacion, sig, env;
24
+ # modulacion = SinOsc.kr(moduladora, 0, 0.5, 0.5);
25
+ # sig = SinOsc.ar(portadora, 0, modulacion);
26
+ # env = EnvGen.kr(Env.asr(2,1,2), gate, doneAction:2);
27
+ # Out.ar(0, sig*env*amp);
28
+ # }).send(s)
29
+ #
30
+ def initialize( name, options = {}, &block )
31
+ @name, @children = name.to_s, []
32
+
33
+ values = options.delete( :values ) || []
34
+ rates = options.delete( :rates ) || []
35
+ block = block || Proc.new{}
36
+
37
+ @control_names = collect_control_names( block, values, rates )
38
+ build_ugen_graph( block, @control_names )
39
+ @constants = collect_constants( @children )
40
+
41
+ @variants = [] #stub!!!
42
+
43
+ warn( 'A SynthDef without a block is useless' ) unless block_given?
44
+ end
45
+
46
+ # Returns a string representing the encoded SynthDef in a way scsynth can interpret and generate.
47
+ # This method is called by a server instance when sending the synthdef via OSC.
48
+ #
49
+ # For complex synthdefs the encoded synthdef can vary a little bit from what SClang would generate
50
+ # but the results will be interpreted in the same way
51
+ def encode
52
+ controls = @control_names.reject { |cn| cn.non_control? }
53
+ encoded_controls = [controls.size].pack('n') + controls.collect{ |c| c.name.encode + [c.index].pack('n') }.to_s
54
+
55
+ init_stream + name.encode + constants.encode_floats + values.flatten.encode_floats + encoded_controls +
56
+ [children.size].pack('n') + children.collect{ |u| u.encode }.join('') +
57
+ [@variants.size].pack('n') #stub!!!
58
+ end
59
+
60
+ def init_stream(file_version = 1, number_of_synths = 1) #:nodoc:
61
+ 'SCgf' + [file_version].pack('N') + [number_of_synths].pack('n')
62
+ end
63
+
64
+ def values #:nodoc:
65
+ @control_names.collect{ |control| control.value }
66
+ end
67
+
68
+ alias :send_msg :send
69
+ # Sends itself to the given servers. One or more servers or an array of servers can be passed.
70
+ # If no arguments are given the synthdef gets sent to all instantiated servers
71
+ # E.g.
72
+ # s = Server.new('localhost', 5114)
73
+ # s.boot
74
+ # r = Server.new('127.1.1.2', 5114)
75
+ #
76
+ # SynthDef.new('sdef'){ Out.ar(0, SinOsc.ar(220)) }.send(s)
77
+ # # this synthdef is only sent to s
78
+ #
79
+ # SynthDef.new('sdef2'){ Out.ar(1, SinOsc.ar(222)) }.send
80
+ # # this synthdef is sent to both s and r
81
+ #
82
+ def send( *servers )
83
+ servers = *servers
84
+ (servers ? servers.to_array : Server.all).each{ |s| s.send_synth_def( self ) }
85
+ self
86
+ end
87
+
88
+ private
89
+ def collect_control_names( function, values, rates ) #:nodoc:
90
+ return [] if (names = function.argument_names).empty?
91
+ names.zip( values, rates ).collect_with_index{ |array, index| ControlName.new *(array << index) }
92
+ end
93
+
94
+ def build_controls( control_names ) #:nodoc:
95
+ # control_names.select{ |c| c.rate == :noncontrol }.sort_by{ |c| c.control_name.index } +
96
+ [:scalar, :trigger, :control].collect do |rate|
97
+ same_rate_array = control_names.select{ |control| control.rate == rate }
98
+ Control.and_proxies_from( same_rate_array ) unless same_rate_array.empty?
99
+ end.flatten.compact.sort_by{ |proxy| proxy.control_name.index }
100
+ end
101
+
102
+ def build_ugen_graph( function, control_names ) #:nodoc:
103
+ Ugen.synthdef = self
104
+ function.call *build_controls( control_names)
105
+ Ugen.synthdef = nil
106
+ end
107
+
108
+ def collect_constants( children ) #:nodoc:
109
+ children.send( :collect_constants ).flatten.compact.uniq
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,18 @@
1
+ module Scruby
2
+ module Audio
3
+ module Ugens
4
+ class EnvGen < Ugen
5
+ class << self
6
+ def ar( envelope, gate = 1, levelScale = 1, levelBias = 0, timeScale = 1, doneAction = 0 )
7
+ new(:audio, gate, levelScale, levelBias, timeScale, doneAction, *envelope.to_array)
8
+ end
9
+
10
+ def kr( envelope, gate = 1, levelScale = 1, levelBias = 0, timeScale = 1, doneAction = 0 )
11
+ new(:control, gate, levelScale, levelBias, timeScale, doneAction, *envelope.to_array)
12
+ end
13
+ named_args_for :ar, :kr
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ module Scruby
2
+ module Audio
3
+ module Ugens
4
+
5
+ class In < MultiOutUgen
6
+ def initialize( rate, channels, bus ) #:nodoc:
7
+ super rate, *(0...channels).map{ |i| OutputProxy.new rate, self, i }
8
+ @inputs = [bus]
9
+ end
10
+
11
+ def self.ar( bus, channels = 1 )
12
+ new :audio, channels, bus
13
+ end
14
+
15
+ def self.kr( bus, num_channels = 1 )
16
+ new :control, channels, bus
17
+ end
18
+ end
19
+
20
+ class Out < Ugen
21
+ def initialize(*args) #:nodoc:
22
+ super
23
+ @channels = []
24
+ end
25
+
26
+ def self.ar ( bus, *inputs )
27
+ inputs = *inputs
28
+ new :audio, bus, *inputs; 0.0 #Out has no output
29
+ end
30
+
31
+ def self.kr ( bus, *inputs )
32
+ inputs = *inputs
33
+ new :control, bus, *inputs; 0.0 #Out has no output
34
+ end
35
+
36
+ def output_specs
37
+ []
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,48 @@
1
+ module Scruby
2
+ module Audio
3
+ module Ugens
4
+
5
+ class OutputProxy < Ugen
6
+ attr_reader :source, :control_name, :output_index
7
+
8
+ def initialize( rate, source, output_index, name = nil )
9
+ super rate
10
+ @source, @control_name, @output_index = source, name, output_index
11
+ end
12
+
13
+ def index
14
+ @source.index
15
+ end
16
+
17
+ def add_to_synthdef; end
18
+ end
19
+
20
+ class MultiOutUgen < Ugen
21
+ def initialize( rate, *channels )
22
+ super rate
23
+ @channels = channels
24
+ end
25
+
26
+ def self.new( rate, *args )
27
+ super( rate, *args ).channels #returns the channels but gets instantiated
28
+ end
29
+
30
+ private
31
+ def output_specs
32
+ channels.collect{ |output| output.send :output_specs }.flatten
33
+ end
34
+ end
35
+
36
+ class Control < MultiOutUgen #:nodoc:
37
+ def initialize( rate, *names )
38
+ super rate, *names.collect_with_index{|n, i| OutputProxy.new rate, self, i, n }
39
+ end
40
+
41
+ def self.and_proxies_from( names )
42
+ Control.new( names.first.rate, *names )
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,92 @@
1
+
2
+ binary:
3
+ !ruby/symbol +: 0
4
+ !ruby/symbol -: 1
5
+ !ruby/symbol *: 2
6
+ !ruby/symbol div: 3
7
+ !ruby/symbol /: 4
8
+ !ruby/symbol mod: 5
9
+ !ruby/symbol <=: 10
10
+ !ruby/symbol >=: 11
11
+ !ruby/symbol minimum: 12
12
+ !ruby/symbol maximum: 13 #can't be called max because there is an array method calle maximum
13
+ !ruby/symbol lcm: 17
14
+ !ruby/symbol gcd: 18
15
+ !ruby/symbol round: 19
16
+ !ruby/symbol roundUp: 20
17
+ !ruby/symbol trunc: 21
18
+ !ruby/symbol atan2: 22
19
+ !ruby/symbol hypot: 23
20
+ !ruby/symbol hypotApx: 24
21
+ !ruby/symbol pow: 25
22
+ !ruby/symbol leftShift: 26
23
+ !ruby/symbol rightShift: 27
24
+ !ruby/symbol unsignedRightShift: 28
25
+ !ruby/symbol ring1: 30
26
+ !ruby/symbol ring2: 31
27
+ !ruby/symbol ring3: 32
28
+ !ruby/symbol ring4: 33
29
+ !ruby/symbol difsqr: 34
30
+ !ruby/symbol sumsqr: 35
31
+ !ruby/symbol sqrsum: 36
32
+ !ruby/symbol sqrdif: 37
33
+ !ruby/symbol absdif: 38
34
+ !ruby/symbol thresh: 39
35
+ !ruby/symbol amclip: 40
36
+ !ruby/symbol scaleneg: 41
37
+ !ruby/symbol clip2: 42
38
+ !ruby/symbol excess: 43
39
+ !ruby/symbol fold2: 44
40
+ !ruby/symbol wrap2: 45
41
+ !ruby/symbol rrand: 47
42
+ !ruby/symbol exprand: 48
43
+
44
+ unary:
45
+ !ruby/symbol neg: 0
46
+ !ruby/symbol bitNot: 4
47
+ !ruby/symbol abs: 5
48
+ !ruby/symbol asFloat: 6
49
+ !ruby/symbol ceil: 8
50
+ !ruby/symbol floor: 9
51
+ !ruby/symbol frac: 10
52
+ !ruby/symbol sign: 11
53
+ !ruby/symbol squared: 12
54
+ !ruby/symbol cubed: 13
55
+ !ruby/symbol sqrt: 14
56
+ !ruby/symbol exp: 15
57
+ !ruby/symbol reciprocal: 16
58
+ !ruby/symbol midicps: 17
59
+ !ruby/symbol cpsmidi: 18
60
+ !ruby/symbol midiratio: 19
61
+ !ruby/symbol ratiomidi: 20
62
+ !ruby/symbol dbamp: 21
63
+ !ruby/symbol ampdb: 22
64
+ !ruby/symbol octcps: 23
65
+ !ruby/symbol cpsoct: 24
66
+ !ruby/symbol log: 25
67
+ !ruby/symbol log2: 26
68
+ !ruby/symbol log10: 27
69
+ !ruby/symbol sin: 28
70
+ !ruby/symbol cos: 29
71
+ !ruby/symbol tam: 30
72
+ !ruby/symbol asin: 31
73
+ !ruby/symbol acos: 32
74
+ !ruby/symbol atan: 33
75
+ !ruby/symbol sinh: 34
76
+ !ruby/symbol cosh: 35
77
+ !ruby/symbol tanh: 36
78
+ !ruby/symbol rand: 37
79
+ !ruby/symbol rand2: 38
80
+ !ruby/symbol linrand: 39
81
+ !ruby/symbol bilinrand: 40
82
+ !ruby/symbol sum3rand: 41
83
+ !ruby/symbol distort: 42
84
+ !ruby/symbol softclip: 43
85
+ !ruby/symbol coin: 44
86
+ !ruby/symbol rectWindow: 48
87
+ !ruby/symbol hanWindow: 49
88
+ !ruby/symbol welWindow: 50
89
+ !ruby/symbol triWindow: 51
90
+ !ruby/symbol ramp: 52
91
+ !ruby/symbol scurve: 53
92
+