Skip to content
Browse files

enhancements to Speaker; param_accessor; first release of gem

  • Loading branch information...
1 parent 99ebfc8 commit 10faa75edffc7040da19630dd26b8e08023a1138 @dfl committed May 16, 2012
View
1 .gitignore
@@ -0,0 +1 @@
+pkg/
View
13 Manifest.txt
@@ -1,8 +1,17 @@
-.autotest
History.txt
Manifest.txt
README.txt
Rakefile
bin/radspberry
-lib/radspberry.rb
test/test_radspberry.rb
+lib/radspberry.rb
+lib/radspberry/
+lib/radspberry/RAFL_wav.rb
+lib/radspberry/dsp.rb
+lib/radspberry/dsp_math.rb
+lib/radspberry/filter.rb
+lib/radspberry/midi.rb
+lib/radspberry/oscillator.rb
+lib/radspberry/ruby_extensions.rb
+lib/radspberry/speaker.rb
+lib/radspberry/super_saw.rb
View
26 README.txt
@@ -9,36 +9,28 @@ A real-time audio dsp library for ruby based on ffi-portaudio
== FEATURES/PROBLEMS:
* realtime output with ffi-portaudio
-* output to wave files
+* output to speaker and wave files
* basic oscillator and filter classes
+* MIDI still needs some work
-== SYNOPSIS:
+== EXAMPLE USE:
require 'radspberry'
-osc = SuperSaw.new
-Speaker[ osc ]
-osc.spread = 0.5
-osc.freq = 120
-Speaker.volume = 0.5
+
+Speaker[ SuperSaw.new ]
+Speaker.synth[ :spread => 0.5, :freq => 120 ]
+Speaker[:volume => 0.5, :synth => {:spread => 0.9, :freq => 200 }]
Speaker.mute
== REQUIREMENTS:
-* depends on ffi-portaudio (which depends on portaudio)
+* depends on ffi-portaudio (which depends on portaudio libs)
== INSTALL:
* brew install portaudio
* gem install ffi-portaudio
-
-== DEVELOPERS:
-
-# After checking out the source, run:
-#
-# $ rake newb
-#
-# This task will install any missing dependencies, run the tests/specs,
-# and generate the RDoc.
+* gem install radspberry
== LICENSE:
View
11 Rakefile
@@ -9,15 +9,16 @@ require 'hoe'
# Hoe.plugin :racc
# Hoe.plugin :rcov
Hoe.plugin :rubyforge
+Hoe.plugin :git
+Hoe.plugin :bundler
Hoe.spec 'radspberry' do
- # HEY! If you fill these out in ~/.hoe_template/Rakefile.erb then
- # you'll never have to touch them again!
- # (delete this comment too, of course)
-
developer('David Lowenfels', 'david@internautdesign.com')
+ extra_deps << ['ffi-portaudio', '~> 0.1.2']
+ # extra_deps << ['unimidi', '~> 0.3.3']
+ extra_deps << ['portmidi', '~> 0.0.6']
+ extra_deps << ['active_support', '~> 3.0']
- # self.rubyforge_name = 'radspberryx' # if different than 'radspberry'
end
# vim: syntax=ruby
View
29 example.rb
@@ -0,0 +1,29 @@
+require 'radspberry'
+
+puts "starting simple oscillator"
+Speaker[ Phasor.new ]
+sleep 1
+
+puts "changing frequency"
+Speaker.synth.freq /= 2
+sleep 1
+
+puts "changing frequency"
+Speaker.synth.freq /= 2
+sleep 1
+
+
+puts "starting crossfader (supersaw with rpmnoise)"
+chain = XFader[ o1=SuperSaw.new, o2=RpmNoise.new ]
+Speaker[ chain ]
+o1.spread = 0.8
+chain.fade = 0
+sleep 5
+10.times do
+ chain.fade += 0.1
+ sleep 0.5
+end
+
+puts "muting"
+Speaker.mute
+sleep 1
View
15 lib/radspberry.rb
@@ -2,7 +2,22 @@ class Radspberry
VERSION = '0.1.0'
end
+require 'matrix'
+# require 'active_support'
+require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/array/grouping'
+require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/hash/reverse_merge'
+
+require 'portmidi'
+require 'radspberry/RAFL_wav'
+
+require 'radspberry/ruby_extensions'
+require 'radspberry/midi'
+require 'radspberry/dsp_math'
require 'radspberry/dsp'
require 'radspberry/speaker'
+require 'radspberry/oscillator'
+require 'radspberry/filter'
require 'radspberry/super_saw'
View
31 lib/radspberry/dsp.rb
@@ -1,11 +1,3 @@
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/array/grouping'
-
-require 'matrix'
-require './RAFL_wav'
-require './midi'
-require './dsp_math'
-
class AudioDSP
include DSP::Constants
@@ -34,9 +26,7 @@ def clear
# allows for setting multiple values at once
def [] args={}
- args.each_pair do |k,v|
- send "#{k}=", v
- end
+ args.each_pair{ |k,v| send "#{k}=", v }
end
end
@@ -104,19 +94,6 @@ def self.[] *chain
end
end
-
-module ArrayExtensions
- def full_of(val,num)
- [].fill(val,0...num)
- end
-
- def zeros num
- full_of(0,num)
- end
-end
-Array.send :extend, ArrayExtensions
-
-
class ProcessorChain < TickerChain
def tick input
@gain * @chain.inject( input ){|x,o| o.tick(x) }
@@ -148,16 +125,12 @@ def ticks samples
end
class XFader < Generator
- attr_accessor :fade
+ param_accessor :fade
def self.[] *mix
new mix[0], mix[1], mix[2]
end
- def fade= fade
- @fade = DSP.clamp( fade, 0, 1.0)
- end
-
def initialize a, b,fade=nil
raise ArgumentError, "inputs cannot be nil!" unless a && b
@fade = fade || 0.5
View
2 lib/radspberry/filter.rb
@@ -1,5 +1,3 @@
-require './dsp'
-
class Biquad < Processor # interpolating biquad, Direct-form 1
include DSP::Math
View
3 lib/radspberry/midi.rb
@@ -1,5 +1,4 @@
require 'portmidi'
-require 'active_support/core_ext/object/try'
module MIDI
extend self
@@ -78,8 +77,6 @@ def krystal_freq( note )
end
module Player
- require './speaker'
-
extend self
def [] gen
View
44 lib/radspberry/oscillator.rb
@@ -1,6 +1,7 @@
-require './dsp'
-
class PhasorOscillator < Oscillator
+ param_accessor :phase, :delegate => :phasor
+ param_accessor :freq, :delegate => :phasor, :range => false # no range check for faster modulation
+
def initialize( freq = DEFAULT_FREQ, phase=0 )
@phasor = Phasor.new( freq, phase )
clear
@@ -10,16 +11,6 @@ def initialize( freq = DEFAULT_FREQ, phase=0 )
def clear
end
- def freq= arg
- @phasor.freq = arg
- end
- def phase= arg
- @phasor.phase = DSP.clamp(arg, 0.0, 1.0)
- end
- def phase
- @phasor.phase
- end
-
def tock
@phasor.phase.tap{ @phasor.tick }
end
@@ -35,16 +26,9 @@ def tick
end
class Pulse < PhasorOscillator
- FACTOR = { true => 1.0, false => -1.0 }
-
- def initialize( freq=DEFAULT_FREQ, phase=0 )
- @duty = 0.5
- super
- end
+ param_accessor :duty, :default => 0.5
- def duty= arg
- @duty = DSP.clamp(arg, 0.0, 1.0)
- end
+ FACTOR = { true => 1.0, false => -1.0 }
def tick
FACTOR[ tock <= @duty ]
@@ -53,19 +37,11 @@ def tick
class RpmSaw < PhasorOscillator
include DSP::Math
-
- def initialize( freq=MIDI::A, phase = DSP.random )
- @beta = 1.5 # TODO: base on frequency?
- super
- end
+ param_accessor :beta, :range => (0..2), :default => 1.5
def clear
@state = @last_out = 0
end
-
- def beta= arg, clamp=true
- @beta = DSP.clamp(arg, 0.0, 2.0)
- end
def tick
@state = 0.5*(@state + @last_out) # one-pole averager
@@ -81,10 +57,10 @@ def tick
end
class RpmNoise < RpmSaw
- attr_accessor :beta
- def initialize( seed = 1234 )
- super()
- @beta = seed
+ param_accessor :beta, :default => 1234 # no range clamping
+
+ def initialize( seed = nil )
+ @beta = seed if seed
end
end
View
42 lib/radspberry/ruby_extensions.rb
@@ -0,0 +1,42 @@
+module ArrayExtensions
+ def full_of(val,num)
+ [].fill(val,0...num)
+ end
+
+ def zeros num
+ full_of(0,num)
+ end
+end
+Array.send :extend, ArrayExtensions
+
+module ModuleExtensions
+ def param_accessor symbol, opts={}
+ opts = {:range => opts } if opts.is_a?(Range)
+ opts.reverse_merge! :range => (0..1)
+ var = nil
+ if d = opts[:delegate]
+ d = "@#{d}" if d.is_a?(Symbol)
+ d = "#{d}.#{symbol}" unless d =~ /\./
+ var = d
+ else
+ var = symbol
+ var = "@#{var}" if var.is_a?(Symbol)
+ end
+
+ ## define getter
+ if opts[:default]
+ module_eval "def #{symbol}() #{var} || #{opts[:default]}; end" # TODO allow Proc.call(self) ?
+ else
+ module_eval "def #{symbol}() #{var}; end"
+ end
+
+ ## define setter
+ if opts[:range]
+ min,max = opts[:range].first.to_f, opts[:range].last.to_f
+ module_eval "def #{symbol}=(val) #{var} = DSP.clamp(val,#{min},#{max}); end"
+ else
+ module_eval "def #{symbol}=(val) #{var} = val; end"
+ end
+ end
+end
+Module.send :include, ModuleExtensions
View
35 lib/radspberry/speaker.rb
@@ -1,37 +1,52 @@
+require 'ffi-portaudio'
+
+# these are equivalent:
+# Speaker[ SuperSaw.new ]
+# Speaker.new( SuperSaw )
+# example use:
+# Speaker.new( SuperSaw, :frameSize => 2**12)[ :volume => 0.5, :synth => {:spread => 0.9, :freq => 200 }]
+# Speaker[:volume => 0.5, :synth => {:spread => 0.9, :freq => 200 }]
module Speaker
extend self
@@stream = nil
- def [] *args
- @@stream.close if @@stream
- @@stream = AudioStream.new( *args )
+ def new synth, opts={}
+ @@stream.try(:close)
+ synth = synth.new if synth.is_a?(Class) # instantiate
+ @@stream = AudioStream.new( synth, opts[:frameSize] )
self
end
- def volume= gain
- @@stream.gain = gain
- end
-
- def volume
- @@stream.gain
+ def [] opts={}
+ return new(opts) if [ Class, AudioDSP ].include?( opts.class )
+ raise ArgumentError, "no stream initialized yet!" unless @@stream
+ synth[ opts.delete(:synth) || {} ]
+ opts.each_pair{ |k,v| send "#{k}=", v }
+ self
end
+ param_accessor :volume, :delegate => "@@stream.gain", :default => 1.0
+
def mute
@@stream.muted = true
end
def unmute
@@stream.muted = false
end
+
+ def muted?
+ @@stream.muted
+ end
def toggleMute
@@stream.muted = !@@stream.muted
end
def synth
- @@stream.synth
+ @@stream.try(:synth)
end
end
View
2 lib/radspberry/super_saw.rb
@@ -1,5 +1,3 @@
-require './filter'
-
# based on Adam Szabo's thesis from csc.kth.se
class SuperSaw < Oscillator
attr_accessor :spread
View
11 lib/radspberry/test.rb
@@ -1,11 +0,0 @@
-require './dsp'
-require './speaker'
-require './super_saw'
-
-chain = XFader[ o1=SuperSaw.new, o2=RpmNoise.new ]
-Speaker[ chain ]
-o1.spread = 0.8
-chain.fade = 0
-
-# Speaker.mute
-

0 comments on commit 10faa75

Please sign in to comment.
Something went wrong with that request. Please try again.