zynaddsubfx

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

commit 59315edbe05268fdf38279eb8470f2f1a1ab0d29
parent d08bbf38af2da1284a7dc97dbfc058b5949533f6
Author: Hans Petter Selasky <hps@selasky.org>
Date:   Mon,  4 Feb 2019 10:13:03 +0100

Add basic support for floating point notes.

Signed-off-by: Hans Petter Selasky <hps@selasky.org>

Diffstat:
Msrc/Containers/NotePool.cpp | 4++--
Msrc/Containers/NotePool.h | 2+-
Msrc/Misc/Master.cpp | 4++--
Msrc/Misc/Master.h | 5++++-
Msrc/Misc/Microtonal.cpp | 9+++++----
Msrc/Misc/Microtonal.h | 2+-
Msrc/Misc/Part.cpp | 28++++++++++++++++------------
Msrc/Misc/Part.h | 9+++++++--
Msrc/Synth/ADnote.cpp | 10++++------
Msrc/Synth/ADnote.h | 2+-
Msrc/Synth/PADnote.cpp | 11+++++------
Msrc/Synth/PADnote.h | 2+-
Msrc/Synth/SUBnote.cpp | 10+++++-----
Msrc/Synth/SUBnote.h | 2+-
Msrc/Synth/SynthNote.cpp | 14+++++++-------
Msrc/Synth/SynthNote.h | 10+++++-----
Msrc/Tests/AdNoteTest.h | 2+-
Msrc/Tests/MemoryStressTest.h | 2+-
Msrc/Tests/MicrotonalTest.h | 2+-
Msrc/Tests/PadNoteTest.h | 2+-
Msrc/Tests/SubNoteTest.h | 2+-
Msrc/Tests/UnisonTest.h | 2+-
22 files changed, 73 insertions(+), 63 deletions(-)

