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
data/README.rdoc ADDED
@@ -0,0 +1,26 @@
1
+
2
+
3
+
4
+ == LICENSE:
5
+
6
+ Copyright (c) 2008 Macario Ortega
7
+
8
+ This program is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation, either version 3 of the License, or
11
+ (at your option) any later version.
12
+
13
+ This program is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ == NOTES
22
+
23
+ Ruby has operation precendence
24
+
25
+
26
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('Scruby', '0.0.8') do |p|
6
+ p.description = "Small client for doing sound synthesis from ruby using the SuperCollider synth, usage is quite similar from SuperCollider."
7
+ p.url = 'http://github.com/maca/scruby'
8
+ p.author = "Macario Ortega"
9
+ p.email = "macarui@gmail.com"
10
+ end
data/Scruby.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{Scruby}
5
+ s.version = "0.0.8"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Macario Ortega"]
9
+ s.date = %q{2009-02-05}
10
+ s.default_executable = %q{live_session.rb}
11
+ s.description = %q{Small client for doing sound synthesis from ruby using the SuperCollider synth, usage is quite similar from SuperCollider.}
12
+ s.email = %q{macarui@gmail.com}
13
+ s.executables = ["live_session.rb"]
14
+ s.extra_rdoc_files = ["bin/live_session.rb", "lib/live/session.rb", "lib/scruby/audio/control_name.rb", "lib/scruby/audio/env.rb", "lib/scruby/audio/node.rb", "lib/scruby/audio/server.rb", "lib/scruby/audio/synth.rb", "lib/scruby/audio/synthdef.rb", "lib/scruby/audio/ugens/env_gen.rb", "lib/scruby/audio/ugens/in_out.rb", "lib/scruby/audio/ugens/multi_out_ugens.rb", "lib/scruby/audio/ugens/operation_indices.yaml", "lib/scruby/audio/ugens/operation_ugens.rb", "lib/scruby/audio/ugens/ugen.rb", "lib/scruby/audio/ugens/ugen_defs.yaml", "lib/scruby/audio/ugens/ugen_operations.rb", "lib/scruby/audio/ugens/ugens.rb", "lib/scruby/control/metro.rb", "lib/scruby/extensions.rb", "lib/scruby/typed_array.rb", "lib/scruby.rb", "README.rdoc"]
15
+ s.files = ["bin/live_session.rb", "changes", "lib/live/session.rb", "lib/scruby/audio/control_name.rb", "lib/scruby/audio/env.rb", "lib/scruby/audio/node.rb", "lib/scruby/audio/server.rb", "lib/scruby/audio/synth.rb", "lib/scruby/audio/synthdef.rb", "lib/scruby/audio/ugens/env_gen.rb", "lib/scruby/audio/ugens/in_out.rb", "lib/scruby/audio/ugens/multi_out_ugens.rb", "lib/scruby/audio/ugens/operation_indices.yaml", "lib/scruby/audio/ugens/operation_ugens.rb", "lib/scruby/audio/ugens/ugen.rb", "lib/scruby/audio/ugens/ugen_defs.yaml", "lib/scruby/audio/ugens/ugen_operations.rb", "lib/scruby/audio/ugens/ugens.rb", "lib/scruby/control/metro.rb", "lib/scruby/extensions.rb", "lib/scruby/typed_array.rb", "lib/scruby.rb", "Rakefile", "README.rdoc", "spec/audio/env_gen_specs.rb", "spec/audio/in_out_spec.rb", "spec/audio/integration_spec.rb", "spec/audio/lib_spec.rb", "spec/audio/multiout_ugen_spec.rb", "spec/audio/node_spec.rb", "spec/audio/operation_ugens_spec.rb", "spec/audio/server_spec.rb", "spec/audio/synth_spec.rb", "spec/audio/synthdef_spec.rb", "spec/audio/ugen_operations_spec.rb", "spec/audio/ugen_spec.rb", "spec/audio/ugens_spec.rb", "spec/env_spec.rb", "spec/extensions_spec.rb", "spec/helper.rb", "spec/typed_array_spec.rb", "Manifest", "Scruby.gemspec"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/maca/scruby}
18
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Scruby", "--main", "README.rdoc"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{scruby}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{Small client for doing sound synthesis from ruby using the SuperCollider synth, usage is quite similar from SuperCollider.}
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_development_dependency(%q<echoe>, [">= 0"])
30
+ else
31
+ s.add_dependency(%q<echoe>, [">= 0"])
32
+ end
33
+ else
34
+ s.add_dependency(%q<echoe>, [">= 0"])
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ live_session_dir = File.join( File.expand_path(File.dirname(__FILE__) ), '..', 'lib', 'live' )
3
+
4
+ require 'tempfile'
5
+ require "#{live_session_dir}/session"
6
+
7
+
8
+ Live::Session.new
9
+
10
+
11
+
12
+
data/changes ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,144 @@
1
+ require 'tempfile'
2
+ require 'rubygems'
3
+ require 'highline'
4
+ require 'parse_tree'
5
+ require 'ruby2ruby'
6
+
7
+ module Kernel
8
+ alias :l :lambda
9
+
10
+ # Calls Kernel#eval with the given args but catches posible errors
11
+ def resilient_eval( *args )
12
+ begin
13
+ begin
14
+ eval( *args )
15
+ rescue SyntaxError => e
16
+ e
17
+ end
18
+ rescue => e
19
+ e
20
+ end
21
+ end
22
+
23
+ def p( obj ) #:nodoc:
24
+ puts obj.to_live_output
25
+ end
26
+
27
+ end
28
+
29
+ class Object
30
+
31
+ # Outputs an ANSI colored string with the object representation
32
+ def to_live_output
33
+ case self
34
+ when Exception
35
+ "\e[41m\e[33m#{self.inspect}\e[0m"
36
+ when Numeric, Symbol, TrueClass, FalseClass, NilClass
37
+ "\e[35m#{self.inspect}\e[0m"
38
+ when Notice
39
+ "\e[42m\e[30m#{self}\e[0m"
40
+ when Warning
41
+ "\e[43m\e[30m#{self}\e[0m"
42
+ when Special
43
+ "\e[44m\e[37m#{self}\e[0m"
44
+ when String
45
+ "\e[32m#{self.inspect}\e[0m"
46
+ when Array
47
+ "[#{ self.collect{ |i| i.to_live_output}.join(', ') }]"
48
+ when Hash
49
+ "{#{ self.collect{ |i| i.collect{|e| e.to_live_output}.join(' => ') } }}"
50
+ else
51
+ "\e[36m#{self}\e[0m"
52
+ end
53
+ end
54
+ end
55
+
56
+ class Notice < String; end
57
+ class Warning < String; end
58
+ class Special < String; end
59
+
60
+ module Live
61
+ class Pipe < Tempfile
62
+ def make_tmpname( *args )
63
+ "ruby_live.pipe"
64
+ end
65
+ end
66
+
67
+ class Session
68
+ include HighLine::SystemExtensions
69
+
70
+ # Starts a live session using a named pipe to receive code from a remote source and evaluates it within a context, a bit like an IRB session but evaluates code sent from a text editor
71
+ def initialize
72
+ return p( Exception.new("Another session sems to be running") ) if File.exist?( "#{Dir.tmpdir}/ruby_live.pipe" )
73
+ p( Notice.new("Live Session") )
74
+ get_binding
75
+ init_pipe
76
+ expect_input
77
+ serve
78
+ end
79
+
80
+ def init_pipe
81
+ @pipe_path = Pipe.new('').path
82
+ `rm #{@pipe_path}; mkfifo #{@pipe_path}`
83
+ end
84
+
85
+ # Starts a loop that checks the named pipe and evaluate its contents, will be called on initialize
86
+ def serve
87
+ File.open( @pipe_path, File::RDONLY | File::NONBLOCK) do |f|
88
+ loop { p evaluate( f.gets.to_s.gsub("∂", "\n") ) }
89
+ end
90
+ end
91
+
92
+ # Starts a new Thread that will loop capturing keystrokes and evaluating the bound block within the @context Binding, will be called on initialize
93
+ def expect_input
94
+ Thread.new do
95
+ loop do
96
+ char = get_character
97
+ @bindings ||= []
98
+ bind = @bindings[ char ]
99
+ p evaluate( bind ) if bind
100
+ end
101
+ end
102
+ end
103
+
104
+ # Expects a one char Symbol or String which will bind to a passed block so it can be called later with a keystroke
105
+ def bind( key, &block )
106
+ @bindings = [] unless @bindings.instance_of?(Array)
107
+ block ||= Proc.new{}
108
+ @bindings[ key.to_s[0] ] = Ruby2Ruby.new.process( [:block, block.to_sexp.last] )
109
+ Notice.new( "Key '#{key}' is bound to an action")
110
+ end
111
+
112
+ # Evaluates a ruby expression within the @context Binding
113
+ def evaluate( string = nil )
114
+ return resilient_eval( string, @context ) if string
115
+ end
116
+
117
+ def clear
118
+ print "\e[2J\e[f"
119
+ end
120
+
121
+ def get_binding #:nodoc:
122
+ @context = binding
123
+ end
124
+
125
+ def run_updates( code )
126
+ source = ParseTree.new.parse_tree_for_string( code ).first
127
+ final = []
128
+ while iter = source.assoc(:iter)
129
+ source -= [iter]
130
+ final << [:block, iter.last] if iter[1].include?(:update)
131
+ end
132
+ evaluate( final.collect{ |exp| Ruby2Ruby.new.process(exp) }.join("\n") )
133
+ Notice.new('Update blocks evaluated')
134
+ end
135
+
136
+ def update # Allmost a stub
137
+ yield
138
+ end
139
+
140
+ alias :reaload! :get_binding
141
+ end
142
+ end
143
+
144
+
data/lib/scruby.rb ADDED
@@ -0,0 +1,60 @@
1
+ #--
2
+ # Copyright (c) 2008 Macario Ortega
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ #++
17
+ require 'rubygems'
18
+ require 'ruby2ruby'
19
+ require "named_arguments"
20
+ require 'osc'
21
+ require 'yaml'
22
+
23
+ SCRUBY_DIR = File.join( File.expand_path(File.dirname(__FILE__) ), 'scruby' )
24
+
25
+
26
+ require "#{SCRUBY_DIR}/typed_array"
27
+
28
+ require "#{SCRUBY_DIR}/audio/ugens/ugen_operations"
29
+ require "#{SCRUBY_DIR}/audio/ugens/ugen"
30
+ require "#{SCRUBY_DIR}/audio/ugens/multi_out_ugens"
31
+ require "#{SCRUBY_DIR}/audio/ugens/in_out"
32
+
33
+ require "#{SCRUBY_DIR}/audio/ugens/operation_ugens"
34
+ require "#{SCRUBY_DIR}/audio/ugens/ugen"
35
+
36
+ require "#{SCRUBY_DIR}/audio/ugens/ugens"
37
+ require "#{SCRUBY_DIR}/audio/control_name"
38
+ require "#{SCRUBY_DIR}/audio/synthdef"
39
+ require "#{SCRUBY_DIR}/extensions"
40
+
41
+ require "#{SCRUBY_DIR}/audio/server"
42
+
43
+ require "#{SCRUBY_DIR}/audio/env"
44
+ require "#{SCRUBY_DIR}/audio/ugens/env_gen"
45
+
46
+ require "#{SCRUBY_DIR}/audio/node"
47
+ require "#{SCRUBY_DIR}/audio/synth"
48
+
49
+
50
+ include Scruby
51
+ include Audio
52
+ include Ugens
53
+ include OperationUgens
54
+
55
+
56
+ class Notice < String; end
57
+ class Warning < String; end
58
+ class Special < String; end
59
+
60
+
@@ -0,0 +1,29 @@
1
+ module Scruby
2
+ module Audio
3
+ class ControlName
4
+ attr_accessor :name, :value, :rate, :index
5
+ RATES = { 'n_' => :noncontrol, 'i_' => :scalar, 'k_' => :control, 't_' => :trigger }
6
+
7
+ def initialize( name, value, rate, index )
8
+ @name, @value, @rate, @index = name.to_s, value.to_f, set_rate( name, rate ), index
9
+ end
10
+
11
+ def set_rate( name, rate )
12
+ RATES.has_value?( rate ) ? rate : rate_from_name( name )
13
+ end
14
+
15
+ def rate_from_name( name )
16
+ RATES[ name.to_s[0..1] ] || :control
17
+ end
18
+
19
+ def non_control?
20
+ @rate == :noncontrol
21
+ end
22
+
23
+ def valid_ugen_input?
24
+ true
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,97 @@
1
+ module Scruby
2
+ class Env
3
+ attr_accessor :levels, :times, :curves, :release_node, :array
4
+ SHAPE_NAMES = {
5
+ :step => 0,
6
+ :lin => 1,
7
+ :linear => 1,
8
+ :exp => 2,
9
+ :exponential => 2,
10
+ :sin => 3,
11
+ :sine => 3,
12
+ :wel => 4,
13
+ :welch => 4,
14
+ :sqr => 6,
15
+ :squared => 6,
16
+ :cub => 7,
17
+ :cubed => 7
18
+ }
19
+
20
+ def initialize( levels, times, curves = :lin, release_node = nil, loop_node = nil )
21
+ @levels, @times, @curves, @release_node, @loop_node = levels, times, curves.to_array, release_node, loop_node
22
+ raise ArgumentError, "levels and times should be array" unless levels.instance_of?(Array) and times.instance_of?(Array)
23
+ end
24
+
25
+ class << self
26
+ # fixed duration envelopes
27
+ def triangle(dur = 1.0, level = 1.0)
28
+ dur = (dur * 0.5)
29
+ new( [0, level, 0], [dur, dur] )
30
+ end
31
+
32
+ def sine(dur = 1.0, level = 1.0)
33
+ dur = (dur * 0.5)
34
+ new( [0, level, 0], [dur, dur], :sine)
35
+ end
36
+
37
+ def perc( attackTime=0.01, releaseTime=1.0, level=1.0, curve = -4.0 )
38
+ new( [0, level, 0], [attackTime, releaseTime], curve )
39
+ end
40
+
41
+ def linen( attackTime=0.01, sustainTime=1.0, releaseTime=1.0, level=1.0, curve = :lin )
42
+ new [0, level, level, 0], [attackTime, sustainTime, releaseTime], curve
43
+ end
44
+
45
+ def cutoff( releaseTime = 0.1, level = 1.0, curve = :lin )
46
+ new( [level, 0], [releaseTime], curve, 0 )
47
+ end
48
+
49
+ 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 )
50
+ new( [0, 0, peakLevel, peakLevel * sustainLevel, 0].collect{ |e| e + bias }, [delayTime, attackTime, decayTime, releaseTime], curve, 3 )
51
+ end
52
+
53
+ def adsr( attackTime=0.01, decayTime=0.3, sustainLevel=0.5, releaseTime=1.0, peakLevel=1.0, curve = -4.0, bias = 0.0 )
54
+ new( [0, peakLevel, peakLevel * sustainLevel, 0].collect{ |e| e + bias }, [attackTime, decayTime, releaseTime], curve, 2 )
55
+ end
56
+
57
+ def asr( attackTime=0.01, sustainLevel=1.0, releaseTime=1.0, curve = -4.0 )
58
+ new( [0, sustainLevel, 0], [attackTime, releaseTime], curve, 1 )
59
+ end
60
+
61
+ named_args_for :triangle, :sine, :perc, :linen, :cutoff, :dadsr, :adsr, :asr
62
+ end
63
+
64
+ def to_array
65
+ *contents = levels[0], times.size, release_node, loop_node
66
+ contents + levels[1..-1].wrap_and_zip( times, shape_numbers, curve_values ).flatten
67
+ end
68
+
69
+ def shape_numbers
70
+ curves.collect do |curve|
71
+ curve.valid_ugen_input? ? 5 : SHAPE_NAMES[curve]
72
+ end
73
+ end
74
+
75
+ def curve_values
76
+ curves.collect do |curve|
77
+ curve.valid_ugen_input? ? curve : 0
78
+ end
79
+ end
80
+
81
+ def release_node
82
+ @release_node ||= -99
83
+ end
84
+
85
+ def loop_node
86
+ @loop_node ||= -99
87
+ end
88
+
89
+ def valid_ugen_input? #returns true
90
+ true
91
+ end
92
+
93
+ def collect_constants #:nodoc:
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,20 @@
1
+ module Scruby
2
+ class Node
3
+ @@base_id = 2000
4
+ attr_reader :name, :servers
5
+
6
+ def initialize( name, *servers )
7
+ @name = name.to_s
8
+ servers = *servers
9
+ @servers = servers ? TypedArray.new( Server, [*servers] ) : Server.all
10
+ end
11
+
12
+ def id
13
+ @id ||= ( @@base_id = @@base_id + 1 )
14
+ end
15
+
16
+ def self.reset!
17
+ @@base_id = 2000
18
+ end
19
+ end
20
+ end