class PM::DSL
Implements a DSL
for describing a PatchMaster
setup.
Public Class Methods
new()
click to toggle source
# File lib/patchmaster/dsl.rb, line 11 def initialize @pm = PatchMaster.instance init end
Public Instance Methods
alias_input(new_sym, old_sym)
click to toggle source
# File lib/patchmaster/dsl.rb, line 187 def alias_input(new_sym, old_sym) @inputs[new_sym] = @inputs[old_sym] end
alias_output(new_sym, old_sym)
click to toggle source
# File lib/patchmaster/dsl.rb, line 191 def alias_output(new_sym, old_sym) @outputs[new_sym] = @outputs[old_sym] end
code_key(key_or_sym, &block)
click to toggle source
# File lib/patchmaster/dsl.rb, line 82 def code_key(key_or_sym, &block) ck = CodeKey.new(to_binding_key(key_or_sym), CodeChunk.new(block)) @pm.bind_code(ck) @code_keys << ck end
connection(in_sym, in_chan, out_sym, out_chan=nil) { |conn| ... }
click to toggle source
in_chan can be skipped, so “connection :foo, :bar, 1” is the same as “connection :foo, nil, :bar, 1”.
# File lib/patchmaster/dsl.rb, line 122 def connection(in_sym, in_chan, out_sym, out_chan=nil) input = @inputs[in_sym] if in_chan.kind_of? Symbol out_chan = out_sym out_sym = in_chan in_chan = nil end raise "can't find input instrument #{in_sym}" unless input output = @outputs[out_sym] raise "can't find outputput instrument #{out_sym}" unless output @conn = Connection.new(input, in_chan, output, out_chan) @patch << @conn yield @conn if block_given? end
filter(&block)
click to toggle source
# File lib/patchmaster/dsl.rb, line 171 def filter(&block) @conn.filter = Filter.new(CodeChunk.new(block)) @filters << @conn.filter end
Also aliased as: f
init()
click to toggle source
Initialize state used for reading.
# File lib/patchmaster/dsl.rb, line 17 def init @inputs = {} @outputs = {} @triggers = [] @filters = [] @code_keys = [] @songs = {} # key = name, value = song end
input(port_num, sym, name=nil)
click to toggle source
# File lib/patchmaster/dsl.rb, line 35 def input(port_num, sym, name=nil) raise "input: two inputs can not have the same symbol (:#{sym})" if @inputs[sym] input = InputInstrument.new(sym, name, port_num, @pm.use_midi?) @inputs[sym] = input @pm.inputs << input rescue => ex raise "input: error creating input instrument \"#{name || sym}\" on input port #{port_num}: #{ex}" end
Also aliased as: inp
load(file)
click to toggle source
# File lib/patchmaster/dsl.rb, line 26 def load(file) contents = IO.read(file) init instance_eval(contents) read_code_keys(contents) read_triggers(contents) read_filters(contents) end
message(name, bytes)
click to toggle source
# File lib/patchmaster/dsl.rb, line 58 def message(name, bytes) @pm.messages[name.downcase] = [name, bytes] end
message_key(key_or_sym, name)
click to toggle source
# File lib/patchmaster/dsl.rb, line 62 def message_key(key_or_sym, name) if name.is_a?(Symbol) name, key_or_sym = key_or_sym, name $stderr.puts "WARNING: the arguments to message_key are now key first, then name." $stderr.puts "I will use #{name} as the name and #{key_or_sym} as the key for now." $stderr.puts "Please swap them for future compatability." end if key_or_sym.is_a?(String) && name.is_a?(String) if name.length == 1 && key_or_sym.length > 1 name, key_or_sym = key_or_sym, name $stderr.puts "WARNING: the arguments to message_key are now key first, then name." $stderr.puts "I will use #{name} as the name and #{key_or_sym} as the key for now." $stderr.puts "Please swap them for future compatability." elsif name.length == 1 && key_or_sym.length == 1 raise "message_key: since both name and key are one-character strings, I can't tell which is which. Please make the name longer." end end @pm.bind_message(name, to_binding_key(key_or_sym)) end
notes(txt)
click to toggle source
# File lib/patchmaster/dsl.rb, line 102 def notes(txt) @song.notes = txt end
output(port_num, sym, name=nil)
click to toggle source
# File lib/patchmaster/dsl.rb, line 46 def output(port_num, sym, name=nil) raise "output: two outputs can not have the same symbol (:#{sym})" if @outputs[sym] output = OutputInstrument.new(sym, name, port_num, @pm.use_midi?) @outputs[sym] = output @pm.outputs << output rescue => ex raise "output: error creating output instrument \"#{name || sym}\" on output port #{port_num}: #{ex}" end
patch(name) { |patch| ... }
click to toggle source
# File lib/patchmaster/dsl.rb, line 106 def patch(name) @patch = Patch.new(name) @song << @patch yield @patch if block_given? end
prog_chg(bank_or_prog, prog=nil)
click to toggle source
If only bank_or_prog
is specified, then it's a program change. If both, then it's bank number.
# File lib/patchmaster/dsl.rb, line 142 def prog_chg(bank_or_prog, prog=nil) if prog @conn.bank = bank_or_prog @conn.pc_prog = prog else @conn.pc_prog = bank_or_prog end end
Also aliased as: pc
save(file)
click to toggle source
****************************************************************
# File lib/patchmaster/dsl.rb, line 197 def save(file) File.open(file, 'w') { |f| save_instruments(f) save_messages(f) save_message_keys(f) save_code_keys(f) save_triggers(f) save_songs(f) save_song_lists(f) } end
save_code_keys(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 231 def save_code_keys(f) @pm.code_bindings.values.each do |code_key| str = if code_key.code_chunk.text[0] == '{' "code_key(#{to_save_key(code_key.key).inspect}) #{code_key.code_chunk.text}" else "code_key #{to_save_key(code_key.key).inspect} #{code_key.code_chunk.text}" end f.puts str end end
save_connection(f, conn)
click to toggle source
# File lib/patchmaster/dsl.rb, line 268 def save_connection(f, conn) in_chan = conn.input_chan ? conn.input_chan + 1 : 'nil' out_chan = conn.output_chan + 1 f.puts " conn :#{conn.input.sym}, #{in_chan}, :#{conn.output.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.code_chunk.text}" if conn.filter f.puts " end" end
save_instruments(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 209 def save_instruments(f) @pm.inputs.each do |instr| f.puts "input #{instr.port_num}, :#{instr.sym}, #{instr.name.inspect}" end @pm.outputs.each do |instr| f.puts "output #{instr.port_num}, :#{instr.sym}, #{instr.name.inspect}" end f.puts end
save_message_keys(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 225 def save_message_keys(f) @pm.message_bindings.each do |key, message_name| f.puts "message_key #{to_save_key(key).inspect}, #{message_name.inspect}" end end
save_messages(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 219 def save_messages(f) @pm.messages.each do |_, (correct_case_name, msg)| f.puts "message #{correct_case_name.inspect}, #{msg.inspect}" end end
save_patch(f, patch)
click to toggle source
# File lib/patchmaster/dsl.rb, line 261 def save_patch(f, patch) f.puts " patch #{patch.name.inspect} do" f.puts " start_bytes #{patch.start_bytes.inspect}" if patch.start_bytes patch.connections.each { |conn| save_connection(f, conn) } f.puts " end" end
save_song_lists(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 279 def save_song_lists(f) @pm.song_lists.each do |sl| next if sl == @pm.all_songs f.puts "song_list #{sl.name.inspect}, [" @pm.all_songs.songs.each do |song| f.puts " #{song.name.inspect}," end f.puts "]" end end
save_songs(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 252 def save_songs(f) @pm.all_songs.songs.each do |song| f.puts "song #{song.name.inspect} do" song.patches.each { |patch| save_patch(f, patch) } f.puts "end" f.puts end end
save_triggers(f)
click to toggle source
# File lib/patchmaster/dsl.rb, line 242 def save_triggers(f) @pm.inputs.each do |instrument| instrument.triggers.each do |trigger| str = "trigger :#{instrument.sym}, #{trigger.bytes.inspect} #{trigger.code_chunk.text}" f.puts str end end f.puts end
song(name) { |song| ... }
click to toggle source
# File lib/patchmaster/dsl.rb, line 96 def song(name) @song = Song.new(name) # ctor saves into @pm.all_songs @songs[name] = @song yield @song if block_given? end
song_list(name, song_names)
click to toggle source
# File lib/patchmaster/dsl.rb, line 177 def song_list(name, song_names) sl = SongList.new(name) @pm.song_lists << sl song_names.each do |sn| song = @songs[sn] raise "song \"#{sn}\" not found (song list \"#{name}\")" unless song sl << song end end
start_bytes(bytes)
click to toggle source
# File lib/patchmaster/dsl.rb, line 112 def start_bytes(bytes) @patch.start_bytes = bytes end
stop_bytes(bytes)
click to toggle source
# File lib/patchmaster/dsl.rb, line 116 def stop_bytes(bytes) @patch.stop_bytes = bytes end
transpose(xpose)
click to toggle source
# File lib/patchmaster/dsl.rb, line 165 def transpose(xpose) @conn.xpose = xpose end
trigger(instrument_sym, bytes, &block)
click to toggle source
# File lib/patchmaster/dsl.rb, line 88 def trigger(instrument_sym, bytes, &block) instrument = @inputs[instrument_sym] raise "trigger: error finding instrument #{instrument_sym}" unless instrument t = Trigger.new(bytes, CodeChunk.new(block)) instrument.triggers << t @triggers << t end
zone(start_or_range=nil, stop=nil)
click to toggle source
If start_or_range
is a Range, use that. Else either or both params may be nil.
# File lib/patchmaster/dsl.rb, line 154 def zone(start_or_range=nil, stop=nil) @conn.zone = if start_or_range.kind_of? Range start_or_range elsif start_or_range == nil && stop == nil nil else ((start_or_range || 0) .. (stop || 127)) end end
Also aliased as: z
Private Instance Methods
read_block_text(name, containers, contents)
click to toggle source
Extremely simple block text reader. Relies on indentation to detect end of code block.
# File lib/patchmaster/dsl.rb, line 325 def read_block_text(name, containers, contents) i = -1 in_block = false block_indentation = nil block_end_token = nil chunk = nil contents.each_line do |line| if line =~ /^(\s*)#{name}\s*.*?(({|do|->\s*{|lambda\s*{)(.*))/ block_indentation, text = $1, $2 i += 1 chunk = containers[i].code_chunk chunk.text = text + "\n" in_block = true block_end_token = case text when /^{/ "}" when /^do\b/ "end" when /^(->|lambda)\s*({|do)/ $2 == "{" ? "}" : "end" else "}|end" # regex end elsif in_block line =~ /^(\s*)(.*)/ indentation, text = $1, $2 if indentation.length <= block_indentation.length if text =~ /^#{block_end_token}/ chunk.text << line end in_block = false else chunk.text << line end end end containers.each do |thing| text = thing.code_chunk.text text.strip! if text end end
read_code_keys(contents)
click to toggle source
# File lib/patchmaster/dsl.rb, line 319 def read_code_keys(contents) read_block_text('code_key', @code_keys, contents) end
read_filters(contents)
click to toggle source
# File lib/patchmaster/dsl.rb, line 315 def read_filters(contents) read_block_text('filter', @filters, contents) end
read_triggers(contents)
click to toggle source
# File lib/patchmaster/dsl.rb, line 311 def read_triggers(contents) read_block_text('trigger', @triggers, contents) end
to_binding_key(key_or_sym)
click to toggle source
Translate symbol like :f1 to the proper function key value.
# File lib/patchmaster/dsl.rb, line 295 def to_binding_key(key_or_sym) if key_or_sym.is_a?(Symbol) && PM::Main::FUNCTION_KEY_SYMBOLS[key_or_sym] key_or_sym = PM::Main::FUNCTION_KEY_SYMBOLS[key_or_sym] end end
to_save_key(key)
click to toggle source
Translate function key values into symbol strings and other keys into double-quoted strings.
# File lib/patchmaster/dsl.rb, line 303 def to_save_key(key) if PM::Main::FUNCTION_KEY_SYMBOLS.value?(key) PM::Main::FUNCTION_KEY_SYMBOLS.key(key) else key end end