Login / Register 0 items | $0.00 New @ KVR
blümchen
Banned

Postby blümchen; Sun Jun 19, 2005 8:16 am

That's all fine.

But has obviously nothing to do with a smart implementation of voice limitation and intelligent voice stealing algorithmic.

Because the performance is actually limited never the less with our computers (even with the last released top products - supporting mega feature sets and unisono ... ).
lalo
KVRian
 
1421 posts since 14 Oct, 2002

Postby lalo; Thu Apr 12, 2018 4:25 am Re: Voice Stealing

Bringin' up a very old one :-)

Is there any code example for poly voice stealing and mono/legato note priority (last/low/high)?
I guess is all a matter of keeping track of voice tags (or just note number), note ons and offs and then update pitch and trigger or not trigger envelopes.
thanks!
a.
User avatar
Richard_Synapse
KVRian
 
845 posts since 19 Dec, 2010

Postby Richard_Synapse; Thu Apr 12, 2018 5:03 am Re: Voice Stealing

I'm not aware of any code examples, but the simplest algorithm for poly stealing I know is to cycle through your voices in a round-robin fashion, while ignoring voices assigned to keys that are currently depressed. Some vintage hardware works in this fashion, very basic but effective.

That said it depends on what synth you're trying to make, so it is hard to give a generic answer here.

Richard
Synapse Audio Software - www.synapse-audio.com
lalo
KVRian
 
1421 posts since 14 Oct, 2002

Postby lalo; Thu Apr 12, 2018 5:29 am Re: Voice Stealing

Richard_Synapse wrote:I'm not aware of any code examples, but the simplest algorithm for poly stealing I know is to cycle through your voices in a round-robin fashion, while ignoring voices assigned to keys that are currently depressed. Some vintage hardware works in this fashion, very basic but effective.

That said it depends on what synth you're trying to make, so it is hard to give a generic answer here.

Richard


Thanks Richard, what seems more difficult to me is implement a mono/legato algorithm with note priority option. Ideally i would spare some time finding some examples.
User avatar
Richard_Synapse
KVRian
 
845 posts since 19 Dec, 2010

Postby Richard_Synapse; Thu Apr 12, 2018 5:52 am Re: Voice Stealing

Ok, here's how Low Note Priority can be implemented, as an example.

Simply keep track of all depressed keys in some array (you could use a list too, of course).

When pressing a key, low note priority will ignore any key higher than the current one. If it is lower, then set the pitch to the new, lower key.

When releasing a key, go through your array above. If it is empty, release the note normally. If it is not empty, take the lowest key and simply change the pitch accordingly.

Richard
Synapse Audio Software - www.synapse-audio.com
User avatar
Vokbuz
KVRer
 
20 posts since 24 Aug, 2014

Postby Vokbuz; Mon Jun 25, 2018 5:36 am Re: Voice Stealing

Maybe this will help someone. I created some code example of voice management and stealing. It is in C++ but can be implemented in C also.

Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/*------------------------------------------------------------------------------
  General purpose DoubleLinked classes.
------------------------------------------------------------------------------*/

class DoubleLinked {
public:
  DoubleLinked() {
    reset();
  }

  DoubleLinked(DoubleLinked& item) {
    link_before(item);
  }

  bool is_linked() const {
    return _next != this;
  }

  DoubleLinked* get_next() const {
    return _next;
  }

  DoubleLinked* get_prev() const {
    return _prev;
  }

  void link_before(DoubleLinked& other) {
    _next = &other;
    _prev = other._prev;
    other._prev->_next = this;
    other._prev = this;
  }

  void link_after(DoubleLinked& other) {
    _prev = &other;
    _next = other._next;
    other._next->_prev = this;
    other._next = this;
  }

  void unlink() {
    _prev->_next = _next;
    _next->_prev = _prev;
  }

  void reset() {
    _next = this;
    _prev = this;
  }

protected:
  DoubleLinked *_next, *_prev;
};

template<typename T>
class DoubleLinkedList: public DoubleLinked {
public:
  DoubleLinkedList() {
  }

  T* get_first() const {
    return static_cast<T*>(_next);
  }

  T* get_last() const {
    return static_cast<T*>(_prev);
  }
};

#define ENUM_DOUBLE_LINKED(list, type, name, code) \
  for (DoubleLinked* _it = (list).get_first(); _it != (DoubleLinked*)&(list);) { \
    DoubleLinked* _next = _it->get_next(); \
    type* name = static_cast<Voice*>(_it); \
    { code; } \
    _it = _next; \
  }

/*------------------------------------------------------------------------------
  Voices.
------------------------------------------------------------------------------*/

struct Voice: public DoubleLinked {
  int index, pitch, counter;

  void start(int pitch, int counter) {
    this->pitch = pitch;
    this->counter = counter;
  }

  bool process() {
    printf("  * Process: %i [pitch=%i]\n", index, pitch);
    return counter-- > 1;
  }
};

enum class VoicePriority {
  First, Last, Low, High
};

class Voices {
public:
  Voices(VoicePriority priority)
    : priority(priority)
  {
    for (int i = 0; i < 4; ++i) {
      all_voices[i].index = i;
      all_voices[i].link_before(inactive_voices);
    }
  }

