zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 0e33740a69f69f62b8bcf1054c448cd17b7ae23f
parent 7aab8e015ed301a8830eec7c05748b7e3b992621
Author: falkTX <falktx@gmail.com>
Date:   Wed, 20 Jan 2016 08:11:43 +0100

Merge branch 'master' of ssh://git.code.sf.net/p/zynaddsubfx/code into dpf-plugin

Diffstat:
Msrc/Containers/NotePool.cpp | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/Containers/NotePool.h | 7++++++-
Msrc/Misc/Part.cpp | 2+-
Msrc/Synth/ADnote.cpp | 7++++++-
Msrc/Synth/ADnote.h | 4+++-
Msrc/Synth/Envelope.cpp | 5+++++
Msrc/Synth/Envelope.h | 2++
Msrc/Synth/PADnote.cpp | 7++++++-
Msrc/Synth/PADnote.h | 4+++-
Msrc/Synth/SUBnote.cpp | 7++++++-
Msrc/Synth/SUBnote.h | 3++-
Msrc/Synth/SynthNote.h | 5++++-
Msrc/Tests/KitTest.h | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 192 insertions(+), 25 deletions(-)

diff --git a/src/Containers/NotePool.cpp b/src/Containers/NotePool.cpp @@ -65,6 +65,28 @@ NotePool::constActiveDescIter NotePool::activeDesc(void) const return constActiveDescIter{*this}; } +int NotePool::usedNoteDesc(void) const +{ + if(needs_cleaning) + const_cast<NotePool*>(this)->cleanup(); + + int cnt = 0; + for(int i=0; i<POLYPHONY; ++i) + cnt += (ndesc[i].size != 0); + return cnt; +} + +int NotePool::usedSynthDesc(void) const +{ + if(needs_cleaning) + const_cast<NotePool*>(this)->cleanup(); + + int cnt = 0; + for(int i=0; i<POLYPHONY*EXPECTED_USAGE; ++i) + cnt += (bool)sdesc[i].note; + return cnt; +} + void NotePool::insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato) { //Get first free note descriptor @@ -160,23 +182,38 @@ int NotePool::getRunningNotes(void) const return running_count; } -int NotePool::enforceKeyLimit(int limit) const +void NotePool::enforceKeyLimit(int limit) { - //{ - //int oldestnotepos = -1; - //if(notecount > keylimit) //find out the oldest note - // for(int i = 0; i < POLYPHONY; ++i) { - // int maxtime = 0; - // if(((partnote[i].status == KEY_PLAYING) || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) && (partnote[i].time > maxtime)) { - // maxtime = partnote[i].time; - // oldestnotepos = i; - // } - // } - //if(oldestnotepos != -1) - // ReleaseNotePos(oldestnotepos); - //} - //printf("Unimplemented enforceKeyLimit()\n"); - return -1; + int notes_to_kill = getRunningNotes() - limit; + if(notes_to_kill <= 0) + return; + + NoteDescriptor *to_kill = NULL; + unsigned oldest = 0; + for(auto &nd : activeDesc()) { + if(to_kill == NULL) { + //There must be something to kill + oldest = nd.age; + to_kill = &nd; + } else if(to_kill->status == Part::KEY_RELEASED && nd.status == Part::KEY_PLAYING) { + //Prefer to kill off a running note + oldest = nd.age; + to_kill = &nd; + } else if(nd.age > oldest && !(to_kill->status == Part::KEY_PLAYING && + nd.status == Part::KEY_RELEASED)) { + //Get an older note when it doesn't move from running to released + oldest = nd.age; + to_kill = &nd; + } + } + + if(to_kill) { + auto status = to_kill->status; + if(status == Part::KEY_RELEASED || status == Part::KEY_RELEASED_AND_SUSTAINED) + kill(*to_kill); + else + entomb(*to_kill); + } } void NotePool::releasePlayingNotes(void) @@ -225,6 +262,13 @@ void NotePool::kill(SynthDescriptor &s) needs_cleaning = true; } +void NotePool::entomb(NoteDescriptor &d) +{ + d.status = Part::KEY_RELEASED; + for(auto &s:activeNotes(d)) + s.note->entomb(); +} + const char *getStatus(int status_bits) { switch(status_bits) diff --git a/src/Containers/NotePool.h b/src/Containers/NotePool.h @@ -84,6 +84,10 @@ class NotePool activeDescIter activeDesc(void); constActiveDescIter activeDesc(void) const; + //Counts of descriptors used for tests + int usedNoteDesc(void) const; + int usedSynthDesc(void) const; + NotePool(void); //Operations @@ -99,7 +103,7 @@ class NotePool //Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING bool existsRunningNote(void) const; int getRunningNotes(void) const; - int enforceKeyLimit(int limit) const; + void enforceKeyLimit(int limit); void releasePlayingNotes(void); void releaseNote(note_t note); @@ -109,6 +113,7 @@ class NotePool void killNote(note_t note); void kill(NoteDescriptor &d); void kill(SynthDescriptor &s); + void entomb(NoteDescriptor &d); void cleanup(void); diff --git a/src/Misc/Part.cpp b/src/Misc/Part.cpp @@ -726,7 +726,7 @@ void Part::setkeylimit(unsigned char Pkeylimit_) if(keylimit == 0) keylimit = POLYPHONY - 5; - if(notePool.getRunningNotes() > keylimit) + if(notePool.getRunningNotes() >= keylimit) notePool.enforceKeyLimit(keylimit); } diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp @@ -1845,7 +1845,7 @@ void ADnote::releasekey() /* * Check if the note is finished */ -int ADnote::finished() const +bool ADnote::finished() const { if(NoteEnabled == ON) return 0; @@ -1853,6 +1853,11 @@ int ADnote::finished() const return 1; } +void ADnote::entomb(void) +{ + NoteGlobalPar.AmpEnvelope->forceFinish(); +} + void ADnote::Voice::releasekey() { if(!Enabled) diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h @@ -52,7 +52,9 @@ class ADnote:public SynthNote int noteout(float *outl, float *outr); void releasekey(); - int finished() const; + bool finished() const; + void entomb(void); + virtual SynthNote *cloneLegato(void) override; private: diff --git a/src/Synth/Envelope.cpp b/src/Synth/Envelope.cpp @@ -101,6 +101,11 @@ void Envelope::releasekey() t = 0.0f; } +void Envelope::forceFinish(void) +{ + envfinish = true; +} + /* * Envelope Output */ diff --git a/src/Synth/Envelope.h b/src/Synth/Envelope.h @@ -35,6 +35,8 @@ class Envelope /**Destructor*/ ~Envelope(); void releasekey(); + /**Push Envelope to finishing state*/ + void forceFinish(void); float envout(); float envout_dB(); /**Determines the status of the Envelope diff --git a/src/Synth/PADnote.cpp b/src/Synth/PADnote.cpp @@ -428,11 +428,16 @@ int PADnote::noteout(float *outl, float *outr) return 1; } -int PADnote::finished() const +bool PADnote::finished() const { return finished_; } +void PADnote::entomb(void) +{ + NoteGlobalPar.AmpEnvelope->forceFinish(); +} + void PADnote::releasekey() { NoteGlobalPar.FreqEnvelope->releasekey(); diff --git a/src/Synth/PADnote.h b/src/Synth/PADnote.h @@ -38,7 +38,9 @@ class PADnote:public SynthNote void legatonote(LegatoParams pars); int noteout(float *outl, float *outr); - int finished() const; + bool finished() const; + void entomb(void); + void releasekey(); private: void setup(float freq, float velocity, int portamento_, diff --git a/src/Synth/SUBnote.cpp b/src/Synth/SUBnote.cpp @@ -619,10 +619,15 @@ void SUBnote::releasekey() /* * Check if the note is finished */ -int SUBnote::finished() const +bool SUBnote::finished() const { if(NoteEnabled == OFF) return 1; else return 0; } + +void SUBnote::entomb(void) +{ + AmpEnvelope->forceFinish(); +} diff --git a/src/Synth/SUBnote.h b/src/Synth/SUBnote.h @@ -38,7 +38,8 @@ class SUBnote:public SynthNote int noteout(float *outl, float *outr); //note output,return 0 if the note is finished void releasekey(); - int finished() const; + bool finished() const; + void entomb(void); private: void setup(float freq, diff --git a/src/Synth/SynthNote.h b/src/Synth/SynthNote.h @@ -63,7 +63,10 @@ class SynthNote /**Return if note is finished. * @return finished=1 unfinished=0*/ - virtual int finished() const = 0; + virtual bool finished() const = 0; + + /**Make a note die off next buffer compute*/ + virtual void entomb(void) = 0; virtual void legatonote(LegatoParams pars) = 0; diff --git a/src/Tests/KitTest.h b/src/Tests/KitTest.h @@ -527,6 +527,94 @@ class KitTest:public CxxTest::TestSuite TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 0) } + void testKeyLimit(void) + { + auto &pool = part->notePool; + //Verify that without a key limit, several notes can be run + part->NoteOn(64, 127, 0); + part->NoteOn(65, 127, 0); + part->NoteOn(66, 127, 0); + part->NoteOn(67, 127, 0); + part->NoteOn(68, 127, 0); + + //Verify that notes are spawned as expected + TS_ASSERT_EQUALS(pool.usedNoteDesc(), 5); + TS_ASSERT_EQUALS(pool.usedSynthDesc(), 5); + + //Reset the part + part->monomemClear(); + pool.killAllNotes(); + + //Verify that notes are despawned + TS_ASSERT_EQUALS(pool.usedNoteDesc(), 0); + TS_ASSERT_EQUALS(pool.usedSynthDesc(), 0); + + //Enable keylimit + part->setkeylimit(3); + + //Replay notes + part->NoteOn(64, 127, 0); + part->NoteOn(65, 127, 0); + part->NoteOn(66, 127, 0); + part->NoteOn(67, 127, 0); + part->NoteOn(68, 127, 0); + + //Verify that notes are spawned as expected with limit + TS_ASSERT_EQUALS(pool.getRunningNotes(), 3);//2 entombed + TS_ASSERT_EQUALS(pool.usedNoteDesc(), 5); + TS_ASSERT_EQUALS(pool.usedSynthDesc(), 5); + + //Reset the part + part->monomemClear(); + pool.killAllNotes(); + + //Verify that notes are despawned + TS_ASSERT_EQUALS(pool.usedNoteDesc(), 0); + TS_ASSERT_EQUALS(pool.usedSynthDesc(), 0); + + //Now to test note stealing + + //Replay notes + part->NoteOn(64, 127, 0); + part->NoteOn(65, 127, 0); + part->NoteOn(66, 127, 0); + + //Verify that note pool is full + TS_ASSERT_EQUALS(pool.usedNoteDesc(), 3); + TS_ASSERT_EQUALS(pool.usedSynthDesc(), 3); + + //Age the notes + pool.ndesc[1].age = 50; + pool.ndesc[2].age = 500; + + printf("-------------------------------------\n"); + + //Inject two more notes which should steal the note + //descriptors for #66 and #65 + part->NoteOn(67, 127, 0); + pool.cleanup(); + TS_ASSERT_EQUALS(pool.ndesc[0].note, 64); + TS_ASSERT_EQUALS(pool.ndesc[1].note, 65); + TS_ASSERT_EQUALS(pool.ndesc[2].note, 66); + TS_ASSERT_EQUALS(pool.ndesc[2].status, Part::KEY_RELEASED); + TS_ASSERT_EQUALS(pool.ndesc[3].note, 67); + + part->NoteOn(68, 127, 0); + + //Verify that note pool is still full and entombed + TS_ASSERT_EQUALS(pool.usedNoteDesc(), 5); + TS_ASSERT_EQUALS(pool.usedSynthDesc(), 5); + + //Check that the result is {64, 68, 67} + TS_ASSERT_EQUALS(pool.ndesc[0].note, 64); + TS_ASSERT_EQUALS(pool.ndesc[1].note, 65); + TS_ASSERT_EQUALS(pool.ndesc[1].status, Part::KEY_RELEASED); + TS_ASSERT_EQUALS(pool.ndesc[2].note, 66); + TS_ASSERT_EQUALS(pool.ndesc[2].status, Part::KEY_RELEASED); + TS_ASSERT_EQUALS(pool.ndesc[3].note, 67); + TS_ASSERT_EQUALS(pool.ndesc[4].note, 68); + } + void tearDown() { delete part; delete[] outL;