Module xVoiceManager
This class keeps track of active, playing voices as they are triggered.
.
In more detail
This class offers advanced voice-management for your own applications. This includes the ability to trigger and release specific instruments in specific tracks, while preserving the ability to freely move around in Renoise while doing so.
Without voice-management it would be too easy to create hanging notes. Everything from switching track, instrument or octave while playing, to having multiple MIDI sources could cause trouble. A good voice-manager will understand this and be able to determine the originating 'place' where the voice got triggered.
Column allocation
The class is able to assist with automatic note-column allocation while recording. It's a basic approach, but close enough to how Renoise usually works to feel familiar.
- Recordings start from the currently selected note column
- New note columns (voices) are allocated as new notes arrive
- Voices stay within their column as other voices are released/removed
Polyphony limits
In Renoise, you can play a maximum of 12 voices in a single track (maximum number of note-columns).
If you are familiar with MIDI recording in Renoise, you probably have noticed that there exist a monophonic trigger option. Enabling this option will restrict the number of possible voices (and note-columns) during a recording session. xVoiceManager has no "monophonic" mode as such, but setting the voice_limit to 1 should yield the same result.
Column allocation
When automatic column-allocation is enabled, new note columns are allocated as the need arises. When this feature is enabled, the polyphonic limit can never be larger than the number of available columns, starting from the column_index of the first voice. So if you start recording while in note-column 11, you have a maximum polyphony of 2 voices before voice stealing kicks in.
Voice stealing
Voice stealing takes effect the moment you feed a xVoiceManager with more voices than the current polyphony limit allows. xVoiceManager is using a simple heuristic which will replace the oldest voice with the new one (just like how Renoise does it).
Observable events
Attach notifiers to detect when voices are triggered or released.
triggered_observable
-> fired right after a voice starts playing
released_observable
-> fired right before a voice is released
After you have attached a notifier, you will receive a 'bang', but no argument. Instead, you
should look for the triggered/released_index
properties - they will contain the value you need.
Usage example
How to instantiate a copy of this class, and feed xMidiMessages into it:
local voicemgr = xVoiceManager{
follow_track = false,
}
voicemgr.triggered_observable:add_notifier(function()
-- use 'voicemgr.triggered_index'
end)
voicemgr.released_observable:add_notifier(function()
-- use 'voicemgr.released_index'
end)
local xmsg = some_note_on_message -- provide your own message
voicemgr:input_message(xmsg) -- should trigger our notifier
Functions
__init (table) | [Constructor] accepts a single argument for initializing the class |
input_message (xmsg) | [Class] Input a message into the voice-manager |
input_note_column (xnotecol, col_idx, line_idx) | [Class] Alternative input method, convert notecol to xmsg (when possible) and invoke input_message |
release_all () | [Class] Release all active voices |
release_all_instrument (instr_idx) | [Class] Release all voices associated with a specific instrument |
release_all_track (track_idx) | [Class] Release all voices associated with a specific track |
release (voice_idx) | [Class] Release specific voice |
get_voice_index (xmsg) | [Class] Locate among active voices, taking the pitch + track + instrument into consideration (if all match, the voice is considered active...) |
get_available_columns (track_idx) | [Class] Get the first column which does not have a playing voice assigned to it |
_steal_voice () | [Class] Release the oldest voice (release, then trigger stolen_observable) |
_attach_to_song () | [Class] Monitor changes to tracks and instruments |
get_by_pitch (pitch) | [Class] Get a voice with a specific pitch |
get_higher (idx) | [Class] Get a voice which has a higher pitch than the provided one |
get_highest () | [Class] Get the voice with the highest pitch |
get_lower (idx) | [Class] Get a voice which has a lower pitch than the provided one |
get_lowest () | [Class] Get the voice with the lowest pitch |
_register (xmsg) | [Class] Register/add a voice |
_check_expired () | [Class] Check if any voices have expired (when duration is set) |
Tables
self.voices | table |
Fields
self.voice_limit | the maximum number of voices (0 = 'unlimited') |
self.duration | number, note duration in seconds (0 = infinite) |
self.column_allocation | bool, whether to use automatic column allocation or not |
self.released_index | voice about to be released (0 = none) |
self.triggered_index | newly triggered voice (0 = none) |
self.stolen_index | when voice got stolen (refer to released_index) |
Functions
- __init (table)
-
[Constructor] accepts a single argument for initializing the class
Parameters:
- table
{
voice_limit (number) duration (number) column_allocation (boolean) follow_track (boolean) follow_instrument (boolean) follow_octave (boolean)
}
- table
{
- input_message (xmsg)
-
[Class] Input a message into the voice-manager
Parameters:
- xmsg (xMidiMessage)
Returns:
- xMidiMessage (added/removed), false (ignored/active) or nil (invalid)
- int (voice index), when added or active
- input_note_column (xnotecol, col_idx, line_idx)
-
[Class] Alternative input method, convert notecol to xmsg (when possible)
and invoke input_message
Parameters:
- xnotecol (xNoteColumn)
- col_idx (number)
- line_idx (number)
- release_all ()
- [Class] Release all active voices
- release_all_instrument (instr_idx)
-
[Class] Release all voices associated with a specific instrument
Parameters:
- instr_idx
- release_all_track (track_idx)
-
[Class] Release all voices associated with a specific track
Parameters:
- track_idx
- release (voice_idx)
-
[Class] Release specific voice
Parameters:
- voice_idx
- get_voice_index (xmsg)
-
[Class] Locate among active voices, taking the pitch + track + instrument into
consideration (if all match, the voice is considered active...)
Parameters:
- xmsg (xMidiMessage) should be a MIDI note-message
Returns:
-
number or nil
- get_available_columns (track_idx)
-
[Class] Get the first column which does not have a playing voice assigned to it
Parameters:
- track_idx , which track to check
Returns:
-
table
, available column indices - _steal_voice ()
- [Class] Release the oldest voice (release, then trigger stolen_observable)
- _attach_to_song ()
- [Class] Monitor changes to tracks and instruments
- get_by_pitch (pitch)
-
[Class] Get a voice with a specific pitch
Parameters:
- pitch (number)
Returns:
- xMidiMessage or nil
- int or nil
- get_higher (idx)
-
[Class] Get a voice which has a higher pitch than the provided one
Parameters:
- idx (number)
Returns:
- xMidiMessage or nil
- int or nil
- get_highest ()
-
[Class] Get the voice with the highest pitch
Returns:
- xMidiMessage or nil
- int or nil
- get_lower (idx)
-
[Class] Get a voice which has a lower pitch than the provided one
Parameters:
- idx (number)
Returns:
-
xMidiMessage or nil
- get_lowest ()
-
[Class] Get the voice with the lowest pitch
Returns:
-
xMidiMessage or nil
- _register (xmsg)
-
[Class] Register/add a voice
Parameters:
- xmsg (xMidiMessage) should be a MIDI note-message
- _check_expired ()
- [Class] Check if any voices have expired (when duration is set)
Tables
Fields
- self.voice_limit
- the maximum number of voices (0 = 'unlimited')
- self.duration
- number, note duration in seconds (0 = infinite)
- self.column_allocation
- bool, whether to use automatic column allocation or not
- self.released_index
- voice about to be released (0 = none)
- self.triggered_index
- newly triggered voice (0 = none)
- self.stolen_index
- when voice got stolen (refer to released_index)