class PM::PatchMaster

Global behavior: master list of songs, list of song lists, stuff like that.

Typical use:

PatchMaster.instance.load("my_pm_dsl_file")
PatchMaster.instance.start
# ...when you're done
PatchMaster.instance.stop

Constants

DEBUG_FILE

Attributes

all_songs[R]
code_bindings[R]
cursor[R]

A Cursor to which we delegate incoming position methods (song_list, song, patch, next_song, prev_patch, etc.)

gui[RW]
inputs[R]
loaded_file[R]
message_bindings[R]
messages[R]
outputs[R]
song_lists[R]
use_midi[RW]
use_midi?[RW]

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/patchmaster/patchmaster.rb, line 34
def initialize
  @running = false
  @cursor = Cursor.new(self)
  super(@cursor)
  @use_midi = true
  @gui = nil
  @message_bindings = {}
  @code_bindings = {}

  if $DEBUG
    @debug_file = File.open(DEBUG_FILE, 'a')
  end

  init_data
end

Public Instance Methods

bind_code(code_key) click to toggle source
# File lib/patchmaster/patchmaster.rb, line 86
def bind_code(code_key)
  @code_bindings[code_key.key] = code_key
end
bind_message(name, key) click to toggle source
# File lib/patchmaster/patchmaster.rb, line 82
def bind_message(name, key)
  @message_bindings[key] = name
end
close_debug_file() click to toggle source
# File lib/patchmaster/patchmaster.rb, line 180
def close_debug_file
  @debug_file.close if @debug_file
end
debug(str) click to toggle source

Output str to @debug_file or $stderr.

# File lib/patchmaster/patchmaster.rb, line 173
def debug(str)
  return unless $DEBUG
  f = @debug_file || $stderr
  f.puts str
  f.flush
end
init_data() click to toggle source

Initializes the cursor and all data.

# File lib/patchmaster/patchmaster.rb, line 91
def init_data
  @cursor.clear
  @inputs = []
  @outputs = []
  @song_lists = []
  @all_songs = SortedSongList.new('All Songs')
  @song_lists << @all_songs
  @messages = {}
end
load(file) click to toggle source

Loads file. Does its best to restore the current song list, song, and patch after loading.

# File lib/patchmaster/patchmaster.rb, line 56
def load(file)
  restart = running?
  stop

  @cursor.mark
  init_data
  DSL.new.load(file)
  @loaded_file = file
  @cursor.restore

  if restart
    start(false)
  elsif @cursor.patch
    @cursor.patch.start
  end
rescue => ex
  raise("error loading #{file}: #{ex}\n" + caller.join("\n"))
end
no_gui!() click to toggle source
# File lib/patchmaster/patchmaster.rb, line 50
def no_gui!
  @no_gui = true
end
panic(individual_notes=false) click to toggle source

Sends the CM_ALL_NOTES_OFF controller message to all output instruments on all 16 MIDI channels. If individual_notes is true send individual NOTE_OFF messages to all notes as well.

# File lib/patchmaster/patchmaster.rb, line 158
def panic(individual_notes=false)
  debug("panic(#{individual_notes})")
  @outputs.each do |out|
    buf = []
    MIDI_CHANNELS.times do |chan|
      buf += [CONTROLLER + chan, CM_ALL_NOTES_OFF, 0]
      if individual_notes
        buf += (0..127).collect { |note| [NOTE_OFF + chan, note, 0] }.flatten
      end
    end
    out.midi_out(buf)
  end
end
run() click to toggle source

Run PatchMaster without the GUI. Don't use this when using PM::Main. If there is a GUI then forward this request to it. Otherwise, call start, wait for inputs' MIDIEye listener threads to finish, then call stop. Note that normally nothing stops those threads, so this is used as a way to make sure the script doesn't quit until killed by something like SIGINT.

# File lib/patchmaster/patchmaster.rb, line 123
def run
  if @gui
    @gui.run
  else
    start(true)
    @inputs.each { |input| input.listener.join }
    stop
  end
end
running?() click to toggle source
# File lib/patchmaster/patchmaster.rb, line 133
def running?
  @running
end
save(file) click to toggle source
# File lib/patchmaster/patchmaster.rb, line 75
def save(file)
  DSL.new.save(file)
  @loaded_file = file
rescue => ex
  raise("error saving #{file}: #{ex}" + caller.join("\n"))
end
send_message(name) click to toggle source

Send the message with the given name to all outputs. Names are matched case-insensitively.

# File lib/patchmaster/patchmaster.rb, line 139
def send_message(name)
  _correct_case_name, msg = @messages[name.downcase]
  if !msg
    message("Message \"#{name}\" not found")
    return
  end

  debug("Sending message \"#{name}\"")
  @outputs.each { |out| out.midi_out(msg) }

  # If the user accidentally calls send_message in a filter at the end,
  # then the filter will return whatever this method returns. Just in
  # case, return nil instead of whatever the preceding code would return.
  nil
end
start(init_cursor = true) click to toggle source

If init_cursor is true (the default), initializes current song list, song, and patch.

# File lib/patchmaster/patchmaster.rb, line 103
def start(init_cursor = true)
  @cursor.init if init_cursor
  @cursor.patch.start if @cursor.patch
  @running = true
  @inputs.map(&:start)
end
stop() click to toggle source

Stop everything, including input instruments' MIDIEye listener threads.

# File lib/patchmaster/patchmaster.rb, line 111
def stop
  @cursor.patch.stop if @cursor.patch
  @inputs.map(&:stop)
  @running = false
end