diff --git a/src/Containers/NotePool.cpp b/src/Containers/NotePool.cpp @@ -193,10 +193,10 @@ void NotePool::insertLegatoNote(note_t note, uint8_t sendto, SynthDescriptor des }; //There should only be one pair of notes which are still playing -void NotePool::applyLegato(LegatoParams &par) +void NotePool::applyLegato(note_t note, LegatoParams &par) { for(auto &desc:activeDesc()) { - desc.note = par.midinote; + desc.note = note; for(auto &synth:activeNotes(desc)) try { synth.note->legatonote(par); diff --git a/src/Containers/NotePool.h b/src/Containers/NotePool.h @@ -122,7 +122,7 @@ class NotePool void insertLegatoNote(note_t note, uint8_t sendto, SynthDescriptor desc); void upgradeToLegato(void); - void applyLegato(LegatoParams &par); + void applyLegato(note_t note, LegatoParams &par); void makeUnsustainable(note_t note); diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp @@ -879,14 +879,14 @@ void Master::defaults() /* * Note On Messages (velocity=0 for NoteOff) */ -void Master::noteOn(char chan, note_t note, char velocity) +void Master::noteOn(char chan, note_t note, char velocity, float note_log2_freq) { if(velocity) { for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart) { if(chan == part[npart]->Prcvchn) { fakepeakpart[npart] = velocity * 2; if(part[npart]->Penabled) - part[npart]->NoteOn(note, velocity, keyshift); + part[npart]->NoteOn(note, velocity, keyshift, note_log2_freq); } } activeNotes[note] = 1; diff --git a/src/Misc/Master.h b/src/Misc/Master.h @@ -104,7 +104,10 @@ class Master void putalldata(const char *data); //Midi IN - void noteOn(char chan, note_t note, char velocity); + void noteOn(char chan, note_t note, char velocity) { + noteOn(chan, note, velocity, note / 12.0f); + }; + void noteOn(char chan, note_t note, char velocity, float note_log2_freq); void noteOff(char chan, note_t note); void polyphonicAftertouch(char chan, note_t note, char velocity); void setController(char chan, int type, int par); diff --git a/src/Misc/Microtonal.cpp b/src/Misc/Microtonal.cpp @@ -257,8 +257,10 @@ unsigned char Microtonal::getoctavesize() const /* * Get the frequency according the note number */ -float Microtonal::getnotefreq(note_t note, int keyshift) const +float Microtonal::getnotefreq(float note_log2_freq, int keyshift) const { + note_t note = roundf(12.0f * note_log2_freq); + // in this function will appears many times things like this: // var=(a+b*100)%b // I had written this way because if I use var=a%b gives unwanted results when a<0 @@ -272,9 +274,8 @@ float Microtonal::getnotefreq(note_t note, int keyshift) const powf(2.0f, (Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents if(Penabled == 0) //12tET - return powf(2.0f, - (note - PAnote - + keyshift) / 12.0f) * PAfreq * globalfinedetunerap; + return powf(2.0f, note_log2_freq + + ((keyshift - PAnote) / 12.0f)) * PAfreq * globalfinedetunerap; int scaleshift = ((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize; diff --git a/src/Misc/Microtonal.h b/src/Misc/Microtonal.h @@ -68,7 +68,7 @@ class Microtonal void defaults(); /**Calculates the frequency for a given note */ - float getnotefreq(note_t note, int keyshift) const; + float getnotefreq(float note_log2_freq, int keyshift) const; //Parameters diff --git a/src/Misc/Part.cpp b/src/Misc/Part.cpp @@ -275,7 +275,7 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_, Pname = new char[PART_MAX_NAME_LEN]; oldvolumel = oldvolumer = 0.5f; - lastnote = -1; + lastnote = -1; defaults(); assert(partefx[0]); @@ -452,7 +452,8 @@ static int kit_usage(const Part::Kit *kits, int note, int mode) */ bool Part::NoteOn(note_t note, unsigned char velocity, - int masterkeyshift) + int masterkeyshift, + float note_log2_freq) { //Verify Basic Mode and sanity const bool isRunningNote = notePool.existsRunningNote(); @@ -471,6 +472,7 @@ bool Part::NoteOn(note_t note, monomemPush(note); monomem[note].velocity = velocity; monomem[note].mkeyshift = masterkeyshift; + monomem[note].note_log2_freq = note_log2_freq; } else if(!monomemEmpty()) monomemClear(); @@ -485,9 +487,9 @@ bool Part::NoteOn(note_t note, const float vel = getVelocity(velocity, Pvelsns, Pveloffs); const int partkeyshift = (int)Pkeyshift - 64; const int keyshift = masterkeyshift + partkeyshift; - const float notebasefreq = getBaseFreq(note, keyshift); + const float notebasefreq = getBaseFreq(note_log2_freq, keyshift); - if(notebasefreq < 0) + if(notebasefreq < 0.0f) return false; //Portamento @@ -507,8 +509,8 @@ bool Part::NoteOn(note_t note, //Adjust Existing Notes if(doingLegato) { - LegatoParams pars = {notebasefreq, vel, portamento, note, true, prng()}; - notePool.applyLegato(pars); + LegatoParams pars = {notebasefreq, vel, portamento, note_log2_freq, true, prng()}; + notePool.applyLegato(note, pars); return true; } @@ -523,7 +525,7 @@ bool Part::NoteOn(note_t note, continue; SynthParams pars{memory, ctl, synth, time, notebasefreq, vel, - portamento, note, false, prng()}; + portamento, note_log2_freq, false, prng()}; const int sendto = Pkitmode ? item.sendto() : 0; try { @@ -729,16 +731,18 @@ void Part::MonoMemRenote() { note_t mmrtempnote = monomemBack(); // Last list element. monomemPop(mmrtempnote); // We remove it, will be added again in NoteOn(...). - NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, - monomem[mmrtempnote].mkeyshift); + NoteOn(mmrtempnote, + monomem[mmrtempnote].velocity, + monomem[mmrtempnote].mkeyshift, + monomem[mmrtempnote].note_log2_freq); } -float Part::getBaseFreq(note_t note, int keyshift) const +float Part::getBaseFreq(float note_log2_freq, int keyshift) const { if(Pdrummode) - return 440.0f * powf(2.0f, (note - 69.0f) / 12.0f); + return 440.0f * powf(2.0f, note_log2_freq - (69.0f / 12.0f)); else - return microtonal->getnotefreq(note, keyshift); + return microtonal->getnotefreq(note_log2_freq, keyshift); } float Part::getVelocity(uint8_t velocity, uint8_t velocity_sense, diff --git a/src/Misc/Part.h b/src/Misc/Part.h @@ -43,9 +43,13 @@ class Part // Midi commands implemented //returns true when note is successfully applied + bool NoteOn(note_t note, uint8_t vel, int shift) REALTIME { + return (NoteOn(note, vel, shift, note / 12.0f)); + }; bool NoteOn(note_t note, unsigned char velocity, - int masterkeyshift) REALTIME; + int masterkeyshift, + float note_log2_freq) REALTIME; void NoteOff(note_t note) REALTIME; void PolyphonicAftertouch(note_t note, unsigned char velocity, @@ -159,7 +163,7 @@ class Part private: void MonoMemRenote(); // MonoMem stuff. - float getBaseFreq(note_t note, int keyshift) const; + float getBaseFreq(float note_log2_freq, int keyshift) const; float getVelocity(uint8_t velocity, uint8_t velocity_sense, uint8_t velocity_offset) const; void verifyKeyMode(void); @@ -187,6 +191,7 @@ class Part struct { unsigned char velocity; int mkeyshift; // I'm not sure masterkeyshift should be remembered. + float note_log2_freq; } monomem[256]; /* 256 is to cover all possible note values. monomem[] is used in conjunction with the list to diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp @@ -42,7 +42,7 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars, ADnoteParameters &pars = *pars_; portamento = spars.portamento; - midinote = spars.note; + note_log2_freq = spars.note_log2_freq; NoteEnabled = ON; basefreq = spars.frequency; velocity = spars.velocity; @@ -512,7 +512,7 @@ void ADnote::setupVoiceMod(int nvoice, bool first_run) SynthNote *ADnote::cloneLegato(void) { SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, - (bool)portamento, legato.param.midinote, true, + (bool)portamento, legato.param.note_log2_freq, true, initial_seed }; return memory.alloc<ADnote>(&pars, sp); } @@ -529,7 +529,7 @@ void ADnote::legatonote(LegatoParams lpars) return; portamento = lpars.portamento; - midinote = lpars.midinote; + note_log2_freq = lpars.note_log2_freq; basefreq = lpars.frequency; initial_seed = lpars.seed; current_prng_state = lpars.seed; @@ -1071,9 +1071,7 @@ float ADnote::getvoicebasefreq(int nvoice) const float fixedfreq = 440.0f; int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET; if(fixedfreqET != 0) { //if the frequency varies according the keyboard note - float tmp = - (midinote - - 69.0f) / 12.0f + float tmp = (note_log2_freq - (69.0f / 12.0f)) * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); if(fixedfreqET <= 64) fixedfreq *= powf(2.0f, tmp); diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h @@ -110,7 +110,7 @@ class ADnote:public SynthNote //GLOBALS ADnoteParameters &pars; unsigned char stereo; //if the note is stereo (allows note Panning) - note_t midinote; + float note_log2_freq; float velocity, basefreq; ONOFFTYPE NoteEnabled; diff --git a/src/Synth/PADnote.cpp b/src/Synth/PADnote.cpp @@ -35,13 +35,13 @@ PADnote::PADnote(const PADnoteParameters *parameters, NoteGlobalPar.FilterLfo = nullptr; firsttime = true; - setup(pars.frequency, pars.velocity, pars.portamento, pars.note, false, wm, prefix); + setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, false, wm, prefix); } void PADnote::setup(float freq, float velocity_, int portamento_, - note_t midinote, + float note_log2_freq, bool legato, WatchManager *wm, const char *prefix) @@ -58,8 +58,7 @@ void PADnote::setup(float freq, int fixedfreqET = pars.PfixedfreqET; if(fixedfreqET != 0) { //if the frequency varies according the keyboard note float tmp = - (midinote - - 69.0f) / 12.0f + (note_log2_freq - (69.0f / 12.0f)) * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); if(fixedfreqET <= 64) basefreq *= powf(2.0f, tmp); @@ -198,7 +197,7 @@ void PADnote::setup(float freq, SynthNote *PADnote::cloneLegato(void) { SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, - (bool)portamento, legato.param.midinote, true, legato.param.seed}; + (bool)portamento, legato.param.note_log2_freq, true, legato.param.seed}; return memory.alloc<PADnote>(&pars, sp, interpolation); } @@ -208,7 +207,7 @@ void PADnote::legatonote(LegatoParams pars) if(legato.update(pars)) return; - setup(pars.frequency, pars.velocity, pars.portamento, pars.midinote, true); + setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, true); } diff --git a/src/Synth/PADnote.h b/src/Synth/PADnote.h @@ -38,7 +38,7 @@ class PADnote:public SynthNote void releasekey(); private: void setup(float freq, float velocity, int portamento_, - note_t midinote, bool legato = false, WatchManager *wm=0, const char *prefix=0); + float note_log2_freq, bool legato = false, WatchManager *wm=0, const char *prefix=0); void fadein(float *smps); void computecurrentparameters(); bool finished_; diff --git a/src/Synth/SUBnote.cpp b/src/Synth/SUBnote.cpp @@ -46,7 +46,7 @@ SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars, WatchM NoteEnabled(true), lfilter(nullptr), rfilter(nullptr) { - setup(spars.frequency, spars.velocity, spars.portamento, spars.note, false, wm, prefix); + setup(spars.frequency, spars.velocity, spars.portamento, spars.note_log2_freq, false, wm, prefix); } float SUBnote::setupFilters(int *pos, bool automation) @@ -91,7 +91,7 @@ float SUBnote::setupFilters(int *pos, bool automation) void SUBnote::setup(float freq, float velocity, int portamento_, - note_t midinote, + float note_log2_freq, bool legato, WatchManager *wm, const char *prefix) @@ -120,7 +120,7 @@ void SUBnote::setup(float freq, basefreq = 440.0f; int fixedfreqET = pars.PfixedfreqET; if(fixedfreqET) { //if the frequency varies according the keyboard note - float tmp = (midinote - 69.0f) / 12.0f + float tmp = (note_log2_freq - (69.0f / 12.0f)) * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f); if(fixedfreqET <= 64) basefreq *= powf(2.0f, tmp); @@ -197,7 +197,7 @@ void SUBnote::setup(float freq, SynthNote *SUBnote::cloneLegato(void) { SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity, - portamento, legato.param.midinote, true, legato.param.seed}; + portamento, legato.param.note_log2_freq, true, legato.param.seed}; return memory.alloc<SUBnote>(&pars, sp); } @@ -208,7 +208,7 @@ void SUBnote::legatonote(LegatoParams pars) return; try { - setup(pars.frequency, pars.velocity, pars.portamento, pars.midinote, + setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, true, wm); } catch (std::bad_alloc &ba) { std::cerr << "failed to set legato note parameter in SUBnote: " << ba.what() << std::endl; diff --git a/src/Synth/SUBnote.h b/src/Synth/SUBnote.h @@ -38,7 +38,7 @@ class SUBnote:public SynthNote void setup(float freq, float velocity, int portamento_, - note_t midinote, + float note_log2_freq, bool legato = false, WatchManager *wm = 0, const char *prefix = 0); float setupFilters(int *pos, bool automation); void computecurrentparameters(); diff --git a/src/Synth/SynthNote.cpp b/src/Synth/SynthNote.cpp @@ -21,11 +21,11 @@ namespace zyn { SynthNote::SynthNote(SynthParams &pars) :memory(pars.memory), legato(pars.synth, pars.frequency, pars.velocity, pars.portamento, - pars.note, pars.quiet, pars.seed), ctl(pars.ctl), synth(pars.synth), time(pars.time) + pars.note_log2_freq, pars.quiet, pars.seed), ctl(pars.ctl), synth(pars.synth), time(pars.time) {} SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port, - note_t note, bool quiet, prng_t seed) + float note_log2_freq, bool quiet, prng_t seed) :synth(synth_) { // Initialise some legato-specific vars @@ -38,7 +38,7 @@ SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port param.freq = freq; param.vel = vel; param.portamento = port; - param.midinote = note; + param.note_log2_freq = note_log2_freq; param.seed = seed; lastfreq = 0.0f; silent = quiet; @@ -53,7 +53,7 @@ int SynthNote::Legato::update(LegatoParams pars) param.freq = pars.frequency; param.vel = pars.velocity; param.portamento = pars.portamento; - param.midinote = pars.midinote; + param.note_log2_freq = pars.note_log2_freq; if(msg == LM_Norm) { if(silent) { fade.m = 0.0f; @@ -92,7 +92,7 @@ void SynthNote::Legato::apply(SynthNote &note, float *outl, float *outr) decounter = -10; msg = LM_ToNorm; LegatoParams pars{param.freq, param.vel, param.portamento, - param.midinote, false, param.seed}; + param.note_log2_freq, false, param.seed}; note.legatonote(pars); break; } @@ -134,7 +134,7 @@ void SynthNote::Legato::apply(SynthNote &note, float *outl, float *outr) //previous freq during the fadeout. float catchupfreq = param.freq * (param.freq / lastfreq); LegatoParams pars{catchupfreq, param.vel, param.portamento, - param.midinote, false, param.seed}; + param.note_log2_freq, false, param.seed}; note.legatonote(pars); break; } @@ -154,7 +154,7 @@ void SynthNote::Legato::apply(SynthNote &note, float *outl, float *outr) void SynthNote::setVelocity(float velocity_) { legato.setSilent(true); //Let legato.update(...) returns 0. LegatoParams pars{legato.getFreq(), velocity_, - legato.getPortamento(), legato.getMidiNote(), true, legato.getSeed()}; + legato.getPortamento(), legato.getNoteLog2Freq(), true, legato.getSeed()}; try { legatonote(pars); } catch (std::bad_alloc &ba) { diff --git a/src/Synth/SynthNote.h b/src/Synth/SynthNote.h @@ -29,7 +29,7 @@ struct SynthParams float frequency; //Note base frequency float velocity; //Velocity of the Note bool portamento;//True if portamento is used for this note - note_t note; //Integer value of the note + float note_log2_freq; //Floating point value of the note bool quiet; //Initial output condition for legato notes prng_t seed; //Random seed }; @@ -39,7 +39,7 @@ struct LegatoParams float frequency; float velocity; bool portamento; - note_t midinote; + float note_log2_freq; //Floating point value of the note bool externcall; prng_t seed; }; @@ -84,7 +84,7 @@ class SynthNote { public: Legato(const SYNTH_T &synth_, float freq, float vel, int port, - note_t note, bool quiet, prng_t seed); + float note_log2_freq, bool quiet, prng_t seed); void apply(SynthNote &note, float *outl, float *outr); int update(LegatoParams pars); @@ -102,7 +102,7 @@ class SynthNote struct { // Note parameters float freq, vel; bool portamento; - note_t midinote; + float note_log2_freq; prng_t seed; } param; const SYNTH_T &synth; @@ -111,7 +111,7 @@ class SynthNote float getFreq() {return param.freq; } float getVelocity() {return param.vel; } bool getPortamento() {return param.portamento; } - note_t getMidiNote() {return param.midinote; } + float getNoteLog2Freq() {return param.note_log2_freq; } prng_t getSeed() {return param.seed;} void setSilent(bool silent_) {silent = silent_; } void setDecounter(int decounter_) {decounter = decounter_; } diff --git a/src/Tests/AdNoteTest.h b/src/Tests/AdNoteTest.h @@ -91,7 +91,7 @@ class AdNoteTest:public CxxTest::TestSuite //lets go with.... 50! as a nice note testnote = 50; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false, prng()}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new ADnote(defaultPreset, pars); diff --git a/src/Tests/MemoryStressTest.h b/src/Tests/MemoryStressTest.h @@ -86,7 +86,7 @@ class AdNoteTest:public CxxTest::TestSuite unsigned char testnote = 42; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false, prng()}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; std::vector<ADnote*> notes; diff --git a/src/Tests/MicrotonalTest.h b/src/Tests/MicrotonalTest.h @@ -62,7 +62,7 @@ class MicrotonalTest:public CxxTest::TestSuite for(int i = 0; i < 128; ++i) TS_ASSERT_EQUALS(testMicro->Pmapping[i], i); - TS_ASSERT_DELTA(testMicro->getnotefreq(19, 0), 24.4997f, 0.0001f); + TS_ASSERT_DELTA(testMicro->getnotefreq(19 / 12.0f, 0), 24.4997f, 0.0001f); } //Tests saving/loading to XML diff --git a/src/Tests/PadNoteTest.h b/src/Tests/PadNoteTest.h @@ -104,7 +104,7 @@ class PadNoteTest:public CxxTest::TestSuite //lets go with.... 50! as a nice note testnote = 50; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars_{memory, *controller, *synth, *time, freq, 120, 0, testnote, false, prng()}; + SynthParams pars_{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new PADnote(pars, pars_, interpolation); } diff --git a/src/Tests/SubNoteTest.h b/src/Tests/SubNoteTest.h @@ -78,7 +78,7 @@ class SubNoteTest:public CxxTest::TestSuite testnote = 50; float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f); - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false, prng()}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new SUBnote(defaultPreset, pars); this->pars = defaultPreset; } diff --git a/src/Tests/UnisonTest.h b/src/Tests/UnisonTest.h @@ -98,7 +98,7 @@ class AdNoteTest:public CxxTest::TestSuite params->VoicePar[0].Unison_vibratto_speed = e; params->VoicePar[0].Unison_invert_phase = f; - SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote, false, prng()}; + SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()}; note = new ADnote(params, pars); note->noteout(outL, outR); TS_ASSERT_DELTA(outL[80], values[0], 1e-5);