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:
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;