  Voice* activate_voice() {
    Voice* voice;
    if (inactive_voices.is_linked()) {
      voice = inactive_voices.get_first();
      printf(" ==== Pick inactive: %i\n", voice->index);
    }
    else {
      // should not ever fail
      assert(active_voices.is_linked());

      switch (priority) {
        case VoicePriority::First: voice = active_voices.get_first(); break;
        case VoicePriority::Last: voice = active_voices.get_last(); break;
        case VoicePriority::Low: voice = get_lowest_active_voice(); break;
        case VoicePriority::High: voice = get_highest_active_voice(); break;
      }

      printf(" ==== Steal active: %i [pitch=%i]\n", voice->index, voice->pitch);
    }

    voice->unlink();
    voice->link_after(active_voices);

    return voice;
  }

  void deactivate_voice(Voice* voice) {
    printf(" ==== Deactivate: %i\n", voice->index);

    voice->unlink();
    voice->link_after(inactive_voices);
  }

  void process() {
    ENUM_DOUBLE_LINKED(active_voices, Voice, voice, {
      if (!voice->process()) {
        deactivate_voice(voice);
      }
    })
  }

protected:
  Voice* get_lowest_active_voice() const {
    Voice* selcted_voice = nullptr;
    ENUM_DOUBLE_LINKED(active_voices, Voice, voice, {
      if (!selcted_voice || voice->pitch < selcted_voice->pitch) {
        selcted_voice = voice;
      }
    })
    return selcted_voice;
  }

  Voice* get_highest_active_voice() const {
    Voice* selcted_voice = nullptr;
    ENUM_DOUBLE_LINKED(active_voices, Voice, voice, {
      if (!selcted_voice || voice->pitch > selcted_voice->pitch) {
        selcted_voice = voice;
      }
    })
    return selcted_voice;
  }

  VoicePriority priority;
  Voice all_voices[4];
  DoubleLinkedList<Voice> active_voices, inactive_voices;
};

/*------------------------------------------------------------------------------
  Demo.
------------------------------------------------------------------------------*/

// clang -lstdc++ -std=c++11 voices.cc -o voices.bin && ./voices.bin

int main() {
  Voices voices(VoicePriority::Last);
  // start voices with kind of random pitch and playing length
  for (int i = 10; i < 100; ++i) {
    Voice* voice = voices.activate_voice();
    voice->start((11 * i) % 13, 1 + (i % 8));
    voices.process();
  }
}
davidguda
KVRist
 
480 posts since 28 Feb, 2011, from Sweden

Postby davidguda; Wed Jun 27, 2018 2:42 am Re: Voice Stealing

If you look at JUCE it already has a default implementation of voice stealing that is rather smart with not stealing highest or lowest note and first voices that are decaying etc.
David Guda gudaaudio.com
User avatar
antto
KVRAF
 
2499 posts since 4 Sep, 2006, from 127.0.0.1

Postby antto; Sun Jul 01, 2018 12:03 am Re: Voice Stealing

maybe not a great example, but there's some pseudo-code for a monophonic voice allocator with "newest" note priority:
http://antonsavov.net/cms/projects/303a ... lassic-mva
just ignore the "accent" bits from it
It doesn't matter how it sounds..
..as long as it has BASS and it's LOUD!

irc.freenode.net >>> #kvr
mystran
KVRAF
 
4979 posts since 11 Feb, 2006, from Helsinki, Finland

Postby mystran; Sun Jul 01, 2018 9:25 am Re: Voice Stealing

antto wrote:maybe not a great example, but there's some pseudo-code for a monophonic voice allocator with "newest" note priority:
http://antonsavov.net/cms/projects/303a ... lassic-mva
just ignore the "accent" bits from it


The way I've normally done it is just push all the new notes in a stack (or rotate already active notes to the top on duplicate note-on; without auxiliary bitmap you still have to scan sadly) and collapse the stack down on noteOff. I suppose it's basically the same thing you're doing, except I'd only shift on noteOn if it's a duplicate, as I'd normally keep the last note at the stack top at the end of the array (well, at the current "nItems" position).

Anyway... something I also do is maintain a single numeric "gate level" (for each voice, in both monophonic and polyphonic code) that has a number of different values (which can be compared with "greater or equal"):

- inactive (optional): voice is not active, no need to process at all
- low: traditional "analog gate" is low, meaning envelopes should release
- pedal (optional): key is released, but hold-pedal is keeping the "gate" active
- high: key is still held
- trigger: a noteOn was received since last process(), with gate already previously active
- trigger-low: a noteOn was received since last process(), with gate previously low

The invariants are that noteOn always sets either "trigger" or "trigger-low" (depending on the previous gate level) and process() always resets them back down to "high" (or "pedal") after rendering the voice. This can then be used for polyphonic voice-allocation heuristics (eg. always prefer to steal a lower gate), but also for modules like envelopes and LFOs to make a local decision of whether or not to retrigger (eg. if you want gate triggering, you check for "trigger-low" and if you want retriggers, you check for "greater or equal to trigger" instead).

For poly alloc, I've settle on a scheme where you keep the maximum number of voices in an array and a counter like "next voice" that is used to choose the initial value for "current candidate" for a noteOn. Then scan through the array, comparing each voice to the "current candidate" and if voice appears better candidate for stealing (according to a bunch of heuristics) then set that voice as the "current candidate" and keep going until all voices are checked or the current candidate is "inactive" (in which case it's fine to early out as no other voice can possibly ever be better).

After all voices have been checked, the "current candidate" becomes the voice we pick and "next voice" index is set to point to the following it in order. This way if we hit the unlikely situation where all voices appear equally good for stealing, things will degenerate into simple round-robin.
Image <- plugins | forum
Previous

Moderator: KVR Moderators (Main)

Return to DSP and Plug-in Development