zynaddsubfx

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

commit 08bf033917388745b3a706ca58b572fcd1fc0452
parent 20ed6166c0ebeb61323ffad0b0b2fc06cf2320f5
Author: friedolino78 <34608315+friedolino78@users.noreply.github.com>
Date:   Wed, 28 Sep 2022 23:48:21 +0200

implement guitar string frequencies (#207)

Also:

* Add stringchoir sizes
* Add preset menu to FL GUI
Diffstat:
Msrc/Effects/CombFilterBank.cpp | 9+++++++--
Msrc/Effects/Sympathetic.cpp | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/Effects/Sympathetic.h | 11+++++++++++
Msrc/UI/EffUI.fl | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 146 insertions(+), 19 deletions(-)

diff --git a/src/Effects/CombFilterBank.cpp b/src/Effects/CombFilterBank.cpp @@ -107,6 +107,7 @@ namespace zyn { for (unsigned int j = 0; j < nrOfStrings; ++j) { + if (delays[j] == 0.0f) continue; assert(float(mem_size)>delays[j]); // calculate the feedback sample positions in the buffer const float pos_reader = fmodf(float(pos_writer+mem_size) - delays[j], float(mem_size)); @@ -117,13 +118,17 @@ namespace zyn { } // mix output buffer samples to output sample smp[i]=0.0f; + unsigned int nrOfActualStrings = 0; for (unsigned int j = 0; j < nrOfStrings; ++j) - smp[i] += string_smps[j][pos_writer]; + if (delays[j] != 0.0f) { + smp[i] += string_smps[j][pos_writer]; + nrOfActualStrings++; + } // apply output gain to sum of strings and // divide by nrOfStrings to get mean value // division by zero is catched at the beginning filterOut() - smp[i] *= outgain / (float)nrOfStrings; + smp[i] *= outgain / (float)nrOfActualStrings; // increment writing position ++pos_writer %= mem_size; diff --git a/src/Effects/Sympathetic.cpp b/src/Effects/Sympathetic.cpp @@ -32,7 +32,7 @@ namespace zyn { rtosc::Ports Sympathetic::ports = { {"preset::i", rProp(parameter) - rOptions(Piano, Grand, Guitar, 12-String) + rOptions(Generic, Piano, Grand, Guitar, 12-String) rDoc("Instrument Presets"), 0, rBegin; rObject *o = (rObject*)d.obj; @@ -41,25 +41,25 @@ rtosc::Ports Sympathetic::ports = { else d.reply(d.loc, "i", o->Ppreset); rEnd}, - rEffParVol(rDefault(127)), + rEffParVol(rDefault(127), rPresets(100, 80, 100, 90)), rEffParPan(rDefault(64)), rEffPar(Pq, 2, rShort("q"), rDefault(65), - "Resonance"), + rPresets(125, 125, 110, 110), "Resonance"), rEffPar(Pdrive, 3, rShort("dr"), rDefault(65), - "Input Amplification"), + rPresets(5, 5, 20, 20), "Input Amplification"), rEffPar(Plevel, 4, rShort("lev"), rDefault(65), - "Output Amplification"), + rPresets(80, 90, 65, 77), "Output Amplification"), rEffPar(Punison_frequency_spread, 5, rShort("detune"), rDefault(30), - "Unison String Detune"), + rPresets(10, 5, 0, 10), "Unison String Detune"), rEffParTF(Pnegate, 6, rShort("neg"), rDefault(false), "Negate Signal"), rEffPar(Plpf, 7, rShort("lpf"), rDefault(127), "Low Pass Cutoff"), rEffPar(Phpf, 8, rShort("hpf"), rDefault(0), "High Pass Cutoff"), rEffParRange(Punison_size, 9, rShort("uni"), rLinear(1,3), rDefault(1), - "Number of Unison Strings"), + rPresets(3, 1, 1, 2), "Number of Unison Strings"), rEffParRange(Pstrings, 10, rShort("str"), rLinear(0,76), rDefault(0), - "Number of Strings"), + rPresets(12, 60, 6, 6), "Number of Strings"), rEffPar(Pbasenote, 11, rShort("base"), rDefault(57), // basefreq = powf(2.0f, (basenote-69)/12)*440; 57->220Hz - "Midi Note of Lowest String"), + rPresets(57, 33, 52, 52), "Midi Note of Lowest String"), rArrayF(freqs, 88, rLinear(27.50f,4186.01f), "String Frequencies"), }; @@ -198,13 +198,30 @@ void Sympathetic::sethpf(unsigned char _Phpf) void Sympathetic::calcFreqs() { + switch(Ppreset) { + case 0: + calcFreqsGeneric(); + break; + case 1: + case 2: + calcFreqsPiano(); + break; + case 3: + case 4: + calcFreqsGuitar(); + break; + } +} + +void Sympathetic::calcFreqsGeneric() +{ const float unison_spread_semicent = powf(Punison_frequency_spread / 63.5f, 2.0f) * 25.0f; const float unison_real_spread_up = powf(2.0f, (unison_spread_semicent * 0.5f) / 1200.0f); const float unison_real_spread_down = 1.0f/unison_real_spread_up; for(unsigned int i = 0; i < Punison_size*Pstrings; i+=Punison_size) { - const float centerFreq = powf(2.0f, (float)i / 12.0f) * baseFreq; + const float centerFreq = powf(2.0f, (float)i / 36.0f) * baseFreq; filterBank->delays[i] = ((float)samplerate)/centerFreq; if (Punison_size > 1) filterBank->delays[i+1] = ((float)samplerate)/(centerFreq * unison_real_spread_up); if (Punison_size > 2) filterBank->delays[i+2] = ((float)samplerate)/(centerFreq * unison_real_spread_down); @@ -213,20 +230,56 @@ void Sympathetic::calcFreqs() } +void Sympathetic::calcFreqsPiano() +{ + const float unison_spread_semicent = powf(Punison_frequency_spread / 63.5f, 2.0f) * 25.0f; + const float unison_real_spread_up = powf(2.0f, (unison_spread_semicent * 0.5f) / 1200.0f); + const float unison_real_spread_down = 1.0f/unison_real_spread_up; + + for(unsigned int i = 0; i < Punison_size*Pstrings; i+=Punison_size) + { + const float centerFreq = powf(2.0f, (float)i / 36.0f) * baseFreq; + const unsigned int stringchoir_size = + i>num_single_strings ? (i>Pstrings-num_triple_strings ? 3 : 2) :1; + filterBank->delays[i] = ((float)samplerate)/centerFreq; + if (stringchoir_size > 1) filterBank->delays[i+1] = ((float)samplerate)/(centerFreq * unison_real_spread_up); + else filterBank->delays[i+1] = 0; + if (stringchoir_size > 2) filterBank->delays[i+2] = ((float)samplerate)/(centerFreq * unison_real_spread_down); + else filterBank->delays[i+2] = 0; + } + filterBank->setStrings(Pstrings*Punison_size,baseFreq); + +} + +void Sympathetic::calcFreqsGuitar() +{ + const float unison_spread_semicent = powf(Punison_frequency_spread / 63.5f, 2.0f) * 25.0f; + const float unison_real_spread_up = powf(2.0f, (unison_spread_semicent * 0.5f) / 1200.0f); + + for(auto i = 0; i < 6*Punison_size; i+=Punison_size) + { + assert(guitar_freqs[i/Punison_size]>0.0f); + filterBank->delays[i] = ((float)samplerate)/guitar_freqs[i/Punison_size]; + if (Punison_size > 1) filterBank->delays[i+1] = ((float)samplerate)/(guitar_freqs[i/Punison_size] * unison_real_spread_up); + } + filterBank->setStrings(Pstrings*Punison_size,guitar_freqs[0]); + +} + unsigned char Sympathetic::getpresetpar(unsigned char npreset, unsigned int npar) { #define PRESET_SIZE 13 #define NUM_PRESETS 4 static const unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { - //Vol Pan Q Drive Lev Spr neg lp hp sz strings note cross + //Vol Pan Q Drive Lev Spr neg lp hp sz strings note //Piano 12-String {100, 64, 125, 5, 80, 10, 0, 127, 0, 3, 12, 57}, //Piano 60-String {80, 64, 125, 5, 90, 5, 0, 127, 0, 1, 60, 33}, //Guitar 6-String - {100, 64, 110, 5, 65, 0, 0, 127, 0, 1, 6, 52}, + {100, 64, 110, 20, 65, 0, 0, 127, 0, 1, 6, 52}, //Guitar 12-String - {90, 64, 110, 5, 77, 10, 0, 127, 0, 2, 6, 52}, + {90, 64, 110, 20, 77, 10, 0, 127, 0, 2, 6, 52}, }; if(npreset < NUM_PRESETS && npar < PRESET_SIZE) { if(npar == 0 && insertion == 0) { @@ -245,6 +298,8 @@ void Sympathetic::setpreset(unsigned char npreset) for(int n = 0; n != 128; n++) changepar(n, getpresetpar(npreset, n)); Ppreset = npreset; + + calcFreqs(); cleanup(); } @@ -288,10 +343,12 @@ void Sympathetic::changepar(int npar, unsigned char value) break; case 9: Punison_size = limit(value, (unsigned char) 1, (unsigned char) 3); + if (Punison_size>2) Ppreset=0; calcFreqs(); break; case 10: Pstrings = limit(value, (unsigned char) 0, (unsigned char) 76); + if (Pstrings>6) Ppreset=0; calcFreqs(); break; case 11: diff --git a/src/Effects/Sympathetic.h b/src/Effects/Sympathetic.h @@ -24,6 +24,14 @@ namespace zyn { const float gainbwd_offset = 0.873f; const float gainbwd_factor = 0.001f; +// number of piano keys with single string +const unsigned int num_single_strings = 12; +// number of piano keys with triple strings +const unsigned int num_triple_strings = 48; + +// frequencies of a guitar in standard e tuning +const float guitar_freqs[6] = {82.4f, 110.0f, 146.8f, 196.0f, 246.9f, 329.6f}; + class Sympathetic:public Effect { @@ -64,6 +72,9 @@ class Sympathetic:public Effect void setlpf(unsigned char _Plpf); void sethpf(unsigned char _Phpf); void calcFreqs(); + void calcFreqsGeneric(); + void calcFreqsPiano(); + void calcFreqsGuitar(); //Real Parameters class AnalogFilter * lpfl, *lpfr, *hpfl, *hpfr; diff --git a/src/UI/EffUI.fl b/src/UI/EffUI.fl @@ -1164,6 +1164,33 @@ eqgraph->update();} code3 {set_module_parameters(o);} class Fl_Group visible } { + Fl_Choice sympp { + label Preset + xywh {11 15 95 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->init("preset");} + class Fl_Osc_Choice + } { + MenuItem {} { + label {Generic} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Piano} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Grand} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Guitar} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {12-String} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + } Fl_Dial symp0 { label Vol tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 @@ -1202,7 +1229,7 @@ eqgraph->update();} } Fl_Dial symp10 { label Str - tooltip {number of strings} xywh {220 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 48 + tooltip {number of strings} xywh {220 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 76 code0 {o->init("parameter10");} class Fl_Osc_Dial } @@ -1319,7 +1346,7 @@ switch(efftype){ effalienwahwindow->show(); break; case 6: - effdistortionwindow->show(); + effdistortionwindow->show(); break; case 7:eqband=0; bandcounter->value(eqband); @@ -2091,6 +2118,33 @@ eqgraph->redraw();} code3 {set_module_parameters(o);} class Fl_Group visible } { + Fl_Choice sympp { + label Preset + xywh {11 15 95 15} box UP_BOX down_box BORDER_BOX color 47 selection_color 7 labelfont 1 labelsize 10 align 5 textfont 1 textsize 10 + code0 {o->init("preset");} + class Fl_Osc_Choice + } { + MenuItem {} { + label {Generic} + xywh {20 20 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Piano} + xywh {30 30 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Grand} + xywh {40 40 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {Guitar} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + MenuItem {} { + label {12-String} + xywh {50 50 100 20} labelfont 1 labelsize 10 + } + } Fl_Dial symp0 { label Vol tooltip {Effect Volume} xywh {10 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 @@ -2129,7 +2183,7 @@ eqgraph->redraw();} } Fl_Dial symp10 { label Strings - tooltip {number of strings} xywh {220 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 127 + tooltip {number of strings} xywh {220 40 30 30} box ROUND_UP_BOX labelfont 1 labelsize 11 maximum 76 code0 {o->init("parameter10");} class Fl_Osc_Dial } @@ -2218,7 +2272,7 @@ switch(efftype){ effalienwahwindow->show(); break; case 6: - effdistortionwindow->show(); + effdistortionwindow->show(); break; case 7: bandcounter->value(eqband);