Class: Musa::MIDIVoices::MIDIVoices

Inherits:
Object
  • Object
show all
Defined in:
lib/musa-dsl/midi/midi-voices.rb

Overview

Note:

All durations are expressed as Rational numbers representing bars.

Note:

MIDI channels are zero-indexed (0-15), not 1-16.

High level helpers to drive one or more MIDI channels from a Sequencer::Sequencer.

A voice represents the state of a given MIDI channel (active notes, controllers, sustain pedal, etc.). MIDIVoices ties the life‑cycle of those voices to the sequencer clock so that note durations, waits and callbacks stay in the musical timeline even when running in fast-forward or quantized sessions.

Typical usage:

Examples:

Basic setup and playback

require 'musa-dsl'
require 'midi-communications'

clock     = Musa::Clock::TimerClock.new bpm: 120
transport = Musa::Transport::Transport.new clock
output    = MIDICommunications::Output.all.first

voices = Musa::MIDIVoices::MIDIVoices.new(
  sequencer: transport.sequencer,
  output:    output,
  channels:  [0, 1] # also accepts ranges such as 0..7
)

voices.voices.first.note pitch: 64, velocity: 90, duration: 1r / 4

Playing chords

voice = voices.voices.first
voice.note pitch: [60, 64, 67], velocity: 90, duration: 1r

Using note controls with callbacks

voice = voices.voices.first
note_ctrl = voice.note pitch: 60, duration: nil  # indefinite
note_ctrl.on_stop { puts "Note ended!" }
# ... later:
note_ctrl.note_off

Fast-forward for silent catch-up

voices.fast_forward = true
# ... replay past events ...
voices.fast_forward = false  # resumes audible output

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sequencer:, output:, channels:, do_log: nil) ⇒ void

Builds the voice container for one or many MIDI channels.

Parameters:

  • sequencer (Musa::Sequencer::Sequencer)

    sequencer that schedules waits and callbacks.

  • output (#puts, nil)

    anything responding to puts that accepts MIDIEvents::Events (typically a MIDICommunications output).

  • channels (Array<Numeric>, Range, Numeric)

    list of MIDI channels to control. Ranges are expanded automatically.

  • do_log (Boolean) (defaults to: nil)

    enables info level logs per emitted message.



102
103
104
105
106
107
108
109
110
111
# File 'lib/musa-dsl/midi/midi-voices.rb', line 102

def initialize(sequencer:, output:, channels:, do_log: nil)
  do_log ||= false

  @sequencer = sequencer
  @output = output
  @channels = channels.arrayfy.explode_ranges
  @do_log = do_log

  reset
end

Instance Attribute Details

#do_logBoolean

Returns whether verbose logging is enabled.

Returns:

  • (Boolean)

    whether verbose logging is enabled.



92
93
94
# File 'lib/musa-dsl/midi/midi-voices.rb', line 92

def do_log
  @do_log
end

#voicesArray<MIDIVoice> (readonly)

Returns read-only list of per-channel voices.

Returns:



122
123
124
# File 'lib/musa-dsl/midi/midi-voices.rb', line 122

def voices
  @voices
end

Instance Method Details

#fast_forward=(enabled) ⇒ void

This method returns an undefined value.

Enables or disables the fast-forward mode on every voice.

When enabled, notes are registered internally but their MIDI messages are not emitted, allowing the sequencer to catch up silently (e.g. when loading a snapshot).

Parameters:

  • enabled (Boolean)

    true to enable fast-forward, false to disable.



132
133
134
# File 'lib/musa-dsl/midi/midi-voices.rb', line 132

def fast_forward=(enabled)
  @voices.each { |voice| voice.fast_forward = enabled }
end

#panic(reset: nil) ⇒ void

This method returns an undefined value.

Sends all-notes-off on every channel and (optionally) a MIDI reset.

Parameters:

  • reset (Boolean) (defaults to: nil)

    whether to emit an FF SystemRealtime (panic) message.



140
141
142
143
144
145
146
# File 'lib/musa-dsl/midi/midi-voices.rb', line 140

def panic(reset: nil)
  reset ||= false

  @voices.each(&:all_notes_off)

  @output.puts MIDIEvents::SystemRealtime.new(0xff) if reset
end

#resetvoid

This method returns an undefined value.

Resets the collection recreating every Musa::MIDIVoices::MIDIVoice. Useful when the MIDI output has changed or after a panic.



117
118
119
# File 'lib/musa-dsl/midi/midi-voices.rb', line 117

def reset
  @voices = @channels.collect { |channel| MIDIVoice.new(sequencer: @sequencer, output: @output, channel: channel, do_log: @do_log) }.freeze
end