lib/patchmaster/dsl.rb in patchmaster-0.0.0 vs lib/patchmaster/dsl.rb in patchmaster-0.0.1
- old
+ new
@@ -12,31 +12,43 @@
@pm = PatchMaster.instance
end
def load(file)
contents = IO.read(file)
+ @triggers = []
@filters = []
+ @songs = {} # key = name, value = song
instance_eval(contents)
+ read_triggers(contents)
read_filters(contents)
end
def input(port_num, sym, name=nil)
- @pm.inputs[sym] = InputDevice.new(name || sym.to_s, port_num, @no_midi)
+ @pm.inputs[sym] = InputInstrument.new(name || sym.to_s, port_num, @no_midi)
rescue => ex
- raise "error creating input device \"#{name}\" on input port #{port_num}: #{ex}"
+ raise "input: error creating input instrument \"#{name}\" on input port #{port_num}: #{ex}"
end
alias_method :in, :input
def output(port_num, sym, name=nil)
- @pm.outputs[sym] = OutputDevice.new(name || sym.to_s, port_num, @no_midi)
+ @pm.outputs[sym] = OutputInstrument.new(name || sym.to_s, port_num, @no_midi)
rescue => ex
- raise "error creating output device \"#{name}\" on output port #{port_num}: #{ex}"
+ raise "output: error creating output instrument \"#{name}\" on output port #{port_num}: #{ex}"
end
alias_method :out, :output
+ def trigger(instrument_sym, bytes, &block)
+ instrument = @pm.inputs[instrument_sym]
+ raise "trigger: error finding instrument #{instrument_sym}" unless instrument
+ t = Trigger.new(bytes, block)
+ instrument.triggers << t
+ @triggers << t
+ end
+
def song(name)
@song = Song.new(name) # ctor saves into @pm.all_songs
+ @songs[name] = @song
yield @song if block_given?
end
def patch(name)
@patch = Patch.new(name)
@@ -48,10 +60,11 @@
@patch.start_bytes = bytes
end
def connection(in_sym, in_chan, out_sym, out_chan)
input = @pm.inputs[in_sym]
+ in_chan = nil if in_chan == :all || in_chan == :any
raise "can't find input instrument #{in_sym}" unless input
output = @pm.outputs[out_sym]
raise "can't find outputput instrument #{out_sym}" unless output
@conn = Connection.new(input, in_chan, output, out_chan)
@@ -93,36 +106,47 @@
def song_list(name, song_names)
sl = SongList.new(name)
@pm.song_lists << sl
song_names.each do |sn|
- song = @pm.all_songs.find(sn)
+ song = @songs[sn]
raise "song \"#{sn}\" not found (song list \"#{name}\")" unless song
sl << song
end
end
# ****************************************************************
def save(file)
File.open(file, 'w') { |f|
save_instruments(f)
+ save_triggers(f)
save_songs(f)
save_song_lists(f)
}
end
def save_instruments(f)
- @pm.inputs.each { |sym, instr|
+ @pm.inputs.each do |sym, instr|
f.puts "input #{instr.port_num}, :#{sym}, #{quoted(instr.name)}"
- }
- @pm.outputs.each { |sym, instr|
+ end
+ @pm.outputs.each do |sym, instr|
f.puts "output #{instr.port_num}, :#{sym}, #{quoted(instr.name)}"
- }
+ end
f.puts
end
+ def save_triggers(f)
+ @pm.inputs.each do |sym, instrument|
+ instrument.triggers.each do |trigger|
+ str = "trigger :#{sym}, #{trigger.bytes.inspect} #{trigger.text}"
+ f.puts str
+ end
+ end
+ f.puts
+ end
+
def save_songs(f)
@pm.all_songs.songs.each do |song|
f.puts "song #{quoted(song.name)} do"
song.patches.each { |patch| save_patch(f, patch) }
f.puts "end"
@@ -136,15 +160,15 @@
patch.connections.each { |conn| save_connection(f, conn) }
f.puts " end"
end
def save_connection(f, conn)
- in_sym = @pm.inputs.patch(conn.input)
+ in_sym = @pm.inputs.key(conn.input)
in_chan = conn.input_chan ? conn.input_chan + 1 : 'nil'
- out_sym = @pm.outputs.patch(conn.output)
+ out_sym = @pm.outputs.key(conn.output)
out_chan = conn.output_chan + 1
- f.puts " conn :#{in_sym}, #{in_chan}, #{out_sym}, #{out_chan} do"
+ f.puts " conn :#{in_sym}, #{in_chan}, :#{out_sym}, #{out_chan} do"
f.puts " prog_chg #{conn.pc_prog}" if conn.pc?
f.puts " zone #{conn.note_num_to_name(conn.zone.begin)}, #{conn.note_num_to_name(conn.zone.end)}" if conn.zone
f.puts " xpose #{conn.xpose}" if conn.xpose
f.puts " filter #{conn.filter.text}" if conn.filter
f.puts " end"
@@ -183,45 +207,54 @@
else
UniMIDI::Output.all[port].open
end
end
- # Extremely simple filter text reader. Relies on indentation to detect end
- # of filter block.
+ def read_triggers(contents)
+ read_block_text('trigger', @triggers, contents)
+ end
+
def read_filters(contents)
- i = 0
- in_filter = false
- filter_indentation = nil
- filter_end_token = nil
+ read_block_text('filter', @filters, contents)
+ end
+
+ # Extremely simple block text reader. Relies on indentation to detect end
+ # of code block.
+ def read_block_text(name, containers, contents)
+ i = -1
+ in_block = false
+ block_indentation = nil
+ block_end_token = nil
contents.each_line do |line|
- if line =~ /^(\s*)filter\s*(.*)/
- filter_indentation, text = $1, $2
- @filters[i].text = text + "\n"
- in_filter = true
- filter_end_token = case text
+ if line =~ /^(\s*)#{name}\s*.*?(({|do|->\s*{|lambda\s*{)(.*))/
+ block_indentation, text = $1, $2
+ i += 1
+ containers[i].text = text + "\n"
+ in_block = true
+ block_end_token = case text
when /^{/
"}"
when /^do\b/
"end"
- when /^lambda\s*({|do)/
- $1 == "{" ? "}" : "end"
+ when /^(->|lambda)\s*({|do)/
+ $2 == "{" ? "}" : "end"
else
"}|end" # regex
end
- elsif in_filter
+ elsif in_block
line =~ /^(\s*)(.*)/
indentation, text = $1, $2
- if indentation.length <= filter_indentation.length
- if text =~ /^#{filter_end_token}/
- @filters[i].text << line
+ if indentation.length <= block_indentation.length
+ if text =~ /^#{block_end_token}/
+ containers[i].text << line
end
- i += 1
- in_filter = false
+ in_block = false
else
- @filters[i].text << line
+ containers[i].text << line
end
end
end
+ containers.each { |thing| thing.text.strip! }
end
end
end