module SonicPiAkaiApcMini::API

Constants

DEFAULT_TARGET

default ‘target` is 0-0.999 instead of 0.1 because many parameters have [0,1) as range and throw an error when passed 1 (e.g. tb303 synth’s res). It’s not the most common usecase, but for the common use case it makes no difference so I think it’s a good default.

Public Instance Methods

attach_fader(n, node, property, target = DEFAULT_TARGET) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 22
def attach_fader(n, node, property, target = DEFAULT_TARGET)
  set_fader(n, target) do |value|
    control node, property => value
  end
end
fader(n, target = DEFAULT_TARGET) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 13
def fader(n, target = DEFAULT_TARGET)
  # TODO: Try to optimize speed, there is some latency because the
  # controller send a lot of events (too much granularity). It is in theory
  # possible to save some of it by `get`ting the value directly instead of
  # waiting for all the events to be processed.
  value = get("fader_#{n}", 0)
  Helpers.normalize(value, target)
end
free_play(row, col, notes, options = {}) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 72
def free_play(row, col, notes, options = {})
  Helpers.key_range(row, col, notes.size).each.with_index do |key, i|
    midi_note_on key, Controller.model.light_yellow
    set "free_play_#{key}", notes[i]
  end

  use_real_time

  message = sync(:free_play)
  note_control = play message[:note], { sustain: 9999 }.merge(options)
  set "free_play_playing_#{message[:key]}", note_control
end
initialize_akai(model) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 97
def initialize_akai(model)
  Controller.model = model
  # This loop manages faders. Whenever they change, the new value is stored via set,
  # and the corresponding light is turned on/off.
  live_loop :faders do
    use_real_time
    note_number, value = sync(Controller.model.midi_event(:control_change))
    fader_number = note_number - Controller.model.fader_offset
    set "fader_#{fader_number}", value
    if Controller.model.fader_light_offset
      light_note_number = note_number + Controller.model.fader_light_offset
      midi_note_on light_note_number,
                   value.zero? ? Controller.model.light_off : Controller.model.light_red
    end
  end

  # Manages the buttons in the grid, both as switches, selectors, and to
  # "free play". Whenever one is pressed, we check if that row is being used
  # to "free play". If it is, we play. If it's not, we check if its used as
  # a selector and manage it. Otherwise, we manage it as a switch.
  live_loop :switches_and_freeplay do
    use_real_time
    n, _vel = sync(Controller.model.midi_event(:note_on))
    if note = get("free_play_#{n}")
      cue :free_play, note: note, key: n
    elsif keys = get("selector_keys_#{n}")
      keys.each do |k|
        midi_note_on k, 3
      end
      midi_note_on n, 1
      set "selector_current_value_#{keys.first}..#{keys.last}", n - keys.first
    else
      new_value = !get("switch_#{n}", false)
      set "switch_#{n}", new_value
      midi_note_on n, (new_value ? 1 : 0)
    end
  end

  live_loop :free_play_note_offs do
    use_real_time
    n, _vel = sync(Controller.model.midi_event(:note_off))
    if note_control = get("free_play_playing_#{n}")
      release = note_control.args['release'] || note_control.info.arg_defaults[:release]
      control note_control, amp: 0, amp_slide: release
      at(release) { note_control.kill }
    end
  end
end
loop_rows(duration, rows) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 39
def loop_rows(duration, rows)
  first_row = rows.keys.max
  Controller.model.grid_columns.times do |beat|
    prev = (beat - 1) % 8
    prev_key = Helpers.key(first_row, prev)
    beat_key = Helpers.key(first_row, beat)
    midi_note_on prev_key, get("switch_#{prev_key}") ? Controller.model.light_green : Controller.model.light_off
    midi_note_on beat_key, Controller.model.light_yellow
    rows.each do |row, sound|
      in_thread(&sound) if switch?(row, beat)
    end
    sleep duration.to_f / Controller.model.grid_columns
  end
end
loop_rows_synth(duration, rows, notes, options = {}) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 54
def loop_rows_synth(duration, rows, notes, options = {})
  rows = rows.map.with_index do |row, i|
    [row, lambda do
            opts = options.respond_to?(:call) ? options.call : options
            play(notes[i], opts)
          end]
  end.to_h
  loop_rows(duration, rows)
end
reset_free_play(row, col, size) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 64
def reset_free_play(row, col, size)
  size = size.size unless size.is_a?(Integer) # so we can pass the same ring
  Helpers.key_range(row, col, size).each do |key|
    midi_note_on key, Controller.model.light_off
    set "free_play_#{key}", nil
  end
end
selector(row, col, values) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 85
def selector(row, col, values)
  # TODO: selector is quite messy. It can use a refactor and proper reset/cleanup (like free_play's).
  krange = Helpers.key_range(row, col, values.size)
  set "selector_values_#{krange}", values.ring
  set "selector_current_value_#{krange}", 0 if get("selector_current_value_#{krange}").nil?
  krange.each.with_index do |key, i|
    set "selector_keys_#{key}", krange.to_a
    midi_note_on key, i == get("selector_current_value_#{krange}") ? 1 : 3
  end
  values[get("selector_current_value_#{krange}")]
end
set_fader(n, target = DEFAULT_TARGET, &block) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 28
def set_fader(n, target = DEFAULT_TARGET, &block)
  # first we just call the block with the current value, or 0
  block.call(Helpers.normalize(get("fader_#{n}", 0), target))
  # and set a loop that will cal it again on every change
  live_loop "global_fader_#{n}" do
    use_real_time
    value = sync("fader_#{n}")
    block.call(Helpers.normalize(value, target))
  end
end
switch?(row, col) click to toggle source
# File lib/sonic-pi-akai-apc-mini/api.rb, line 3
def switch?(row, col)
  !!get("switch_#{Helpers.key(row, col)}")
end