Class VoiceSeparator
Algorithm for separating polyphonic music into individual voices. Uses pitch proximity and voice leading principles.
public static class VoiceSeparator
- Inheritance
-
VoiceSeparator
- Inherited Members
Methods
Separate(NoteBuffer, int)
Separate notes into voices using pitch-proximity algorithm. Notes that overlap in time are forced into different voices; the VoiceSeparatorOptions overload adds voice-crossing and stepwise-motion penalties on top of that.
public static VoiceSeparationResult Separate(NoteBuffer buffer, int maxVoices = 4)
Parameters
bufferNoteBuffermaxVoicesint
Returns
Exceptions
- ArgumentNullException
bufferis null.
Separate(NoteBuffer, int, VoiceSeparatorOptions)
Separate notes into voices with custom options. At each onset the notes go to distinct voices at the least total cost, a note's cost being its distance from the voice's last pitch: a line continues in the voice nearest its last note, a note further than MaxMelodicInterval from every voice opens a new one while a voice is free, and notes that overlap in time are forced into different voices. VoiceSeparatorOptions adds a penalty for voice-order violations when AllowCrossings is false and a superlinear penalty for motion beyond a whole step when PreferStepwise is set. The voices come back highest first by average pitch, named for their register.
public static VoiceSeparationResult Separate(NoteBuffer buffer, int maxVoices, VoiceSeparatorOptions options)
Parameters
bufferNoteBuffermaxVoicesintoptionsVoiceSeparatorOptions
Returns
Remarks
The assignment within an onset used to keep the voices in register order whatever AllowCrossings said — the higher note always went to the lower-numbered voice — so a line entering above an active voice took that voice over and pushed its continuation into a new one: two lines cut and re-joined, and a canon answered an octave above its subject came back from DetectImitation(NoteBuffer, int) as answered below, or not at all, depending on the register the subject started in. Opening a new voice was also cheap enough — the seed's distance plus 4 — that a line moving by a third or a fourth was cut in two whenever a free voice was seeded nearby. The order constraint now applies only when crossings are forbidden, and a new voice costs more than any melodic continuation.
Exceptions
- ArgumentNullException
bufferoroptionsis null.
SeparateIntoSatb(NoteBuffer, VoiceSeparatorOptions?)
Convenience SATB separation: returns exactly 4 voices named Soprano/Alto/Tenor/Bass.
public static SatbSeparationResult SeparateIntoSatb(NoteBuffer buffer, VoiceSeparatorOptions? options = null)
Parameters
bufferNoteBufferoptionsVoiceSeparatorOptions
Returns
Exceptions
- ArgumentNullException
bufferis null.
SeparateIntoSatb(IEnumerable<NoteEvent>, VoiceSeparatorOptions?)
Convenience SATB separation: returns exactly 4 voices named Soprano/Alto/Tenor/Bass.
public static SatbSeparationResult SeparateIntoSatb(IEnumerable<NoteEvent> notes, VoiceSeparatorOptions? options = null)
Parameters
notesIEnumerable<NoteEvent>optionsVoiceSeparatorOptions
Returns
Exceptions
- ArgumentNullException
notesis null.