zynaddsubfx

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

commit 934f9657083cc7fc67c3ffad0bb913389e542f0e
parent d8121bf92692e4eaa51104eb03b6bb38baa7fa20
Author: fundamental <mark.d.mccurry@gmail.com>
Date:   Fri,  3 Jul 2015 16:26:40 -0400

Rewrite Part's Note Pool Implementation

Makes Part::NoteOn massively more readable

- Currently ignores key limits
- Doesn't have too many notes case tested

Diffstat:
Msrc/CMakeLists.txt | 1+
Asrc/Containers/NotePool.cpp | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Containers/NotePool.h | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Misc/Part.cpp | 628++++++++++++++++++++++---------------------------------------------------------
Msrc/Misc/Part.h | 36++++++++++++++++++------------------
Msrc/Synth/ADnote.cpp | 7+++++++
Msrc/Synth/ADnote.h | 2++
Msrc/Synth/PADnote.cpp | 9++++++++-
Msrc/Synth/PADnote.h | 3++-
Msrc/Synth/SUBnote.cpp | 10+++++++++-
Msrc/Synth/SUBnote.h | 4+++-
Msrc/Synth/SynthNote.cpp | 6+++---
Msrc/Synth/SynthNote.h | 11++++++++---
Msrc/Tests/KitTest.h | 619++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
14 files changed, 1008 insertions(+), 724 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -347,6 +347,7 @@ add_subdirectory(Nio) add_library(zynaddsubfx_core STATIC ../tlsf/tlsf.c + Containers/NotePool.cpp ${zynaddsubfx_dsp_SRCS} ${zynaddsubfx_effect_SRCS} ${zynaddsubfx_misc_SRCS} diff --git a/src/Containers/NotePool.cpp b/src/Containers/NotePool.cpp @@ -0,0 +1,283 @@ +#include "NotePool.h" +//XXX eliminate dependence on Part.h +#include "../Misc/Part.h" +#include "../Misc/Allocator.h" +#include "../Synth/SynthNote.h" +#include <cstring> +#include <cassert> + +NotePool::NotePool(void) +{ + memset(ndesc, 0, sizeof(ndesc)); + memset(sdesc, 0, sizeof(ndesc)); +} +NotePool::activeNotesIter NotePool::activeNotes(NoteDescriptor &n) +{ + const int off_d1 = &n-ndesc; + int off_d2 = 0; + assert(off_d1 <= POLYPHONY); + for(int i=0; i<off_d1; ++i) + off_d2 += ndesc[i].size; + return NotePool::activeNotesIter{sdesc+off_d2,sdesc+off_d2+n.size}; +} + +bool NotePool::NoteDescriptor::operator==(NoteDescriptor nd) +{ + return age == nd.age && note == nd.note && sendto == nd.sendto && size == nd.size && status == nd.status; +} + +//return either the first unused descriptor or the last valid descriptor which +//matches note/sendto +static int getMergeableDescriptor(uint8_t note, uint8_t sendto, bool legato, + NotePool::NoteDescriptor *ndesc) +{ + int desc_id = 0; + for(int i=0; i<POLYPHONY; ++i, ++desc_id) + if(ndesc[desc_id].status == Part::KEY_OFF) + break; + + //Out of free descriptors + if(ndesc[desc_id].status != Part::KEY_OFF) + return -1; + + if(desc_id != 0) { + auto &nd = ndesc[desc_id-1]; + if(nd.age == 0 && nd.note == note && nd.sendto == sendto + && nd.status == Part::KEY_PLAYING && nd.legatoMirror == legato) + return desc_id-1; + } + return desc_id; +} + +NotePool::activeDescIter NotePool::activeDesc(void) +{ + cleanup(); + return activeDescIter{*this}; +} + +NotePool::constActiveDescIter NotePool::activeDesc(void) const +{ + const_cast<NotePool*>(this)->cleanup(); + return constActiveDescIter{*this}; +} + +void NotePool::insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato) +{ + //Get first free note descriptor + int desc_id = getMergeableDescriptor(note, sendto, legato, ndesc); + assert(desc_id != -1); + + ndesc[desc_id].note = note; + ndesc[desc_id].sendto = sendto; + ndesc[desc_id].size += 1; + ndesc[desc_id].status = Part::KEY_PLAYING; + ndesc[desc_id].legatoMirror = legato; + + //Get first free synth descriptor + int sdesc_id = 0; + while(sdesc[sdesc_id].note) + sdesc_id++; + sdesc[sdesc_id] = desc; +}; + +void NotePool::upgradeToLegato(void) +{ + for(auto &d:activeDesc()) + if(d.status == Part::KEY_PLAYING) + for(auto &s:activeNotes(d)) + insertLegatoNote(d.note, d.sendto, s); +} + +void NotePool::insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc) +{ + assert(desc.note); + desc.note = desc.note->cloneLegato(); + insertNote(note, sendto, desc, true); +}; + +//There should only be one pair of notes which are still playing +void NotePool::applyLegato(LegatoParams &par) +{ + for(auto &desc:activeDesc()) { + desc.note = par.midinote; + for(auto &synth:activeNotes(desc)) + synth.note->legatonote(par); + } +}; + +//Note that isn't KEY_PLAYING or KEY_RELASED_AND_SUSTAINING +bool NotePool::existsRunningNote(void) const +{ + printf("runing note # =%d\n", getRunningNotes()); + return getRunningNotes(); +} + +int NotePool::getRunningNotes(void) const +{ + bool running[256] = {0}; + for(auto &desc:activeDesc()) { + printf("note!(%d)\n", desc.note); + running[desc.note] = true; + } + + int running_count = 0; + for(int i=0; i<256; ++i) + running_count += running[i]; + + return running_count; +} +int NotePool::enforceKeyLimit(int limit) const +{ + //{ + //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; +} + +void NotePool::releasePlayingNotes(void) +{ + for(auto &d:activeDesc()) { + if(d.status == Part::KEY_PLAYING) { + d.status = Part::KEY_RELEASED; + for(auto s:activeNotes(d)) + s.note->releasekey(); + } + } +} + +void NotePool::release(NoteDescriptor &d) +{ + d.status = Part::KEY_RELEASED; + for(auto s:activeNotes(d)) + s.note->releasekey(); +} + +void NotePool::killAllNotes(void) +{ + for(auto &d:activeDesc()) + kill(d); +} + +void NotePool::killNote(uint8_t note) +{ + for(auto &d:activeDesc()) { + if(d.note == note) + kill(d); + } +} + +void NotePool::kill(NoteDescriptor &d) +{ + d.status = Part::KEY_OFF; + for(auto &s:activeNotes(d)) + kill(s); +} + +void NotePool::kill(SynthDescriptor &s) +{ + printf("Kill synth...\n"); + s.note->memory.dealloc(s.note); + needs_cleaning = true; +} + +const char *getStatus(int status_bits) +{ + switch(status_bits) + { + case 0: return "OFF "; + case 1: return "PLAY"; + case 2: return "SUST"; + case 3: return "RELA"; + default: return "INVD"; + } +} + +void NotePool::cleanup(void) +{ + if(!needs_cleaning) + return; + needs_cleaning = false; + int new_length[POLYPHONY] = {0}; + int cur_length[POLYPHONY] = {0}; + printf("Cleanup Start\n"); + dump(); + + //Identify the current length of all segments + //and the lengths discarding invalid entries + + int last_valid_desc = 0; + for(int i=0; i<POLYPHONY; ++i) + if(ndesc[i].status != Part::KEY_OFF) + last_valid_desc = i; + + //Find the real numbers of allocated notes + { + int cum_old = 0; + + for(int i=0; i<=last_valid_desc; ++i) { + cur_length[i] = ndesc[i].size; + for(int j=0; j<ndesc[i].size; ++j) + new_length[i] += (bool)sdesc[cum_old++].note; + } + } + + + //Move the note descriptors + { + int cum_new = 0; + for(int i=0; i<=last_valid_desc; ++i) { + ndesc[i].size = new_length[i]; + if(new_length[i] != 0) + ndesc[cum_new++] = ndesc[i]; + else + ndesc[i].status = Part::KEY_OFF; + } + memset(ndesc+cum_new, 0, sizeof(*ndesc)*(POLYPHONY-cum_new)); + } + + //Move the synth descriptors + { + int total_notes=0; + for(int i=0; i<=last_valid_desc; ++i) + total_notes+=cur_length[i]; + + int cum_new = 0; + for(int i=0; i<total_notes; ++i) + if(sdesc[i].note) + sdesc[cum_new++] = sdesc[i]; + memset(sdesc+cum_new, 0, sizeof(*sdesc)*(POLYPHONY*EXPECTED_USAGE-cum_new)); + } + printf("Cleanup Done\n"); + dump(); +} + +void NotePool::dump(void) +{ + printf("NotePool::dump<\n"); + const char *format = + " Note %d:%d age(%d) note(%d) sendto(%d) status(%s) legato(%d) type(%d) kit(%d) ptr(%p)\n"; + int note_id=0; + int descriptor_id=0; + for(auto &d:activeDesc()) { + descriptor_id += 1; + for(auto &s:activeNotes(d)) { + note_id += 1; + printf(format, + note_id, descriptor_id, + d.age, d.note, d.sendto, + getStatus(d.status), d.legatoMirror, s.type, s.kit, s.note); + } + } + printf(">NotePool::dump\n"); +} diff --git a/src/Containers/NotePool.h b/src/Containers/NotePool.h @@ -0,0 +1,113 @@ +#pragma once +#include <stdint.h> +#include <functional> +#include "../globals.h" + +//Expected upper bound of synths given that max polyphony is hit +#define EXPECTED_USAGE 3 + +class LegatoParams; +class NotePool +{ + public: + typedef uint8_t note_t; + //Currently this wastes a ton of bits due ot the legatoMirror flag + struct NoteDescriptor { + //acceptable overlap after 2 minutes + //run time at 48kHz 8 samples per buffer + //19 bit minimum + uint32_t age; + uint8_t note; + uint8_t sendto; + //max of 16 kit elms and 3 kit items per + uint8_t size; + uint8_t status; + bool legatoMirror; + bool operator==(NoteDescriptor); + }; + + //To be pedantic this wastes 2 or 6 bytes per descriptor + //depending on 32bit/64bit alignment rules + struct SynthDescriptor { + SynthNote *note; + uint8_t type; + uint8_t kit; + }; + + + //Pool of notes + NoteDescriptor ndesc[POLYPHONY]; + SynthDescriptor sdesc[POLYPHONY*EXPECTED_USAGE]; + bool needs_cleaning; + + + //Iterators + struct activeNotesIter { + SynthDescriptor *begin() {return _b;}; + SynthDescriptor *end() {return _e;}; + SynthDescriptor *_b; + SynthDescriptor *_e; + }; + + struct activeDescIter { + activeDescIter(NotePool &_np):np(_np) + { + int off=0; + for(int i=0; i<POLYPHONY; ++i, ++off) + if(np.ndesc[i].status == 0) + break; + _end = np.ndesc+off; + } + NoteDescriptor *begin() {return np.ndesc;}; + NoteDescriptor *end() { return _end; }; + NoteDescriptor *_end; + NotePool &np; + }; + + struct constActiveDescIter { + constActiveDescIter(const NotePool &_np):np(_np) + { + int off=0; + for(int i=0; i<POLYPHONY; ++i, ++off) + if(np.ndesc[i].status == 0) + break; + _end = np.ndesc+off; + } + const NoteDescriptor *begin() const {return np.ndesc;}; + const NoteDescriptor *end() const { return _end; }; + const NoteDescriptor *_end; + const NotePool &np; + }; + + activeNotesIter activeNotes(NoteDescriptor &n); + + activeDescIter activeDesc(void); + constActiveDescIter activeDesc(void) const; + + NotePool(void); + + //Operations + void insertNote(uint8_t note, uint8_t sendto, SynthDescriptor desc, bool legato=false); + void insertLegatoNote(uint8_t note, uint8_t sendto, SynthDescriptor desc); + + void upgradeToLegato(void); + void applyLegato(LegatoParams &par); + + //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 releasePlayingNotes(void); + void releaseNote(note_t note); + void release(NoteDescriptor &d); + + void killAllNotes(void); + void killNote(note_t note); + void kill(NoteDescriptor &d); + void kill(SynthDescriptor &s); + + void cleanup(void); + + void dump(void); +}; diff --git a/src/Misc/Part.cpp b/src/Misc/Part.cpp @@ -186,13 +186,18 @@ const Ports &Part::Kit::ports = kitPorts; const Ports &Part::ports = partPorts; Part::Part(Allocator &alloc, const SYNTH_T &synth_, Microtonal *microtonal_, FFTwrapper *fft_) - :ctl(synth_), memory(alloc), synth(synth_) + : + Pdrummode(false), + Ppolymode(true), + Plegatomode(false), + partoutl(new float[synth_.buffersize]), + partoutr(new float[synth_.buffersize]), + ctl(synth_), + lastlegatomodevalid(false), + microtonal(microtonal_), fft(fft_), + memory(alloc), + synth(synth_) { - microtonal = microtonal_; - fft = fft_; - partoutl = new float [synth.buffersize]; - partoutr = new float [synth.buffersize]; - monomemClear(); for(int n = 0; n < NUM_KIT_ITEMS; ++n) { @@ -209,6 +214,7 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, Microtonal *microtonal_, FFT partefx[nefx] = new EffectMgr(memory, synth, 1); Pefxbypass[nefx] = false; } + assert(partefx[0]); for(int n = 0; n < NUM_PART_EFX + 1; ++n) { partfxinputl[n] = new float [synth.buffersize]; @@ -218,28 +224,15 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, Microtonal *microtonal_, FFT killallnotes = false; oldfreq = -1.0f; - - for(int i = 0; i < POLYPHONY; ++i) { - partnote[i].status = KEY_OFF; - partnote[i].note = -1; - partnote[i].itemsplaying = 0; - for(int j = 0; j < NUM_KIT_ITEMS; ++j) { - partnote[i].kititem[j].adnote = NULL; - partnote[i].kititem[j].subnote = NULL; - partnote[i].kititem[j].padnote = NULL; - } - partnote[i].time = 0; - } cleanup(); Pname = new char[PART_MAX_NAME_LEN]; oldvolumel = oldvolumer = 0.5f; lastnote = -1; - lastpos = 0; // lastpos will store previously used NoteOn(...)'s pos. - lastlegatomodevalid = false; // To store previous legatomodevalid value. defaults(); + assert(partefx[0]); } void Part::cloneTraits(Part &p) const @@ -326,8 +319,7 @@ void Part::defaultsinstrument() */ void Part::cleanup(bool final_) { - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); for(int i = 0; i < synth.buffersize; ++i) { partoutl[i] = final_ ? 0.0f : denormalkillbuf[i]; partoutr[i] = final_ ? 0.0f : denormalkillbuf[i]; @@ -380,297 +372,92 @@ void Part::NoteOn(unsigned char note, unsigned char velocity, int masterkeyshift) { - // Legato and MonoMem used vars: - int posb = POLYPHONY - 1; // Just a dummy initial value. - bool legatomodevalid = false; //true when legato mode is determined applicable. - bool doinglegato = false; // true when we determined we do a legato note. - bool ismonofirstnote = false; /*(In Mono/Legato) true when we determined - no other notes are held down or sustained.*/ - int lastnotecopy = lastnote; //Useful after lastnote has been changed. + //Verify Basic Mode and sanity + const bool isRunningNote = notePool.existsRunningNote(); + const bool doingLegato = isRunningNote && isLegatoMode() && + lastlegatomodevalid; if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey)) return; + verifyKeyMode(); assert_kit_sanity(kit); - // MonoMem stuff: - if(!Ppolymode) { // If Poly is off - monomemPush(note); // Add note to the list. - monomem[note].velocity = velocity; // Store this note's velocity. - monomem[note].mkeyshift = masterkeyshift; /* Store masterkeyshift too*/ - if((partnote[lastpos].status != KEY_PLAYING) - && (partnote[lastpos].status != KEY_RELEASED_AND_SUSTAINED)) - ismonofirstnote = true; // No other keys are held or sustained. + //Preserve Note Stack + if(isMonoMode() || isLegatoMode()) { + monomemPush(note); + monomem[note].velocity = velocity; + monomem[note].mkeyshift = masterkeyshift; + } else if(!monomemEmpty()) monomemClear(); - lastnote = note; - - int pos = -1; - for(int i = 0; i < POLYPHONY; ++i) - if(partnote[i].status == KEY_OFF) { - pos = i; - break; - } - - if(Plegatomode && !Pdrummode) { - if(Ppolymode) { - fprintf( - stderr, - "ZynAddSubFX WARNING: Poly and Legato modes are both On, that should not happen ! ... Disabling Legato mode ! - (Part.cpp::NoteOn(..))\n"); - Plegatomode = 0; - } - else { - // Legato mode is on and applicable. - legatomodevalid = true; - if(!ismonofirstnote && lastlegatomodevalid) { - // At least one other key is held or sustained, and the - // previous note was played while in valid legato mode. - doinglegato = true; // So we'll do a legato note. - pos = lastpos; // A legato note uses same pos as previous.. - posb = lastposb; // .. same goes for posb. - } - else { - // Legato mode is valid, but this is only a first note. - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].status == KEY_PLAYING) - || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) - ReleaseNotePos(i); - - // Set posb - posb = (pos + 1) % POLYPHONY; //We really want it (if the following fails) - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].status == KEY_OFF) && (pos != i)) { - posb = i; - break; - } - } - lastposb = posb; // Keep a trace of used posb - } - } - else // Legato mode is either off or non-applicable. - if(!Ppolymode) { //if the mode is 'mono' turn off all other notes - for(int i = 0; i < POLYPHONY; ++i) - if(partnote[i].status == KEY_PLAYING) - ReleaseNotePos(i); - ReleaseSustainedKeys(); - } - lastlegatomodevalid = legatomodevalid; - - if(pos == -1) - fprintf(stderr, - "%s", - "NOTES TOO MANY (> POLYPHONY) - (Part.cpp::NoteOn(..))\n"); - else { - //start the note - partnote[pos].status = KEY_PLAYING; - partnote[pos].note = note; - if(legatomodevalid) { - partnote[posb].status = KEY_PLAYING; - partnote[posb].note = note; - } - - //this computes the velocity sensing of the part - float vel = VelF(velocity / 127.0f, Pvelsns); - - //compute the velocity offset - vel = limit(vel + (Pveloffs - 64.0f) / 64.0f, 0.0f, 1.0f); - - //compute the keyshift - int partkeyshift = (int)Pkeyshift - 64; - int keyshift = masterkeyshift + partkeyshift; - - //initialise note frequency - float notebasefreq; - if(Pdrummode == 0) { - notebasefreq = microtonal->getnotefreq(note, keyshift); - if(notebasefreq < 0.0f) - return;//the key is no mapped - } - else - notebasefreq = 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); - - //Portamento - if(oldfreq < 1.0f) - oldfreq = notebasefreq;//this is only the first note is played - - // For Mono/Legato: Force Portamento Off on first - // notes. That means it is required that the previous note is - // still held down or sustained for the Portamento to activate - // (that's like Legato). - bool portamento = false; - if(Ppolymode || !ismonofirstnote) - // I added a third argument to the - // ctl.initportamento(...) function to be able - // to tell it if we're doing a legato note. - portamento = ctl.initportamento(oldfreq, notebasefreq, doinglegato); - - if(portamento) - ctl.portamento.noteusing = pos; - oldfreq = notebasefreq; - - lastpos = pos; // Keep a trace of used pos. - - if(doinglegato) { - // Do Legato note - if(Pkitmode == 0) { // "normal mode" legato note - - auto note1 = partnote[pos].kititem[0]; - auto note2 = partnote[posb].kititem[0]; - LegatoParams pars = {notebasefreq, vel, portamento, note, true}; - if(kit[0].Padenabled && note1.adnote && note2.adnote) { - note1.adnote->legatonote(pars); - note2.adnote->legatonote(pars); - } - - if(kit[0].Psubenabled && note1.subnote && note2.subnote) { - note1.subnote->legatonote(pars); - note2.subnote->legatonote(pars); - } - - if(kit[0].Ppadenabled && note1.padnote && note2.padnote) { - note1.padnote->legatonote(pars); - note2.padnote->legatonote(pars); - } - } - else { // "kit mode" legato note - int ci = 0; - for(int item = 0; item < NUM_KIT_ITEMS; ++item) { - - //Make sure the key is valid and not across multiple ranges - if(kit[item].Pmuted || !inRange(note, kit[item].Pminkey, kit[item].Pmaxkey) - || !inRange((unsigned char)lastnotecopy, kit[item].Pminkey, kit[item].Pmaxkey)) - continue; - - auto note1 = partnote[pos].kititem[ci]; - auto note2 = partnote[posb].kititem[ci]; - LegatoParams pars = {notebasefreq, vel, portamento, note, true}; - note1.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); - note2.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); - - if(kit[item].Padenabled && kit[item].adpars && note1.adnote && note2.adnote) { - note1.adnote->legatonote(pars); - note2.adnote->legatonote(pars); - } - if(kit[item].Psubenabled && kit[item].subpars && note1.subnote && note2.subnote) { - note1.subnote->legatonote(pars); - note2.subnote->legatonote(pars); - } - if(kit[item].Ppadenabled && kit[item].padpars && note1.padnote && note2.padnote) { - note1.padnote->legatonote(pars); - note2.padnote->legatonote(pars); - } - - if(kit[item].adpars || kit[item].subpars || kit[item].padpars) { - ci++; - if((kit[item].Padenabled || kit[item].Psubenabled || kit[item].Ppadenabled) && (Pkitmode == 2)) - break; - } - } - if(ci == 0) { - // No legato were performed at all, so pretend nothing happened: - monomemPop(monomemBack()); // Remove last note from the list. - lastnote = lastnotecopy; // Set lastnote back to previous value. - } - } - return; // Ok, Legato note done, return. - } - - partnote[pos].itemsplaying = 0; - if(legatomodevalid) - partnote[posb].itemsplaying = 0; - - if(Pkitmode == 0) { //init the notes for the "normal mode" - partnote[pos].kititem[0].sendtoparteffect = 0; - SynthParams pars{memory, ctl, synth, notebasefreq, vel, (bool) portamento, note, false}; - - if(kit[0].Padenabled) - partnote[pos].kititem[0].adnote = - memory.alloc<ADnote>(kit[0].adpars, pars); - if(kit[0].Psubenabled) - partnote[pos].kititem[0].subnote = - memory.alloc<SUBnote>(kit[0].subpars, pars); - if(kit[0].Ppadenabled) - partnote[pos].kititem[0].padnote = - memory.alloc<PADnote>(kit[0].padpars, pars); - - - if(kit[0].Padenabled || kit[0].Psubenabled || kit[0].Ppadenabled) - partnote[pos].itemsplaying++; - - // Spawn another note (but silent) if legatomodevalid==true - if(legatomodevalid) { - partnote[posb].kititem[0].sendtoparteffect = 0; - pars.quiet = true; - - if(kit[0].Padenabled) - partnote[posb].kititem[0].adnote = - memory.alloc<ADnote>(kit[0].adpars, pars); - if(kit[0].Psubenabled) - partnote[posb].kititem[0].subnote = - memory.alloc<SUBnote>(kit[0].subpars, pars); - if(kit[0].Ppadenabled) - partnote[posb].kititem[0].padnote = - memory.alloc<PADnote>(kit[0].padpars, pars); - - if(kit[0].Padenabled || kit[0].Psubenabled || kit[0].Ppadenabled) - partnote[posb].itemsplaying++; - } - } - else //init the notes for the "kit mode" - for(int item = 0; item < NUM_KIT_ITEMS; ++item) { - if(kit[item].Pmuted || !inRange(note, kit[item].Pminkey, kit[item].Pmaxkey)) - continue; + //Mono/Legato Release old notes + if(isMonoMode() || (isLegatoMode() && !doingLegato)) + notePool.releasePlayingNotes(); - int ci = partnote[pos].itemsplaying; //ci=current item - auto &note1 = partnote[pos].kititem[ci]; + lastlegatomodevalid = isLegatoMode(); - //if this parameter is 127 for "unprocessed" - note1.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); + //Compute Note Parameters + const float vel = getVelocity(velocity, Pvelsns, Pveloffs); + const int partkeyshift = (int)Pkeyshift - 64; + const int keyshift = masterkeyshift + partkeyshift; + const float notebasefreq = getBaseFreq(note, keyshift); - SynthParams pars{memory, ctl, synth, notebasefreq, vel, (bool) portamento, note, false}; - - if(kit[item].adpars && kit[item].Padenabled) - note1.adnote = - memory.alloc<ADnote>(kit[item].adpars, pars); - - if(kit[item].subpars && kit[item].Psubenabled) - note1.subnote = - memory.alloc<SUBnote>(kit[item].subpars, pars); - - if(kit[item].padpars && kit[item].Ppadenabled) - note1.padnote = - memory.alloc<PADnote>(kit[item].padpars, pars); - - // Spawn another note (but silent) if legatomodevalid==true - if(legatomodevalid) { - auto &note2 = partnote[posb].kititem[ci]; - note2.sendtoparteffect = limit((int)kit[item].Psendtoparteffect, 0, NUM_PART_EFX); + if(notebasefreq < 0) + return; - pars.quiet = true; - if(kit[item].adpars && kit[item].Padenabled) - note2.adnote = - memory.alloc<ADnote>(kit[item].adpars, pars); - if(kit[item].subpars && kit[item].Psubenabled) - note2.subnote = - memory.alloc<SUBnote>(kit[item].subpars, pars); - if(kit[item].padpars && kit[item].Ppadenabled) - note2.padnote = - memory.alloc<PADnote>(kit[item].padpars, pars); + //Portamento + lastnote = note; + if(oldfreq < 1.0f) + oldfreq = notebasefreq;//this is only the first note is played + + // For Mono/Legato: Force Portamento Off on first + // notes. That means it is required that the previous note is + // still held down or sustained for the Portamento to activate + // (that's like Legato). + bool portamento = false; + if(Ppolymode || isRunningNote) + portamento = ctl.initportamento(oldfreq, notebasefreq, doingLegato); + + oldfreq = notebasefreq; + + //Adjust Existing Notes + if(doingLegato) { + LegatoParams pars = {notebasefreq, vel, portamento, note, true}; + notePool.applyLegato(pars); + return; + } - if(kit[item].adpars || kit[item].subpars || kit[item].padpars) - partnote[posb].itemsplaying++; - } + //Create New Notes + for(uint8_t i = 0; i < NUM_KIT_ITEMS; ++i) { + auto &item = kit[i]; + if(Pkitmode != 0 && !item.validNote(note)) + continue; - if(kit[item].adpars || kit[item].subpars) { - partnote[pos].itemsplaying++; - if((kit[item].Padenabled || kit[item].Psubenabled || kit[item].Ppadenabled) && (Pkitmode == 2)) - break; - } - } + SynthParams pars{memory, ctl, synth, notebasefreq, vel, + portamento, note, false}; + const int sendto = Pkitmode ? item.sendto() : 0; + + if(item.Padenabled) + notePool.insertNote(note, sendto, + {memory.alloc<ADnote>(kit[0].adpars, pars), 0, i}); + if(item.Psubenabled) + notePool.insertNote(note, sendto, + {memory.alloc<SUBnote>(kit[0].subpars, pars), 1, i}); + if(item.Ppadenabled) + notePool.insertNote(note, sendto, + {memory.alloc<PADnote>(kit[0].padpars, pars), 2, i}); + + //Partial Kit Use + if(isNonKit() || (isSingleKit() && item.active())) + break; } - //this only release the keys if there is maximum number of keys allowed + if(isLegatoMode()) + notePool.upgradeToLegato(); + + //Enforce the key limit setkeylimit(Pkeylimit); } @@ -683,17 +470,18 @@ void Part::NoteOff(unsigned char note) //release the key if(!monomemEmpty()) monomemPop(note); - for(int i = POLYPHONY - 1; i >= 0; i--) //first note in, is first out if there are same note multiple times - if((partnote[i].status == KEY_PLAYING) && (partnote[i].note == note)) { - if(!ctl.sustain.sustain) { //the sustain pedal is not pushed - if(!Ppolymode && !monomemEmpty()) - MonoMemRenote();//Play most recent still active note - else - ReleaseNotePos(i); - } - else //the sustain pedal is pushed - partnote[i].status = KEY_RELEASED_AND_SUSTAINED; + for(auto &desc:notePool.activeDesc()) { + if(desc.note != note) + continue; + if(!ctl.sustain.sustain) { //the sustain pedal is not pushed + if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) + MonoMemRenote();//Play most recent still active note + else + notePool.release(desc); } + else //the sustain pedal is pushed + desc.status = KEY_RELEASED_AND_SUSTAINED; + } } void Part::PolyphonicAftertouch(unsigned char note, @@ -709,36 +497,12 @@ void Part::PolyphonicAftertouch(unsigned char note, if(!Ppolymode) // if Poly is off monomem[note].velocity = velocity; // Store this note's velocity. - - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].note == note) && (partnote[i].status == KEY_PLAYING)) { - /* update velocity */ - // compute the velocity offset - float vel = VelF(velocity / 127.0f, Pvelsns) + (Pveloffs - 64.0f) / 64.0f; - vel = limit(vel, 0.0f, 1.0f); - - if(!Pkitmode) { // "normal mode" - if(kit[0].Padenabled && partnote[i].kititem[0].adnote) - partnote[i].kititem[0].adnote->setVelocity(vel); - if(kit[0].Psubenabled && partnote[i].kititem[0].subnote) - partnote[i].kititem[0].subnote->setVelocity(vel); - if(kit[0].Ppadenabled && partnote[i].kititem[0].padnote) - partnote[i].kititem[0].padnote->setVelocity(vel); - } - else // "kit mode" - for(int item = 0; item < NUM_KIT_ITEMS; ++item) { - if(kit[item].Pmuted || !inRange(note, kit[item].Pminkey, kit[item].Pmaxkey)) - continue; - - if(kit[item].Padenabled && partnote[i].kititem[item].adnote) - partnote[i].kititem[item].adnote->setVelocity(vel); - if(kit[item].Psubenabled && partnote[i].kititem[item].subnote) - partnote[i].kititem[item].subnote->setVelocity(vel); - if(kit[item].Ppadenabled && partnote[i].kititem[item].padnote) - partnote[i].kititem[item].padnote->setVelocity(vel); - } - } - + const float vel = getVelocity(velocity, Pvelsns, Pveloffs); + for(auto &d:notePool.activeDesc()) { + if(d.note == note && d.status == KEY_PLAYING) + for(auto &s:notePool.activeNotes(d)) + s.note->setVelocity(vel); + } } /* @@ -839,13 +603,14 @@ void Part::SetController(unsigned int type, int par) void Part::ReleaseSustainedKeys() { // Let's call MonoMemRenote() on some conditions: - if(Ppolymode == 0 && !monomemEmpty()) + if((isMonoMode() || isLegatoMode()) && !monomemEmpty()) if(monomemBack() != lastnote) // Sustain controller manipulation would cause repeated same note respawn without this check. MonoMemRenote(); // To play most recent still held note. - for(int i = 0; i < POLYPHONY; ++i) - if(partnote[i].status == KEY_RELEASED_AND_SUSTAINED) - ReleaseNotePos(i); + for(auto &d:notePool.activeDesc()) + if(d.status == KEY_RELEASED_AND_SUSTAINED) + for(auto &s:notePool.activeNotes(d)) + s.note->releasekey(); } /* @@ -854,10 +619,10 @@ void Part::ReleaseSustainedKeys() void Part::ReleaseAllKeys() { - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].status != KEY_RELEASED) - && (partnote[i].status != KEY_OFF)) //thanks to Frank Neumann - ReleaseNotePos(i); + for(auto &d:notePool.activeDesc()) + if(d.status != KEY_RELEASED) + for(auto &s:notePool.activeNotes(d)) + s.note->releasekey(); } // Call NoteOn(...) with the most recent still held key as new note @@ -866,50 +631,36 @@ void Part::MonoMemRenote() { unsigned char mmrtempnote = monomemBack(); // Last list element. monomemPop(mmrtempnote); // We remove it, will be added again in NoteOn(...). - if(Pnoteon == 0) - ReleaseNotePos(lastpos); - else - NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, - monomem[mmrtempnote].mkeyshift); + NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, + monomem[mmrtempnote].mkeyshift); } -/* - * Release note at position - */ -void Part::ReleaseNotePos(int pos) +float Part::getBaseFreq(int note, int keyshift) const { - for(int j = 0; j < NUM_KIT_ITEMS; ++j) { - if(partnote[pos].kititem[j].adnote) - partnote[pos].kititem[j].adnote->releasekey(); + if(Pdrummode) + return 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); + else + return microtonal->getnotefreq(note, keyshift); +} - if(partnote[pos].kititem[j].subnote) - partnote[pos].kititem[j].subnote->releasekey(); +float Part::getVelocity(uint8_t velocity, uint8_t velocity_sense, + uint8_t velocity_offset) const +{ + //compute sense function + const float vel = VelF(velocity / 127.0f, velocity_sense); - if(partnote[pos].kititem[j].padnote) - partnote[pos].kititem[j].padnote->releasekey(); - } - partnote[pos].status = KEY_RELEASED; + //compute the velocity offset + return limit(vel + (velocity_offset - 64.0f) / 64.0f, 0.0f, 1.0f); } - -/* - * Kill note at position - */ -void Part::KillNotePos(int pos) +void Part::verifyKeyMode(void) { - partnote[pos].status = KEY_OFF; - partnote[pos].note = -1; - partnote[pos].time = 0; - partnote[pos].itemsplaying = 0; - - for(int j = 0; j < NUM_KIT_ITEMS; ++j) { - memory.dealloc(partnote[pos].kititem[j].adnote); - memory.dealloc(partnote[pos].kititem[j].subnote); - memory.dealloc(partnote[pos].kititem[j].padnote); - } - if(pos == ctl.portamento.noteusing) { - ctl.portamento.noteusing = -1; - ctl.portamento.used = 0; + if(Plegatomode && !Pdrummode && Ppolymode) { + fprintf(stderr, + "WARNING: Poly & Legato modes are On, that shouldn't happen\n" + "Disabling Legato mode...\n" + "(Part.cpp::NoteOn(..))\n"); + Plegatomode = 0; } } @@ -917,32 +668,15 @@ void Part::KillNotePos(int pos) /* * Set Part's key limit */ -void Part::setkeylimit(unsigned char Pkeylimit) +void Part::setkeylimit(unsigned char Pkeylimit_) { - this->Pkeylimit = Pkeylimit; + Pkeylimit = Pkeylimit_; int keylimit = Pkeylimit; if(keylimit == 0) keylimit = POLYPHONY - 5; - //release old keys if the number of notes>keylimit - if(Ppolymode != 0) { - int notecount = 0; - for(int i = 0; i < POLYPHONY; ++i) - if((partnote[i].status == KEY_PLAYING) || (partnote[i].status == KEY_RELEASED_AND_SUSTAINED)) - notecount++; - - 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); - } + if(notePool.getRunningNotes() > keylimit) + notePool.enforceKeyLimit(keylimit); } @@ -954,65 +688,35 @@ void Part::AllNotesOff() killallnotes = true; } -void Part::RunNote(unsigned int k) +/* + * Compute Part samples and store them in the partoutl[] and partoutr[] + */ +void Part::ComputePartSmps() { - unsigned noteplay = 0; - for(int item = 0; item < partnote[k].itemsplaying; ++item) { - int sendcurrenttofx = partnote[k].kititem[item].sendtoparteffect; - - for(unsigned type = 0; type < 3; ++type) { - //Select a note - SynthNote **note = NULL; - if(type == 0) - note = &partnote[k].kititem[item].adnote; - else if(type == 1) - note = &partnote[k].kititem[item].subnote; - else if(type == 2) - note = &partnote[k].kititem[item].padnote; - - //Process if it exists - if(!(*note)) - continue; - noteplay++; + assert(partefx[0]); + for(unsigned nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx) { + memset(partfxinputl[nefx], 0, synth.bufferbytes); + memset(partfxinputr[nefx], 0, synth.bufferbytes); + } + for(auto &d:notePool.activeDesc()) { + d.age++; + for(auto &s:notePool.activeNotes(d)) { float tmpoutr[synth.buffersize]; float tmpoutl[synth.buffersize]; - (*note)->noteout(&tmpoutl[0], &tmpoutr[0]); + auto &note = *s.note; + note.noteout(&tmpoutl[0], &tmpoutr[0]); - if((*note)->finished()) - memory.dealloc(*note); for(int i = 0; i < synth.buffersize; ++i) { //add the note to part(mix) - partfxinputl[sendcurrenttofx][i] += tmpoutl[i]; - partfxinputr[sendcurrenttofx][i] += tmpoutr[i]; + partfxinputl[d.sendto][i] += tmpoutl[i]; + partfxinputr[d.sendto][i] += tmpoutr[i]; } - } - } - - //Kill note if there is no synth on that note - if(!noteplay) - KillNotePos(k); -} -/* - * Compute Part samples and store them in the partoutl[] and partoutr[] - */ -void Part::ComputePartSmps() -{ - for(unsigned nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx) - for(int i = 0; i < synth.buffersize; ++i) { - partfxinputl[nefx][i] = 0.0f; - partfxinputr[nefx][i] = 0.0f; + if(note.finished()) + notePool.kill(s); } - - for(unsigned k = 0; k < POLYPHONY; ++k) { - if(partnote[k].status == KEY_OFF) - continue; - partnote[k].time++; - //get the sampledata of the note and kill it if it's finished - RunNote(k); } - //Apply part's effects and mix them for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) { if(!Pefxbypass[nefx]) { @@ -1040,8 +744,7 @@ void Part::ComputePartSmps() partoutl[i] *= tmp; partoutr[i] *= tmp; } - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); killallnotes = false; for(int nefx = 0; nefx < NUM_PART_EFX; ++nefx) partefx[nefx]->cleanup(); @@ -1087,9 +790,7 @@ void Part::setkititemstatus(unsigned kititem, bool Penabled_) delete kkit.padpars; kkit.Pname[0] = '\0'; - //Reset notes s.t. stale buffers will not get read - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); } else { //All parameters must be NULL in this case @@ -1247,8 +948,7 @@ void Part::kill_rt(void) { for(int i=0; i<NUM_PART_EFX; ++i) partefx[i]->kill(); - for(int k = 0; k < POLYPHONY; ++k) - KillNotePos(k); + notePool.killAllNotes(); } void Part::monomemPush(char note) @@ -1414,3 +1114,19 @@ void Part::getfromXML(XMLwrapper *xml) xml->exitbranch(); } } + +bool Part::Kit::active(void) const +{ + return Padenabled || Psubenabled || Ppadenabled; +} + +uint8_t Part::Kit::sendto(void) const +{ + return limit((int)Psendtoparteffect, 0, NUM_PART_EFX); + +} + +bool Part::Kit::validNote(char note) const +{ + return !Pmuted && inRange((uint8_t)note, Pminkey, Pmaxkey); +} diff --git a/src/Misc/Part.h b/src/Misc/Part.h @@ -27,6 +27,7 @@ #include "../globals.h" #include "../Params/Controller.h" +#include "../Containers/NotePool.h" #include <functional> @@ -94,6 +95,10 @@ class Part SUBnoteParameters *subpars; PADnoteParameters *padpars; + bool active(void) const; + uint8_t sendto(void) const; + bool validNote(char note) const; + const static rtosc::Ports &ports; } kit[NUM_KIT_ITEMS]; @@ -155,25 +160,22 @@ class Part const static rtosc::Ports &ports; private: - void RunNote(unsigned k); - void KillNotePos(int pos); - void ReleaseNotePos(int pos); void MonoMemRenote(); // MonoMem stuff. + float getBaseFreq(int note, int keyshift) const; + float getVelocity(uint8_t velocity, uint8_t velocity_sense, + uint8_t velocity_offset) const; + void verifyKeyMode(void); + bool isPolyMode(void) const {return Ppolymode;} + bool isMonoMode(void) const {return !Ppolymode && !Plegatomode;}; + bool isLegatoMode(void) const {return Plegatomode && !Pdrummode;} + bool isNonKit(void) const {return Pkitmode == 0;} + bool isMultiKit(void) const {return Pkitmode == 1;} + bool isSingleKit(void) const {return Pkitmode == 2;} - int killallnotes; //is set to 1 if I want to kill all notes - - struct PartNotes { - NoteStatus status; - int note; //if there is no note playing, the "note"=-1 - int itemsplaying; - struct { - SynthNote *adnote, *subnote, *padnote; - int sendtoparteffect; - } kititem[NUM_KIT_ITEMS]; - int time; - }; + bool killallnotes; + + NotePool notePool; - int lastpos, lastposb; // To keep track of previously used pos and posb. bool lastlegatomodevalid; // To keep track of previous legatomodevalid. // MonoMem stuff @@ -193,8 +195,6 @@ class Part store the velocity and masterkeyshift values of a given note (the list only store note values). For example 'monomem[note].velocity' would be the velocity value of the note 'note'.*/ - PartNotes partnote[POLYPHONY]; - float oldfreq; //this is used for portamento Microtonal *microtonal; FFTwrapper *fft; diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp @@ -397,6 +397,13 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars) initparameters(); } +SynthNote *ADnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, legato.param.freq, velocity, + (bool)portamento, legato.param.midinote, true}; + return memory.alloc<ADnote>(&pars, sp); +} + // ADlegatonote: This function is (mostly) a copy of ADnote(...) and // initparameters() stuck together with some lines removed so that it // only alter the already playing note (to perform legato). It is diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h @@ -53,6 +53,8 @@ class ADnote:public SynthNote int noteout(float *outl, float *outr); void releasekey(); int finished() const; + + virtual SynthNote *cloneLegato(void) override; private: /**Changes the frequency of an oscillator. diff --git a/src/Synth/PADnote.cpp b/src/Synth/PADnote.cpp @@ -28,7 +28,7 @@ #include "../Params/FilterParams.h" #include "../Misc/Util.h" -PADnote::PADnote(PADnoteParameters *parameters, +PADnote::PADnote(const PADnoteParameters *parameters, SynthParams pars) :SynthNote(pars), pars(*parameters) { @@ -166,6 +166,13 @@ void PADnote::setup(float freq, } } +SynthNote *PADnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, legato.param.freq, velocity, + (bool)portamento, legato.param.midinote, true}; + return memory.alloc<PADnote>(&pars, sp); +} + void PADnote::legatonote(LegatoParams pars) { // Manage legato stuff diff --git a/src/Synth/PADnote.h b/src/Synth/PADnote.h @@ -30,9 +30,10 @@ class PADnote:public SynthNote { public: - PADnote(PADnoteParameters *parameters, SynthParams pars); + PADnote(const PADnoteParameters *parameters, SynthParams pars); ~PADnote(); + SynthNote *cloneLegato(void); void legatonote(LegatoParams pars); int noteout(float *outl, float *outr); diff --git a/src/Synth/SUBnote.cpp b/src/Synth/SUBnote.cpp @@ -33,7 +33,7 @@ #include "../Misc/Util.h" #include "../Misc/Allocator.h" -SUBnote::SUBnote(SUBnoteParameters *parameters, SynthParams &spars) +SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars) :SynthNote(spars), pars(*parameters) { NoteEnabled = ON; @@ -46,6 +46,7 @@ void SUBnote::setup(float freq, int midinote, bool legato) { + this->velocity = velocity; portamento = portamento_; NoteEnabled = ON; volume = powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f)); //-60 dB .. 0 dB @@ -210,6 +211,13 @@ void SUBnote::setup(float freq, oldamplitude = newamplitude; } +SynthNote *SUBnote::cloneLegato(void) +{ + SynthParams sp{memory, ctl, synth, legato.param.freq, velocity, + (bool)portamento, legato.param.midinote, true}; + return memory.alloc<SUBnote>(&pars, sp); +} + void SUBnote::legatonote(LegatoParams pars) { // Manage legato stuff diff --git a/src/Synth/SUBnote.h b/src/Synth/SUBnote.h @@ -30,9 +30,10 @@ class SUBnote:public SynthNote { public: - SUBnote(SUBnoteParameters *parameters, SynthParams &pars); + SUBnote(const SUBnoteParameters *parameters, SynthParams &pars); ~SUBnote(); + SynthNote *cloneLegato(void); void legatonote(LegatoParams pars); int noteout(float *outl, float *outr); //note output,return 0 if the note is finished @@ -100,6 +101,7 @@ class SUBnote:public SynthNote int oldpitchwheel, oldbandwidth; float globalfiltercenterq; + float velocity; }; #endif diff --git a/src/Synth/SynthNote.cpp b/src/Synth/SynthNote.cpp @@ -3,9 +3,9 @@ #include <cstring> SynthNote::SynthNote(SynthParams &pars) - :legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, - pars.note, pars.quiet), - memory(pars.memory), ctl(pars.ctl), synth(pars.synth) + :memory(pars.memory), + legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, + pars.note, pars.quiet), ctl(pars.ctl), synth(pars.synth) {} SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port, diff --git a/src/Synth/SynthNote.h b/src/Synth/SynthNote.h @@ -28,7 +28,7 @@ class Controller; struct SynthParams { Allocator &memory; //Memory Allocator for the Note to use - Controller &ctl; + const Controller &ctl; const SYNTH_T &synth; float frequency; //Note base frequency float velocity; //Velocity of the Note @@ -65,8 +65,14 @@ class SynthNote virtual int finished() const = 0; virtual void legatonote(LegatoParams pars) = 0; + + virtual SynthNote *cloneLegato(void) = 0; + /* For polyphonic aftertouch needed */ void setVelocity(float velocity_); + + //Realtime Safe Memory Allocator For notes + class Allocator &memory; protected: // Legato transitions class Legato @@ -87,6 +93,7 @@ class SynthNote int length; float m, step; } fade; + public: struct { // Note parameters float freq, vel; bool portamento; @@ -103,8 +110,6 @@ class SynthNote void setDecounter(int decounter_) {decounter = decounter_; } } legato; - //Realtime Safe Memory Allocator For notes - class Allocator &memory; const Controller &ctl; const SYNTH_T &synth; }; diff --git a/src/Tests/KitTest.h b/src/Tests/KitTest.h @@ -52,22 +52,32 @@ class KitTest:public CxxTest::TestSuite part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[0].note, 64); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=64, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); } void testNoKitYesLegatoNoMono() { @@ -75,26 +85,44 @@ class KitTest:public CxxTest::TestSuite part->Plegatomode = true; part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[0].note, 65); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].adnote->legato.silent, true); - - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + if(part->notePool.sdesc[1].note) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, true); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 0) } void testNoKitNoLegatoYesMono() { @@ -102,26 +130,44 @@ class KitTest:public CxxTest::TestSuite part->Plegatomode = false; part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_RELEASED); - TS_ASSERT_EQUALS(part->partnote[0].note, 64); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=64, + .sendto=0, + .size=1, + .status=Part::KEY_RELEASED, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 0) } //Normal Kit @@ -136,34 +182,58 @@ class KitTest:public CxxTest::TestSuite part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[0].note, 64); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + part->notePool.dump(); + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=64, + .sendto=0, + .size=2, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=2, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 1) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[2].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[2].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[3].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[3].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[3].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[3].kit, 1) + + TS_ASSERT_EQUALS(part->notePool.sdesc[4].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[4].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[4].kit, 0) } void testYesKitYesLegatoNoMono() { @@ -177,38 +247,59 @@ class KitTest:public CxxTest::TestSuite part->Plegatomode = true; part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[0].note, 65); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].adnote->legato.silent, true); - - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + + part->notePool.dump(); + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=2, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=2, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 1) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[2].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].note->legato.silent, true); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[2].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[3].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[3].note->legato.silent, true); + TS_ASSERT_EQUALS(part->notePool.sdesc[3].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[3].kit, 1) + + TS_ASSERT_EQUALS(part->notePool.sdesc[4].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[4].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[4].kit, 0) } void testYesKitNoLegatoYesMono() { @@ -222,38 +313,59 @@ class KitTest:public CxxTest::TestSuite part->Plegatomode = false; part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_RELEASED); - TS_ASSERT_EQUALS(part->partnote[0].note, 64); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + + part->notePool.dump(); + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=64, + .sendto=0, + .size=2, + .status=Part::KEY_RELEASED, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=2, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 1) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[2].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[2].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[3].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[3].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[3].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[3].kit, 1) + + TS_ASSERT_EQUALS(part->notePool.sdesc[4].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[4].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[4].kit, 0) } //Single Kit @@ -267,34 +379,48 @@ class KitTest:public CxxTest::TestSuite part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[0].note, 64); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + part->notePool.dump(); + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=64, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 0) + + TS_ASSERT_EQUALS(part->notePool.sdesc[2].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[2].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[2].kit, 0) } void testSingleKitYesLegatoNoMono() { @@ -308,38 +434,44 @@ class KitTest:public CxxTest::TestSuite part->Plegatomode = true; part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[0].note, 65); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].adnote->legato.silent, true); - - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + if(part->notePool.sdesc[1].note) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, true); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 0) } void testSingleKitNoLegatoYesMono() { @@ -353,38 +485,45 @@ class KitTest:public CxxTest::TestSuite part->Plegatomode = false; part->NoteOn(64, 127, 0); part->NoteOn(65, 127, 0); - TS_ASSERT_EQUALS(part->partnote[0].status, Part::KEY_RELEASED); - TS_ASSERT_EQUALS(part->partnote[0].note, 64); - TS_ASSERT_EQUALS(part->partnote[0].time, 0); - TS_ASSERT_DIFFERS(part->partnote[0].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[0].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[1].status, Part::KEY_PLAYING); - TS_ASSERT_EQUALS(part->partnote[1].note, 65); - TS_ASSERT_EQUALS(part->partnote[1].time, 0); - TS_ASSERT_DIFFERS(part->partnote[1].kititem[0].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[1].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].adnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[2].subnote, nullptr); - TS_ASSERT_EQUALS(part->partnote[1].kititem[0].adnote->legato.silent, false); - - - TS_ASSERT_EQUALS(part->partnote[2].status, Part::KEY_OFF); - TS_ASSERT_EQUALS(part->partnote[2].note, -1); + + + + TS_ASSERT_EQUALS(part->notePool.ndesc[0], + (NotePool::NoteDescriptor{ + .age=0, + .note=64, + .sendto=0, + .size=1, + .status=Part::KEY_RELEASED, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[1], + (NotePool::NoteDescriptor{ + .age=0, + .note=65, + .sendto=0, + .size=1, + .status=Part::KEY_PLAYING, + .legatoMirror=false})); + + TS_ASSERT_EQUALS(part->notePool.ndesc[2], + (NotePool::NoteDescriptor{ + .age=0, + .note=0, + .sendto=0, + .size=0, + .status=0, + .legatoMirror=false})); + + TS_ASSERT_DIFFERS(part->notePool.sdesc[0].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[0].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[0].kit, 0) + + TS_ASSERT_DIFFERS(part->notePool.sdesc[1].note, nullptr); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].note->legato.silent, false); + TS_ASSERT_EQUALS(part->notePool.sdesc[1].type, 0) + TS_ASSERT_EQUALS(part->notePool.sdesc[1].kit, 0) } void tearDown() {