Class: Musa::MIDIVoices::MIDIVoice Private
- Defined in:
- lib/musa-dsl/midi/midi-voices.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Individual MIDI channel voice with sequencer-synchronized note management.
Manages the state of a single MIDI channel including active notes, controller values, and sustain pedal. All note scheduling is tied to the sequencer clock, ensuring proper timing in fast-forward mode or during quantized playback.
Supports indefinite notes (manual note-off), automatic note-off scheduling, callbacks on note stop, and fast-forward mode for silent state updates.
Instance Attribute Summary collapse
-
#active_pitches ⇒ Array<Hash>
readonly
private
Metadata for each of the 128 MIDI pitches.
-
#channel ⇒ Integer
readonly
private
MIDI channel number (0-15).
-
#do_log ⇒ Boolean
private
Whether this voice logs every emitted message.
-
#name ⇒ String?
private
Optional name used in log messages.
-
#output ⇒ #puts?
readonly
private
MIDI destination.
-
#sequencer ⇒ Musa::Sequencer::Sequencer
readonly
private
Sequencer driving this voice.
-
#tick_duration ⇒ Rational
readonly
private
Duration (in bars) of a sequencer tick; used to schedule note offs.
Instance Method Summary collapse
-
#all_notes_off ⇒ void
private
Sends immediate note-off messages for all active pitches and an all-notes-off message on this channel and resets internal state.
-
#controller ⇒ ControllersControl
private
MIDI CC manager for this voice.
-
#fast_forward=(enabled) ⇒ void
private
Turns fast-forward on/off for this voice.
-
#fast_forward? ⇒ Boolean
private
True when in fast-forward mode (notes registered but not emitted).
- #initialize(sequencer:, output:, channel:, name: nil, do_log: nil) ⇒ void constructor private
-
#log(msg) ⇒ void
private
Logs a message tagging the current voice.
-
#note(pitchvalue = nil, pitch: nil, velocity: nil, duration: nil, duration_offset: nil, note_duration: nil, velocity_off: nil) ⇒ NoteControl?
private
Plays one or several MIDI notes.
-
#sustain_pedal ⇒ Integer?
private
Current sustain pedal value.
-
#sustain_pedal=(value) ⇒ Object
private
Sets the sustain pedal state.
-
#to_s ⇒ String
private
Human-readable voice description.
Constructor Details
#initialize(sequencer:, output:, channel:, name: nil, do_log: nil) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 248 def initialize(sequencer:, output:, channel:, name: nil, do_log: nil) do_log ||= false @sequencer = sequencer @output = output @channel = channel @name = name @do_log = do_log @tick_duration = Rational(1, @sequencer.) @controllers_control = ControllersControl.new(@output, @channel) @active_pitches = [] fill_active_pitches @active_pitches @sequencer.logger.warn 'voice without output' unless @output self end |
Instance Attribute Details
#active_pitches ⇒ Array<Hash> (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns metadata for each of the 128 MIDI pitches. Mainly used internally.
237 238 239 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 237 def active_pitches @active_pitches end |
#channel ⇒ Integer (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns MIDI channel number (0-15).
234 235 236 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 234 def channel @channel end |
#do_log ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns whether this voice logs every emitted message.
225 226 227 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 225 def do_log @do_log end |
#name ⇒ String?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns optional name used in log messages.
222 223 224 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 222 def name @name end |
#output ⇒ #puts? (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns MIDI destination. When nil the voice becomes silent.
231 232 233 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 231 def output @output end |
#sequencer ⇒ Musa::Sequencer::Sequencer (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns sequencer driving this voice.
228 229 230 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 228 def sequencer @sequencer end |
#tick_duration ⇒ Rational (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns duration (in bars) of a sequencer tick; used to schedule note offs.
240 241 242 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 240 def tick_duration @tick_duration end |
Instance Method Details
#all_notes_off ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Sends immediate note-off messages for all active pitches and an all-notes-off message on this channel and resets internal state.
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 349 def all_notes_off @output.puts MIDIEvents::ChannelMessage.new(0xb, @channel, 0x7b, 0) @active_pitches.each_with_index do |pitch_status, pitch| # Everything goes quiet here, including the notes that had let go of a # pitch someone else was holding and were waiting for it to fall to # zero. Clearing the table without telling them would leave them # sounding for ever as far as they know, and their callbacks unrun. waiting = pitch_status[:awaiting_silence] pitch_status[:awaiting_silence] = [] waiting.each(&:_pitch_silenced) next if @fast_forward || pitch_status[:note_controls].empty? msg = MIDIEvents::NoteOff.new(@channel, pitch, 0) @output&.puts msg end @active_pitches.clear fill_active_pitches @active_pitches end |
#controller ⇒ ControllersControl
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns MIDI CC manager for this voice.
330 331 332 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 330 def controller @controllers_control end |
#fast_forward=(enabled) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Turns fast-forward on/off for this voice.
When disabling it, pending notes that were held silently are sent again so the synth is in sync with the sequencer state.
276 277 278 279 280 281 282 283 284 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 276 def fast_forward=(enabled) if @fast_forward && !enabled (0..127).each do |pitch| @output.puts MIDIEvents::NoteOn.new(@channel, pitch, @active_pitches[pitch][:velocity]) unless @active_pitches[pitch][:note_controls].empty? end end @fast_forward = enabled end |
#fast_forward? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns true when in fast-forward mode (notes registered but not emitted).
287 288 289 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 287 def fast_forward? @fast_forward end |
#log(msg) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Logs a message tagging the current voice.
375 376 377 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 375 def log(msg) @sequencer.logger.info('MIDIVoice') { "voice #{name || @channel}: #{msg}" } if @do_log end |
#note(pitchvalue = nil, pitch: nil, velocity: nil, duration: nil, duration_offset: nil, note_duration: nil, velocity_off: nil) ⇒ NoteControl?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Plays one or several MIDI notes.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 306 def note(pitchvalue = nil, pitch: nil, velocity: nil, duration: nil, duration_offset: nil, note_duration: nil, velocity_off: nil) pitch ||= pitchvalue if pitch velocity ||= 63 duration_offset ||= -@tick_duration # Only when there is a duration to compute it from. Computing it # unconditionally raised on the nil that means "no end of its own", # which NoteControl one layer down goes out of its way to accept, so # the indefinite note could be implemented but not asked for # (issue #81). The keyword's own default value was unusable. note_duration ||= [0, duration + duration_offset].max if duration velocity_off ||= 63 log "note #{pitch} has no duration: it will sound until note_off" unless note_duration NoteControl.new(self, pitch: pitch, velocity: velocity, duration: note_duration, velocity_off: velocity_off).note_on end end |
#sustain_pedal ⇒ Integer?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns current sustain pedal value.
342 343 344 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 342 def sustain_pedal @controllers_control[:sustain_pedal] end |
#sustain_pedal=(value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Sets the sustain pedal state.
337 338 339 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 337 def sustain_pedal=(value) @controllers_control[:sustain_pedal] = value end |
#to_s ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns human-readable voice description.
380 381 382 |
# File 'lib/musa-dsl/midi/midi-voices.rb', line 380 def to_s "voice #{@name} output: #{@output} channel: #{@channel}" end |