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