Monophonic Algorithm

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Below is a voice handling algorithm for a monophonic synth. It's untested. I'm thinking about implementing this hopefully in such a way as to make it voice independent, i.e. you could drop your own voice class into the code and it would work find as long as the voice implements the right methods. And posting the results here for review. It was fun last time I did that with the template state variable filter.

Code: Select all

Note-On
-------

Is there already a note playing?
  Yes
    1) Place the currently playing note number 
       at the end of the note list.
    2) Keep track of the new note number, e.g. 
       currentNote = newNote;
    3) Trigger Voice with the new note number.
  No
    1) Keep track of the new note number, e.g. 
       currentNote = newNote;
    2) Trigger Voice with the new note number.


Note-Off
--------

Is there a note playing?
  Yes
    Does the note-off number match the currently 
    playing note number?
      Yes
        Are there any notes in the note list?
          Yes
            1) Get the note number at the end 
               of the note list.
            2) Remove the note number at the 
               end of the note list.
            3) Keep track of the previous note 
               number, e.g currentNote = prevNote.
            4) Trigger Voice with the previous 
               note number.
          No
            Is the sustain pedal depressed?
              Yes
                1) Indicate that the current note 
                   is sustaining, e.g. 
                   currentNote = SUSTAINING;
              No
                1) Indicate that there isn't a note 
                   being played, e.g. 
                   currentNote = NOT_PLAYING;
                2) Release Voice.
      No
        Is the note-off number in the note list?
          Yes
            1) Remove note-off number from the 
               note list.
          No
            1) Do nothing.
  No
    Do nothing.


Sustain Pedal On
----------------

1) Indicate that the sustain pedal is on, e.g. 
   sustaining = true;


Sustain Pedal Off
-----------------

Is the sustain pedal on?
  Yes
    Is the current note sustaining, e.g. 
    if(currentNote == SUSTAINING)?
      Yes
        1) Indicate that there isn't a note being 
           played, e.g. 
           currentNote = NOT_PLAYING;
        2) Release Voice.
      No
        1) Do nothing.
  No
    1) Do nothing.
Any corrections or suggestions are welcome. The goal is to distill one approach to monophonic voice management into a pure algorithm, i.e. programming language independent.

Post

Leslie Sanford wrote:Below is a voice handling algorithm for a monophonic synth.
I do it with a stack,

Code: Select all

noteOn: 

Push new note on top of stack.
Set the top of stack note as playing.

noteOff: 

Scan whole stack and remove any notes that match the noteoff.
Set the top of stack note playing.
That'll always give you the most recent note playing. The key is that if you play 5 notes in order 1,2,3,4,5, and then release notes 2,3, if these are just removed from the stack, then the remaining notes will stay in the order in which they were played.

You can also get highest and lowest note priority by simply sorting the stack after adding / removing notes.

Post

nollock wrote:
Leslie Sanford wrote:Below is a voice handling algorithm for a monophonic synth.
I do it with a stack,
Same.. in monophonic mode, top-most element is always playing.

In polyphonic mode, on noteOn I further check what's the top-most element of the stack (and which voice is playing that note) so if I want glide, I can steal the current value of pitch on that voice to start the glide from.

Post

Do y'all handle the sustain pedal? I find that it complicates the algorithm quite a bit; it adds to the number of states you have to handle.

Post

Leslie Sanford wrote:Do y'all handle the sustain pedal? I find that it complicates the algorithm quite a bit; it adds to the number of states you have to handle.
Well.. you say it.. handle it as a state machine, and it's a lot easier than trying to come up with a decision tree. :)

Post

you're better off to use one of two "brute force" methods. the first uses a fixed array and is required for a full monophonic implementation on hardware like a dsp chip.

the second uses linked lists and is more practical on a large system like a daw.

i'll only post the first since you can use it in both cases. the only reason for the linked list implementation is code clarity when used in a context sensitive way. (in classes)

http://xhip.cjb.net/temp/public/notelogic.c

this is probably the most efficient you'll get. you could optionally pre-sort notes as they are added to get the different modes but that will take over-all a little more processing time than the brute force "get lowest" and "get highest" since you lose the efficiency of the "newest" and "oldest" methods which are static indices.

the handling of sustain goes in the external section calling to addnote(), removenote(), etc. there you would need another array of 128 bytes to store notes which had been "sustained" and remove all notes held there when sustain switches state to off.

it's a little interesting and i've implemented it in xhip, but it isnt really all that useful for a monophonic synth in most cases. (plus, when you have 2048 bytes 128 bytes are valuable!)

Post

mystran wrote:Well.. you say it.. handle it as a state machine, and it's a lot easier than trying to come up with a decision tree. :)
Heh, state machines are near and dear to my heart. :-)

The interesting thing about modeling a monophonic voice algorithm as a state machine is the use of guards; it would be helpful to put guards on the state transitions. For example, say we have a Triggered state. We reach this state after receiving a note-on event. Then, let's say, we receive a note-off event.

What happens next depends on whether there are any notes being held in addition to the note that's sounding, i.e. the player is holding down more than one key. If there are no other notes being held, we can (ignoring the sustain pedal) transition to a Released state; otherwise, we want to pop the stack of held notes and retrigger the voice the note that was on the top of the stack.

So we need a guard on the transition from the Triggered state to the Released state:

Code: Select all

     +-----------+    note-off         +----------+
 +-->| Triggered |-------------------->| Released |
 |   +-----------+ [notes.size() == 0] +----------+
 |           |
 | note-off  |
 +---------- +
[notes.size() > 0]
The guards are inside the square brackets, and the events are listed above them. When there are any notes on the note stack, a note-off event triggeres a self-transition in which the note on the top of the stack is retriggered.

The alternative to using guards would be to represent every possible state with, erm, a state. You'd have an explosions of states, so a judicious use of guards in this case helps simplify the state machine.

Post

mystran wrote:
nollock wrote:
Leslie Sanford wrote:Below is a voice handling algorithm for a monophonic synth.
I do it with a stack,
Same.. in monophonic mode, top-most element is always playing.

In polyphonic mode, on noteOn I further check what's the top-most element of the stack (and which voice is playing that note) so if I want glide, I can steal the current value of pitch on that voice to start the glide from.
Actualy yeah I do that aswell.

:D

Post

Leslie Sanford wrote:Do y'all handle the sustain pedal? I find that it complicates the algorithm quite a bit; it adds to the number of states you have to handle.
I havnt actuly implemented sustain pedal, so im not really sure how it's supposed to work tbh.

Post

aciddose wrote: this is probably the most efficient you'll get. you could optionally pre-sort notes as they are added to get the different modes but that will take over-all a little more processing time than the brute force "get lowest" and "get highest" since you lose the efficiency of the "newest" and "oldest" methods which are static indices.
Yeah it'll be more efficient to just have a getLowest and getHighest methods, rather than sorting the notes.

And i guess it might be useful to keep the notes in order played aswell, in case you need that for something else. Note stealing, or arpegiator ordering, or somesuch.

Post Reply

Return to “DSP and Plugin Development”