From ab4a7de34dc49493c9eb31bf0954920858e4b2ce Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Mon, 7 May 2018 06:09:21 +0200 Subject: [PATCH] Fixes. --- EngineMkI.cpp | 30 +- EngineMkI.h | 2 +- EngineOpl.cpp | 3 +- EngineOpl.h | 4 +- MicroDexed.ino | 36 +- controllers.h | 41 +-- dexed.cpp | 36 +- dexed.h | 6 +- dexed.orig | 931 +++++++++++++++++++++++++++++++++++++++++++++++ dx7note.cpp | 52 ++- dx7note.h | 4 +- env.cc | 188 ++++++++++ env.h | 10 +- fm_core.cpp | 5 +- fm_core.h | 2 +- fm_op_kernel.cc | 283 ++++++++++++++ fm_op_kernel.cpp | 6 +- lfo.cpp | 1 - synth.h | 8 - trace.h | 11 + 20 files changed, 1548 insertions(+), 111 deletions(-) create mode 100644 dexed.orig create mode 100644 env.cc create mode 100644 fm_op_kernel.cc create mode 100644 trace.h diff --git a/EngineMkI.cpp b/EngineMkI.cpp index 9b94d43..9588fa2 100644 --- a/EngineMkI.cpp +++ b/EngineMkI.cpp @@ -21,6 +21,7 @@ * */ +#include #include "EngineMkI.h" #define _USE_MATH_DEFINES #include @@ -29,22 +30,21 @@ #include "sin.h" #include "exp2.h" -#undef min -#define min(a,b) ((a)<(b)?(a):(b)) - #ifdef DEBUG #include "time.h" //#define MKIDEBUG #endif #ifdef _WIN32 +#if _MSC_VER < 1800 double log2(double n) { return log(n) / log(2.0); } double round(double n) { return n < 0.0 ? ceil(n - 0.5) : floor(n + 0.5); } - __declspec(align(16)) const int zeros[_N_] = {0}; +#endif + __declspec(align(16)) const int zeros[N] = {0}; #else const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0}; #endif @@ -77,6 +77,7 @@ static inline uint16_t sinLog(uint16_t phi) { return sinLogTable[index ^ SINLOG_TABLEFILTER] | NEGATIVE_BIT; } } + EngineMkI::EngineMkI() { float bitReso = SINLOG_TABLESIZE; @@ -95,32 +96,36 @@ EngineMkI::EngineMkI() { char buffer[4096]; int pos = 0; + TRACE("****************************************"); for(int i=0;i 90 ) { + TRACE("SINLOGTABLE: %s" ,buffer); buffer[0] = 0; pos = 0; } } + TRACE("SINLOGTABLE: %s", buffer); buffer[0] = 0; pos = 0; + TRACE("----------------------------------------"); for(int i=0;i 90 ) { + TRACE("SINEXTTABLE: %s" ,buffer); buffer[0] = 0; pos = 0; } } + TRACE("SINEXTTABLE: %s", buffer); + TRACE("****************************************"); #endif - } inline int32_t mkiSin(int32_t phase, uint16_t env) { uint16_t expVal = sinLog(phase >> (22 - SINLOG_BITDEPTH)) + (env); -#ifdef MKIDEBUG - int16_t expValShow = expVal; -#endif - + //int16_t expValShow = expVal; + const bool isSigned = expVal & NEGATIVE_BIT; expVal &= ~NEGATIVE_BIT; @@ -135,6 +140,7 @@ inline int32_t mkiSin(int32_t phase, uint16_t env) { if ( expValShow < 0 ) { expValShow = (expValShow + 0x7FFF) * -1; } + //TRACE(",%d,%d,%d,%d,%d,%d", phase >> (22 - SINLOG_BITDEPTH), env, expValShow, ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER, resultB4, result); } #endif @@ -287,7 +293,7 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, fb_buf[1] = y; } -void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) { +void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift) { const int kLevelThresh = ENV_MAX-100; FmAlgorithm alg = algorithms[algorithm]; bool has_contents[3] = { true, false, false }; @@ -322,14 +328,14 @@ void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32 switch ( algorithm ) { // three operator feedback, process exception for ALGO 4 case 3 : - compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2),16)); + compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16)); params[1].phase += params[1].freq << LG_N; // hack, we already processed op-5 - op-4 params[2].phase += params[2].freq << LG_N; // yuk yuk op += 2; // ignore the 2 other operators break; // two operator feedback, process exception for ALGO 6 case 5 : - compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2),16)); + compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16)); params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5 op++; // ignore next operator; break; diff --git a/EngineMkI.h b/EngineMkI.h index 3222de4..f790e0f 100644 --- a/EngineMkI.h +++ b/EngineMkI.h @@ -29,7 +29,7 @@ class EngineMkI : public FmCore { public: EngineMkI(); - void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift); + void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift); void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add); diff --git a/EngineOpl.cpp b/EngineOpl.cpp index 0d2c091..532f4d5 100644 --- a/EngineOpl.cpp +++ b/EngineOpl.cpp @@ -168,8 +168,7 @@ void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq, } -void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm, - int32_t *fb_buf, int feedback_shift) { +void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,int32_t *fb_buf, int32_t feedback_shift) { const int kLevelThresh = 507; // really ???? const FmAlgorithm alg = algorithms[algorithm]; bool has_contents[3] = { true, false, false }; diff --git a/EngineOpl.h b/EngineOpl.h index 59b57fe..6f71088 100644 --- a/EngineOpl.h +++ b/EngineOpl.h @@ -20,7 +20,7 @@ #ifndef ENGINEOPL_H_INCLUDED #define ENGINEOPL_H_INCLUDED - +#include "Arduino.h" #include "synth.h" #include "aligned_buf.h" #include "fm_op_kernel.h" @@ -31,7 +31,7 @@ class EngineOpl : public FmCore { public: virtual void render(int32_t *output, FmOpParams *params, int algorithm, - int32_t *fb_buf, int feedback_shift); + int32_t *fb_buf, int32_t feedback_shift); void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add); void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add); void compute_fb(int32_t *output, int32_t phase0, int32_t freq, diff --git a/MicroDexed.ino b/MicroDexed.ino index b626e00..e40b2bf 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -5,7 +5,7 @@ #define RATE 128 #define TEENSY 1 -#define TEST_MIDI +#define TEST_MIDI 1 #ifdef TEENSY #include @@ -30,6 +30,8 @@ Dexed* dexed = new Dexed(RATE); void setup() { Serial.begin(115200); + //while (!Serial) ; // wait for Arduino Serial Monitor + delay(200); Serial.println(F("MicroDexed")); MIDI.begin(MIDI_CHANNEL_OMNI); @@ -40,7 +42,7 @@ void setup() AudioMemory(8); sgtl5000_1.enable(); - sgtl5000_1.volume(0.5); + sgtl5000_1.volume(0.2); // Initialize processor and memory measurements //AudioProcessorUsageMaxReset(); @@ -54,9 +56,14 @@ void setup() } #endif + dexed->activate(); + #ifdef TEST_MIDI dexed->ProcessMidiMessage(0x90, 64, 100); #endif + + Serial.println(dexed->getEngineType(), DEC); + Serial.println("Go"); } void loop() @@ -68,18 +75,35 @@ void loop() #endif // process midi->audio - while (MIDI.read()) + if (MIDI.read()) { dexed->ProcessMidiMessage(MIDI.getType(), MIDI.getData1(), MIDI.getData2()); } +/* uint8_t i = 0; + Serial.println("Before:"); + for (i = 0; i < 128; i++) + { + Serial.print(audio_buffer[i]); + Serial.print(","); + } + Serial.println();*/ - dexed->GetSamples(audio_buffer); + dexed->GetSamples(RATE, audio_buffer); + +/* Serial.println("After:"); + for (i = 0; i < 128; i++) + { + Serial.print(audio_buffer[i]); + Serial.print(","); + } + Serial.println();*/ #ifdef TEENSY // play the current buffer - while (!queue1.available()); - + while (!queue1.available()) + Serial.println("Block"); queue1.playBuffer(); #endif + delay(500); } diff --git a/controllers.h b/controllers.h index 5a34622..b8179e2 100644 --- a/controllers.h +++ b/controllers.h @@ -20,7 +20,7 @@ #include "synth.h" #include #include -//#include +#include "trace.h" #ifdef _WIN32 #define snprintf _snprintf @@ -48,46 +48,42 @@ struct FmMod { void setRange(uint8_t r) { range = r < 0 && r > 127 ? 0 : r; + TRACE("range=%d",range); } void setTarget(uint8_t assign) { + TRACE("Target: %d", assign); assign=assign < 0 && assign > 7 ? 0 : assign; - if(assign&1) // AMP - pitch=true; - if(assign&2) // PITCH - amp=true; - if(assign&4) // EG - eg=true; + pitch=assign&1; // AMP + amp=assign&2; // PITCH + eg=assign&4; // EG + + TRACE("pitch[%d] amp[%d] eg[%d]", pitch,amp,eg); } }; class Controllers { void applyMod(int cc, FmMod &mod) { - int total = float(cc) * 0.01 * float(mod.range); - - - if(mod.amp==true) - { + float range = 0.01 * mod.range; + uint8_t total = (float)cc * range; + TRACE("amp[%d]|pitch[%d]|eg[%d]",mod.amp,mod.pitch,mod.eg); + TRACE("range=%f mod.range=%d total=%d cc=%d",range,mod.range,total,cc); + if(mod.amp) amp_mod = max(amp_mod, total); - } - if(mod.pitch==true) - { + if(mod.pitch) pitch_mod = max(pitch_mod, total); - } - if(mod.eg==true) - { + if(mod.eg) eg_mod = max(eg_mod, total); - } } public: int32_t values_[3]; - int amp_mod; - int pitch_mod; - int eg_mod; + uint8_t amp_mod; + uint8_t pitch_mod; + uint8_t eg_mod; uint8_t aftertouch_cc; uint8_t breath_cc; @@ -119,7 +115,6 @@ public: if ( ! ((wheel.eg || foot.eg) || (breath.eg || at.eg)) ) eg_mod = 127; - } FmCore *core; diff --git a/dexed.cpp b/dexed.cpp index 29c5f50..65e6352 100644 --- a/dexed.cpp +++ b/dexed.cpp @@ -31,7 +31,7 @@ #include #include -Dexed::Dexed(double num_samples) +Dexed::Dexed(uint16_t num_samples) { uint8_t i; @@ -78,7 +78,7 @@ Dexed::Dexed(double num_samples) memset(&voiceStatus, 0, sizeof(VoiceStatus)); - setEngineType(DEXED_ENGINE_MODERN); + setEngineType(DEXED_ENGINE_MARKI); } Dexed::~Dexed() @@ -337,21 +337,9 @@ void Dexed::deactivate(void) } */ -/*void Dexed::run (uint8_t* midi_data) - { - static int16_t buffer; - - ProcessMidiMessage(midi_data); - - // render audio from the last frame until the timestamp of this event - GetSamples(&buffer); - - //fx.process(&buffer,_rate); - }*/ - -void Dexed::GetSamples(int16_t* buffer) +void Dexed::GetSamples(uint32_t n_samples, int16_t* buffer) { - uint32_t i = 0; + uint32_t i; if (refreshVoice) { for (i = 0; i < max_notes; i++) { @@ -362,8 +350,7 @@ void Dexed::GetSamples(int16_t* buffer) refreshVoice = false; } - // remaining buffer is still to be processed - for (; i < _rate; i += _N_) { + for (i = 0; i < n_samples; i += _N_) { AlignedBuf audiobuf; float sumbuf[_N_]; @@ -376,9 +363,12 @@ void Dexed::GetSamples(int16_t* buffer) int32_t lfodelay = lfo.getdelay(); for (uint8_t note = 0; note < max_notes; ++note) { + if (voices[note].live) { voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); + for (uint32_t j = 0; j < _N_; ++j) { + int32_t val = audiobuf.get()[j]; val = val >> 4; int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; @@ -390,11 +380,12 @@ void Dexed::GetSamples(int16_t* buffer) } } } - - for (uint32_t j = 0; j < _N_; ++j) - buffer[i + j] = sumbuf[j]; + for (uint32_t j = 0; j < _N_; ++j) { + buffer[j + (i * _N_)] = sumbuf[j]; + } } + // mark unused voice as not live if (++_k_rate_counter % 32 && !monoMode) { uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers @@ -410,7 +401,7 @@ void Dexed::GetSamples(int16_t* buffer) for (uint8_t op = 0; op < 6; op++) { - uint8_t op_bit = pow(2, op); + uint8_t op_bit = static_cast(pow(2, op)); if ((op_carrier & op_bit) > 0) { @@ -442,7 +433,6 @@ bool Dexed::ProcessMidiMessage(uint8_t cmd, uint8_t data1, uint8_t data2) return (false); break; case 0x90 : - // TRACE("MIDI keydown event: %d %d",buf[1],buf[2]); keydown(data1, data2); return (false); break; diff --git a/dexed.h b/dexed.h index c9cd4e6..89d1727 100644 --- a/dexed.h +++ b/dexed.h @@ -72,7 +72,7 @@ class DexedVoice class Dexed { public: - Dexed(double rate); + Dexed(uint16_t rate); ~Dexed(); void run(uint8_t* midi_data); void activate(void); @@ -82,7 +82,7 @@ class Dexed bool isMonoMode(void); void setMonoMode(bool mode); void set_params(void); - void GetSamples(int16_t* buffer); + void GetSamples(uint32_t n_samples, int16_t* buffer); bool ProcessMidiMessage(uint8_t cmd, uint8_t data1, uint8_t data2); Controllers controllers; @@ -110,8 +110,6 @@ class Dexed EngineOpl* engineOpl; float* outbuf_; uint32_t bufsize_; - float extra_buf_[_N_]; - uint32_t extra_buf_size_; private: uint16_t _rate; diff --git a/dexed.orig b/dexed.orig new file mode 100644 index 0000000..c5a6246 --- /dev/null +++ b/dexed.orig @@ -0,0 +1,931 @@ +/** + * + * Copyright (c) 2016-2017 Holger Wirtz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "synth.h" +#include "dexed.h" +#include "EngineMkI.h" +#include "EngineOpl.h" +#include "fm_core.h" +#include "exp2.h" +#include "sin.h" +#include "freqlut.h" +#include "controllers.h" +#include +#include + +Dexed::Dexed(double rate) : lvtk::Synth(p_n_ports, p_midi_in) +{ + uint8_t i; + + TRACE("--------------------------------------------------------------------------------"); + TRACE("Hi"); + + Exp2::init(); + Tanh::init(); + Sin::init(); + + Freqlut::init(rate); + Lfo::init(rate); + PitchEnv::init(rate); + Env::init_sr(rate); + fx.init(rate); + + engineMkI=new EngineMkI; + engineOpl=new EngineOpl; + engineMsfa=new FmCore; + + /* + if(!(engineMkI=new (std::nothrow) EngineMkI)) + TRACE("Cannot not create engine EngineMkI"); + if(!(engineOpl=new (std::nothrow) EngineOpl)) + { + delete(engineMkI); + TRACE("Cannot not create engine EngineOpl"); + } + if(!(engineMsfa=new (std::nothrow) FmCore)) + { + delete(engineMkI); + delete(engineOpl); + TRACE("Cannot create engine FmCore"); + } + */ + + for(i=0; i(data[i]); + TRACE("%d->%f",i,data_float[i]); + } + + max_notes=16; + currentNote = 0; + controllers.values_[kControllerPitch] = 0x2000; + controllers.values_[kControllerPitchRange] = 0; + controllers.values_[kControllerPitchStep] = 0; + controllers.modwheel_cc = 0; + controllers.foot_cc = 0; + controllers.breath_cc = 0; + controllers.aftertouch_cc = 0; + controllers.masterTune=0; + controllers.opSwitch=0x3f; // enable all operators + //controllers.opSwitch=0x00; + + bufsize_=256; + + outbuf_=new float[bufsize_]; + //if(!(outbuf_=new (std::nothrow) float[bufsize_])) + // TRACE("Cannot create outbuf_ buffer"); + + lfo.reset(data+137); + + setMonoMode(false); + + sustain = false; + + extra_buf_size_ = 0; + + memset(&voiceStatus, 0, sizeof(VoiceStatus)); + + setEngineType(DEXED_ENGINE_MODERN); + + //add_voices(new DexedVoice(rate)); + + add_audio_outputs(p_audio_out); + + TRACE("Bye"); +} + +Dexed::~Dexed() +{ + TRACE("Hi"); + + delete [] outbuf_; + + currentNote = -1; + + for (uint8_t note = 0; note < MAX_ACTIVE_NOTES; ++note) + delete voices[note].dx7_note; + + delete(engineMsfa); + delete(engineOpl); + delete(engineMkI); + + TRACE("Bye"); + TRACE("--------------------------------------------------------------------------------"); +} + +void Dexed::activate(void) +{ + TRACE("Hi"); + + Plugin::activate(); + panic(); + controllers.values_[kControllerPitchRange] = data[155]; + controllers.values_[kControllerPitchStep] = data[156]; + + TRACE("Bye"); +} + +void Dexed::deactivate(void) +{ + TRACE("Hi"); + + Plugin::deactivate(); + + TRACE("Bye"); +} + +void Dexed::set_params(void) +{ + //TRACE("Hi"); + + _param_change_counter=0; + + bool polymono=bool(*p(p_polymono)); + uint8_t engine=uint8_t(*p(p_engine)); + float f_gain=*p(p_output); + float f_cutoff=*p(p_cutoff); + float f_reso=*p(p_resonance); + + // Dexed-Unisono + if(isMonoMode()!=polymono) + setMonoMode(polymono); + + // Dexed-Engine + if(controllers.core==NULL || getEngineType()!=engine) + { + setEngineType(engine); + refreshVoice=true; + } + + // Dexed-Filter + if(fx.uiCutoff!=f_cutoff) + { + fx.uiCutoff=f_cutoff; + refreshVoice=true; + } + if(fx.uiReso!=f_reso) + { + fx.uiReso=f_reso; + refreshVoice=true; + } + if(fx.uiGain!=f_gain) + { + fx.uiGain=f_gain; + refreshVoice=true; + } + + // OP6 + onParam(0,*p(p_op6_eg_rate_1)); + onParam(1,*p(p_op6_eg_rate_2)); + onParam(2,*p(p_op6_eg_rate_3)); + onParam(3,*p(p_op6_eg_rate_4)); + onParam(4,*p(p_op6_eg_level_1)); + onParam(5,*p(p_op6_eg_level_2)); + onParam(6,*p(p_op6_eg_level_3)); + onParam(7,*p(p_op6_eg_level_4)); + onParam(8,*p(p_op6_kbd_lev_scl_brk_pt)); + onParam(9,*p(p_op6_kbd_lev_scl_lft_depth)); + onParam(10,*p(p_op6_kbd_lev_scl_rht_depth)); + onParam(11,*p(p_op6_kbd_lev_scl_lft_curve)); + onParam(12,*p(p_op6_kbd_lev_scl_rht_curve)); + onParam(13,*p(p_op6_kbd_rate_scaling)); + onParam(14,*p(p_op6_amp_mod_sensitivity)); + onParam(15,*p(p_op6_key_vel_sensitivity)); + onParam(16,*p(p_op6_operator_output_level)); + onParam(17,*p(p_op6_osc_mode)); + onParam(18,*p(p_op6_osc_freq_coarse)); + onParam(19,*p(p_op6_osc_freq_fine)); + onParam(20,*p(p_op6_osc_detune)+7); + // OP5 + onParam(21,*p(p_op5_eg_rate_1)); + onParam(22,*p(p_op5_eg_rate_2)); + onParam(23,*p(p_op5_eg_rate_3)); + onParam(24,*p(p_op5_eg_rate_4)); + onParam(25,*p(p_op5_eg_level_1)); + onParam(26,*p(p_op5_eg_level_2)); + onParam(27,*p(p_op5_eg_level_3)); + onParam(28,*p(p_op5_eg_level_4)); + onParam(29,*p(p_op5_kbd_lev_scl_brk_pt)); + onParam(30,*p(p_op5_kbd_lev_scl_lft_depth)); + onParam(31,*p(p_op5_kbd_lev_scl_rht_depth)); + onParam(32,*p(p_op5_kbd_lev_scl_lft_curve)); + onParam(33,*p(p_op5_kbd_lev_scl_rht_curve)); + onParam(34,*p(p_op5_kbd_rate_scaling)); + onParam(35,*p(p_op5_amp_mod_sensitivity)); + onParam(36,*p(p_op5_key_vel_sensitivity)); + onParam(37,*p(p_op5_operator_output_level)); + onParam(38,*p(p_op5_osc_mode)); + onParam(39,*p(p_op5_osc_freq_coarse)); + onParam(40,*p(p_op5_osc_freq_fine)); + onParam(41,*p(p_op5_osc_detune)+7); + // OP4 + onParam(42,*p(p_op4_eg_rate_1)); + onParam(43,*p(p_op4_eg_rate_2)); + onParam(44,*p(p_op4_eg_rate_3)); + onParam(45,*p(p_op4_eg_rate_4)); + onParam(46,*p(p_op4_eg_level_1)); + onParam(47,*p(p_op4_eg_level_2)); + onParam(48,*p(p_op4_eg_level_3)); + onParam(49,*p(p_op4_eg_level_4)); + onParam(50,*p(p_op4_kbd_lev_scl_brk_pt)); + onParam(51,*p(p_op4_kbd_lev_scl_lft_depth)); + onParam(52,*p(p_op4_kbd_lev_scl_rht_depth)); + onParam(53,*p(p_op4_kbd_lev_scl_lft_curve)); + onParam(54,*p(p_op4_kbd_lev_scl_rht_curve)); + onParam(55,*p(p_op4_kbd_rate_scaling)); + onParam(56,*p(p_op4_amp_mod_sensitivity)); + onParam(57,*p(p_op4_key_vel_sensitivity)); + onParam(58,*p(p_op4_operator_output_level)); + onParam(59,*p(p_op4_osc_mode)); + onParam(60,*p(p_op4_osc_freq_coarse)); + onParam(61,*p(p_op4_osc_freq_fine)); + onParam(62,*p(p_op4_osc_detune)+7); + // OP3 + onParam(63,*p(p_op3_eg_rate_1)); + onParam(64,*p(p_op3_eg_rate_2)); + onParam(65,*p(p_op3_eg_rate_3)); + onParam(66,*p(p_op3_eg_rate_4)); + onParam(67,*p(p_op3_eg_level_1)); + onParam(68,*p(p_op3_eg_level_2)); + onParam(69,*p(p_op3_eg_level_3)); + onParam(70,*p(p_op3_eg_level_4)); + onParam(71,*p(p_op3_kbd_lev_scl_brk_pt)); + onParam(72,*p(p_op3_kbd_lev_scl_lft_depth)); + onParam(73,*p(p_op3_kbd_lev_scl_rht_depth)); + onParam(74,*p(p_op3_kbd_lev_scl_lft_curve)); + onParam(75,*p(p_op3_kbd_lev_scl_rht_curve)); + onParam(76,*p(p_op3_kbd_rate_scaling)); + onParam(77,*p(p_op3_amp_mod_sensitivity)); + onParam(78,*p(p_op3_key_vel_sensitivity)); + onParam(79,*p(p_op3_operator_output_level)); + onParam(80,*p(p_op3_osc_mode)); + onParam(81,*p(p_op3_osc_freq_coarse)); + onParam(82,*p(p_op3_osc_freq_fine)); + onParam(83,*p(p_op3_osc_detune)+7); + // OP2 + onParam(84,*p(p_op2_eg_rate_1)); + onParam(85,*p(p_op2_eg_rate_2)); + onParam(86,*p(p_op2_eg_rate_3)); + onParam(87,*p(p_op2_eg_rate_4)); + onParam(88,*p(p_op2_eg_level_1)); + onParam(89,*p(p_op2_eg_level_2)); + onParam(90,*p(p_op2_eg_level_3)); + onParam(91,*p(p_op2_eg_level_4)); + onParam(92,*p(p_op2_kbd_lev_scl_brk_pt)); + onParam(93,*p(p_op2_kbd_lev_scl_lft_depth)); + onParam(94,*p(p_op2_kbd_lev_scl_rht_depth)); + onParam(95,*p(p_op2_kbd_lev_scl_lft_curve)); + onParam(96,*p(p_op2_kbd_lev_scl_rht_curve)); + onParam(97,*p(p_op2_kbd_rate_scaling)); + onParam(98,*p(p_op2_amp_mod_sensitivity)); + onParam(99,*p(p_op2_key_vel_sensitivity)); + onParam(100,*p(p_op2_operator_output_level)); + onParam(101,*p(p_op2_osc_mode)); + onParam(102,*p(p_op2_osc_freq_coarse)); + onParam(103,*p(p_op2_osc_freq_fine)); + onParam(104,*p(p_op2_osc_detune)+7); + // OP1 + onParam(105,*p(p_op1_eg_rate_1)); + onParam(106,*p(p_op1_eg_rate_2)); + onParam(107,*p(p_op1_eg_rate_3)); + onParam(108,*p(p_op1_eg_rate_4)); + onParam(109,*p(p_op1_eg_level_1)); + onParam(110,*p(p_op1_eg_level_2)); + onParam(111,*p(p_op1_eg_level_3)); + onParam(112,*p(p_op1_eg_level_4)); + onParam(113,*p(p_op1_kbd_lev_scl_brk_pt)); + onParam(114,*p(p_op1_kbd_lev_scl_lft_depth)); + onParam(115,*p(p_op1_kbd_lev_scl_rht_depth)); + onParam(116,*p(p_op1_kbd_lev_scl_lft_curve)); + onParam(117,*p(p_op1_kbd_lev_scl_rht_curve)); + onParam(118,*p(p_op1_kbd_rate_scaling)); + onParam(119,*p(p_op1_amp_mod_sensitivity)); + onParam(120,*p(p_op1_key_vel_sensitivity)); + onParam(121,*p(p_op1_operator_output_level)); + onParam(122,*p(p_op1_osc_mode)); + onParam(123,*p(p_op1_osc_freq_coarse)); + onParam(124,*p(p_op1_osc_freq_fine)); + onParam(125,*p(p_op1_osc_detune)+7); + // Global for all OPs + onParam(126,*p(p_pitch_eg_rate_1)); + onParam(127,*p(p_pitch_eg_rate_2)); + onParam(128,*p(p_pitch_eg_rate_3)); + onParam(129,*p(p_pitch_eg_rate_4)); + onParam(130,*p(p_pitch_eg_level_1)); + onParam(131,*p(p_pitch_eg_level_2)); + onParam(132,*p(p_pitch_eg_level_3)); + onParam(133,*p(p_pitch_eg_level_4)); + onParam(134,*p(p_algorithm_num)-1); + onParam(135,*p(p_feedback)); + onParam(136,*p(p_oscillator_sync)); + onParam(137,*p(p_lfo_speed)); + onParam(138,*p(p_lfo_delay)); + onParam(139,*p(p_lfo_pitch_mod_depth)); + onParam(140,*p(p_lfo_amp_mod_depth)); + onParam(141,*p(p_lfo_sync)); + onParam(142,*p(p_lfo_waveform)); + onParam(143,*p(p_pitch_mod_sensitivity)); + onParam(144,*p(p_transpose)); + // 10 bytes (145-154) are the name of the patch + // Controllers (added at the end of the data[]) + onParam(155,*p(p_pitch_bend_range)); + onParam(156,*p(p_pitch_bend_step)); + onParam(157,*p(p_mod_wheel_range)); + onParam(158,*p(p_mod_wheel_assign)); + onParam(159,*p(p_foot_ctrl_range)); + onParam(160,*p(p_foot_ctrl_assign)); + onParam(161,*p(p_breath_ctrl_range)); + onParam(162,*p(p_breath_ctrl_assign)); + onParam(163,*p(p_aftertouch_range)); + onParam(164,*p(p_aftertouch_assign)); + onParam(165,*p(p_master_tune)); + onParam(166,*p(p_op1_enable)); + onParam(167,*p(p_op2_enable)); + onParam(168,*p(p_op3_enable)); + onParam(169,*p(p_op4_enable)); + onParam(170,*p(p_op5_enable)); + onParam(171,*p(p_op6_enable)); + onParam(172,*p(p_number_of_voices)); + + if(_param_change_counter>PARAM_CHANGE_LEVEL) + { + panic(); + controllers.refresh(); + } + + //TRACE("Bye"); +} + +// override the run() method +void Dexed::run (uint32_t sample_count) +{ + const LV2_Atom_Sequence* seq = p (p_midi_in); + float* output = p(p_audio_out); + uint32_t last_frame = 0, num_this_time = 0; + bool drop_next_events=false; + + Plugin::run(sample_count); + + if(++_k_rate_counter%16) + set_params(); // pre_process: copy actual voice params + + for (LV2_Atom_Event* ev = lv2_atom_sequence_begin (&seq->body); + !lv2_atom_sequence_is_end(&seq->body, seq->atom.size, ev); + ev = lv2_atom_sequence_next (ev)) + { + num_this_time = ev->time.frames - last_frame; + + // If it's midi, send it to the engine + if (ev->body.type == m_midi_type) + { + drop_next_events|=ProcessMidiMessage((uint8_t*) LV2_ATOM_BODY (&ev->body),ev->body.size); + if(drop_next_events==true) + continue; + } + + // render audio from the last frame until the timestamp of this event + GetSamples (num_this_time, outbuf_); + + // i is the index of the engine's buf, which always starts at 0 (i think) + // j is the index of the plugin's float output buffer which will be the timestamp + // of the last processed atom event. + for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j) + output[j]=outbuf_[i]; + + last_frame = ev->time.frames; + } + + // render remaining samples if any left + if (last_frame < sample_count) + { + // do the same thing as above except from last frame until the end of + // the processing cycles last sample. at this point, all events have + // already been handled. + + num_this_time = sample_count - last_frame; + GetSamples (num_this_time, outbuf_); + for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j) + output[j] = outbuf_[i]; + } + + fx.process(output, sample_count); +} + +void Dexed::GetSamples(uint32_t n_samples, float* buffer) +{ + uint32_t i; + + if(refreshVoice) { + for(i=0;i < max_notes;i++) { + if ( voices[i].live ) + voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity); + } + lfo.reset(data+137); + refreshVoice = false; + } + + // flush first events + for (i=0; i < n_samples && i < extra_buf_size_; i++) { + buffer[i] = extra_buf_[i]; + } + + // remaining buffer is still to be processed + if (extra_buf_size_ > n_samples) { + for (uint32_t j = 0; j < extra_buf_size_ - n_samples; j++) { + extra_buf_[j] = extra_buf_[j + n_samples]; + } + extra_buf_size_ -= n_samples; + } + else + { + for (; i < n_samples; i += N) { + AlignedBuf audiobuf; + float sumbuf[N]; + + for (uint32_t j = 0; j < N; ++j) { + audiobuf.get()[j] = 0; + sumbuf[j] = 0.0; + } + + int32_t lfovalue = lfo.getsample(); + int32_t lfodelay = lfo.getdelay(); + + for (uint8_t note = 0; note < max_notes; ++note) { + if (voices[note].live) { + voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); + for (uint32_t j=0; j < N; ++j) { + int32_t val = audiobuf.get()[j]; + val = val >> 4; + int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; + float f = static_cast(clip_val>>1)/0x8000; + if(f>1) f=1; + if(f<-1) f=-1; + sumbuf[j]+=f; + audiobuf.get()[j]=0; + } + } + } + + uint32_t jmax = n_samples - i; + for (uint32_t j = 0; j < N; ++j) { + if (j < jmax) + buffer[i + j] = sumbuf[j]; + else + extra_buf_[j - jmax] = sumbuf[j]; + } + } + extra_buf_size_ = i - n_samples; + } + + if(++_k_rate_counter%32 && !monoMode) + { + uint8_t op_carrier=controllers.core->get_carrier_operators(data[134]); // look for carriers + + for(i=0;i < max_notes;i++) + { + if(voices[i].live==true) + { + uint8_t op_amp=0; + uint8_t op_carrier_num=0; + + voices[i].dx7_note->peekVoiceStatus(voiceStatus); + + for(uint8_t op=0;op<6;op++) + { + uint8_t op_bit=static_cast(pow(2,op)); + + if((op_carrier&op_bit)>0) + { + // this voice is a carrier! + op_carrier_num++; + + //TRACE("Voice[%2d] OP [%d] amp=%ld,amp_step=%d,pitch_step=%d",i,op,voiceStatus.amp[op],voiceStatus.ampStep[op],voiceStatus.pitchStep); + + if(voiceStatus.amp[op]<=1069 && voiceStatus.ampStep[op]==4) // this voice produces no audio output + op_amp++; + } + } + if(op_amp==op_carrier_num) + { + // all carrier-operators are silent -> disable the voice + voices[i].live=false; + voices[i].sustained=false; + voices[i].keydown=false; + TRACE("Shutted down Voice[%2d]",i); + } + } +// TRACE("Voice[%2d] live=%d keydown=%d",i,voices[i].live,voices[i].keydown); + } + } +} + +bool Dexed::ProcessMidiMessage(const uint8_t *buf, uint32_t buf_size) { + TRACE("Hi"); + + uint8_t cmd = buf[0]; + + switch(cmd & 0xf0) { + case 0x80 : + TRACE("MIDI keyup event: %d",buf[1]); + keyup(buf[1]); + return(false); + break; + case 0x90 : + TRACE("MIDI keydown event: %d %d",buf[1],buf[2]); + keydown(buf[1], buf[2]); + return(false); + break; + case 0xb0 : { + uint8_t ctrl = buf[1]; + uint8_t value = buf[2]; + + switch(ctrl) { + case 1: + TRACE("MIDI modwheel event: %d %d",ctrl,value); + controllers.modwheel_cc = value; + controllers.refresh(); + break; + case 2: + TRACE("MIDI breath event: %d %d",ctrl,value); + controllers.breath_cc = value; + controllers.refresh(); + break; + case 4: + TRACE("MIDI footsw event: %d %d",ctrl,value); + controllers.foot_cc = value; + controllers.refresh(); + break; + case 64: + TRACE("MIDI sustain event: %d %d",ctrl,value); + sustain = value > 63; + if (!sustain) { + for (uint8_t note = 0; note < max_notes; note++) { + if (voices[note].sustained && !voices[note].keydown) { + voices[note].dx7_note->keyup(); + voices[note].sustained = false; + } + } + } + break; + case 120: + TRACE("MIDI all-sound-off: %d %d",ctrl,value); + panic(); + return(true); + break; + case 123: + TRACE("MIDI all-notes-off: %d %d",ctrl,value); + notes_off(); + return(true); + break; + } + break; + } + +// case 0xc0 : +// setCurrentProgram(buf[1]); +// break; + + // channel aftertouch + case 0xd0 : + TRACE("MIDI aftertouch 0xd0 event: %d %d",buf[1]); + controllers.aftertouch_cc = buf[1]; + controllers.refresh(); + break; + // pitchbend + case 0xe0 : + TRACE("MIDI pitchbend 0xe0 event: %d %d",buf[1],buf[2]); + controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7); + break; + + default: + TRACE("MIDI event unknown: cmd=%d, val1=%d, val2=%d",buf[0],buf[1],buf[2]); + break; + } + + TRACE("Bye"); + return(false); +} + +void Dexed::keydown(uint8_t pitch, uint8_t velo) { +TRACE("Hi"); +TRACE("pitch=%d, velo=%d\n",pitch,velo); + if ( velo == 0 ) { + keyup(pitch); + return; + } + + pitch += data[144] - 24; + + uint8_t note = currentNote; + uint8_t keydown_counter=0; + + for (uint8_t i=0; iinit(data, pitch, velo); + if ( data[136] ) + voices[note].dx7_note->oscSync(); + break; + } + else + keydown_counter++; + + note = (note + 1) % max_notes; + } + + if(keydown_counter==0) + lfo.keydown(); + if ( monoMode ) { + for(uint8_t i=0; itransferSignal(*voices[i].dx7_note); + break; + } + if ( voices[i].midi_note < pitch ) { + voices[i].live = false; + voices[note].dx7_note->transferState(*voices[i].dx7_note); + break; + } + return; + } + } + } + + voices[note].live = true; +TRACE("Bye"); +} + +void Dexed::keyup(uint8_t pitch) { +TRACE("Hi"); +TRACE("pitch=%d\n",pitch); + + pitch += data[144] - 24; + + uint8_t note; + for (note=0; note= max_notes ) { + TRACE("note-off not found???"); + return; + } + + if ( monoMode ) { + int8_t highNote = -1; + int8_t target = 0; + for (int8_t i=0; i highNote ) { + target = i; + highNote = voices[i].midi_note; + } + } + + if ( highNote != -1 && voices[note].live ) { + voices[note].live = false; + voices[target].live = true; + voices[target].dx7_note->transferState(*voices[note].dx7_note); + } + } + + if ( sustain ) { + voices[note].sustained = true; + } else { + voices[note].dx7_note->keyup(); + } +TRACE("Bye"); +} + +void Dexed::onParam(uint8_t param_num,float param_val) +{ + int32_t tune; + + if(param_val!=data_float[param_num]) + { + TRACE("Parameter %d change from %f to %f",param_num, data_float[param_num], param_val); +#ifdef DEBUG + uint8_t tmp=data[param_num]; +#endif + + _param_change_counter++; + + if(param_num==144 || param_num==134 || param_num==172) + panic(); + + refreshVoice=true; + data[param_num]=static_cast(param_val); + data_float[param_num]=param_val; + + switch(param_num) + { + case 155: + controllers.values_[kControllerPitchRange]=data[param_num]; + break; + case 156: + controllers.values_[kControllerPitchStep]=data[param_num]; + break; + case 157: + controllers.wheel.setRange(data[param_num]); + controllers.wheel.setTarget(data[param_num+1]); + controllers.refresh(); + break; + case 158: + controllers.wheel.setRange(data[param_num-1]); + controllers.wheel.setTarget(data[param_num]); + controllers.refresh(); + break; + case 159: + controllers.foot.setRange(data[param_num]); + controllers.foot.setTarget(data[param_num+1]); + controllers.refresh(); + break; + case 160: + controllers.foot.setRange(data[param_num-1]); + controllers.foot.setTarget(data[param_num]); + controllers.refresh(); + break; + case 161: + controllers.breath.setRange(data[param_num]); + controllers.breath.setTarget(data[param_num+1]); + controllers.refresh(); + break; + case 162: + controllers.breath.setRange(data[param_num-1]); + controllers.breath.setTarget(data[param_num]); + controllers.refresh(); + break; + case 163: + controllers.at.setRange(data[param_num]); + controllers.at.setTarget(data[param_num+1]); + controllers.refresh(); + break; + case 164: + controllers.at.setRange(data[param_num-1]); + controllers.at.setTarget(data[param_num]); + controllers.refresh(); + break; + case 165: + tune=param_val*0x4000; + controllers.masterTune=(tune<<11)*(1.0/12); + break; + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + controllers.opSwitch=(data[166]<<5)|(data[167]<<4)|(data[168]<<3)|(data[169]<<2)|(data[170]<<1)|data[171]; + break; + case 172: + max_notes=data[param_num]; + break; + } + + TRACE("Done: Parameter %d changed from %d to %d",param_num, tmp, data[param_num]); + } +} + +uint8_t Dexed::getEngineType() { + return engineType; +} + +void Dexed::setEngineType(uint8_t tp) { + TRACE("settings engine %d", tp); + + if(engineType==tp && controllers.core!=NULL) + return; + + switch (tp) { + case DEXED_ENGINE_MARKI: + TRACE("DEXED_ENGINE_MARKI:%d",DEXED_ENGINE_MARKI); + controllers.core = engineMkI; + break; + case DEXED_ENGINE_OPL: + TRACE("DEXED_ENGINE_OPL:%d",DEXED_ENGINE_OPL); + controllers.core = engineOpl; + break; + default: + TRACE("DEXED_ENGINE_MODERN:%d",DEXED_ENGINE_MODERN); + controllers.core = engineMsfa; + tp=DEXED_ENGINE_MODERN; + break; + } + engineType = tp; + panic(); + controllers.refresh(); +} + +bool Dexed::isMonoMode(void) { + return monoMode; +} + +void Dexed::setMonoMode(bool mode) { + if(monoMode==mode) + return; + + monoMode = mode; +} + +void Dexed::panic(void) { + for(uint8_t i=0;ioscSync(); + } + } + } +} + +void Dexed::notes_off(void) { + for(uint8_t i=0;i> 3; @@ -138,7 +144,7 @@ Dx7Note::Dx7Note() { } } -void Dx7Note::init(const uint8_t patch[167], int midinote, int velocity) { +void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { int rates[4]; int levels[4]; for (int op = 0; op < 6; op++) { @@ -164,6 +170,7 @@ void Dx7Note::init(const uint8_t patch[167], int midinote, int velocity) { int fine = patch[off + 19]; int detune = patch[off + 20]; int32_t freq = osc_freq(midinote, mode, coarse, fine, detune); + opMode[op] = mode; basepitch_[op] = freq; ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; } @@ -186,30 +193,31 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); int32_t pmod_1 = (((int64_t) pmd) * (int64_t) senslfo) >> 39; pmod_1 = abs(pmod_1); - int32_t pmod_2 = ((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14; + int32_t pmod_2 = (int32_t)(((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14); pmod_2 = abs(pmod_2); int32_t pitch_mod = max(pmod_1, pmod_2); pitch_mod = pitchenv_.getsample() + (pitch_mod * (senslfo < 0 ? -1 : 1)); - + // ---- PITCH BEND ---- int pitchbend = ctrls->values_[kControllerPitch]; int32_t pb = (pitchbend - 0x2000); if (pb != 0) { if (ctrls->values_[kControllerPitchStep] == 0) { - pb = ((float) (pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0; + pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRange]) / 12.0; } else { int stp = 12 / ctrls->values_[kControllerPitchStep]; pb = pb * stp / 8191; pb = (pb * (8191 / stp)) << 11; } } - pitch_mod += pb; - pitch_mod += ctrls->masterTune; - + int32_t pitch_base = pb + ctrls->masterTune; + pitch_mod += pitch_base; + // ==== AMP MOD ==== - uint32_t amod_1 = ((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8; // Q24 :D - amod_1 = ((int64_t) amod_1 * (int64_t) lfo_val) >> 24; - uint32_t amod_2 = ((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7; // Q?? :| + lfo_val = (1<<24) - lfo_val; + uint32_t amod_1 = (uint32_t)(((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8); // Q24 :D + amod_1 = (uint32_t)(((int64_t) amod_1 * (int64_t) lfo_val) >> 24); + uint32_t amod_2 = (uint32_t)(((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7); // Q?? :| uint32_t amd_mod = max(amod_1, amod_2); // ==== EG AMP MOD ==== @@ -218,21 +226,25 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co // ==== OP RENDER ==== for (int op = 0; op < 6; op++) { - //if ( ctrls->opSwitch[op] == '0' ) { - if (!(ctrls->opSwitch & (1<opSwitch[op] == '0' ) { + if (!(ctrls->opSwitch & (1<> 24; + uint32_t sensamp = (uint32_t)(((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24); // TODO: mehhh.. this needs some real tuning. uint32_t pt = exp(((float)sensamp)/262144 * 0.07 + 12.2); - uint32_t ldiff = ((uint64_t)level) * (((uint64_t)pt<<4)) >> 28; + uint32_t ldiff = (uint32_t)(((uint64_t)level) * (((uint64_t)pt<<4)) >> 28); level -= ldiff; } params_[op].level_in = level; @@ -259,10 +271,11 @@ void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) { int detune = patch[off + 20]; basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune); ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; + opMode[op] = mode; for (int i = 0; i < 4; i++) { rates[i] = patch[off + i]; - levels[i] = patch[off + 4 + i]; + levels[i] = patch[off + 4 + i]; } int outlevel = patch[off + 16]; outlevel = Env::scaleoutlevel(outlevel); @@ -316,4 +329,3 @@ void Dx7Note::oscSync() { params_[i].phase = 0; } } - diff --git a/dx7note.h b/dx7note.h index 3636797..da0120e 100644 --- a/dx7note.h +++ b/dx7note.h @@ -37,8 +37,7 @@ struct VoiceStatus { class Dx7Note { public: Dx7Note(); - //void init(const uint8_t patch[156], int midinote, int velocity, int fb_depth); - void init(const uint8_t patch[160], int midinote, int velocity); + void init(const uint8_t patch[156], int midinote, int velocity); // Note: this _adds_ to the buffer. Interesting question whether it's // worth it... @@ -67,6 +66,7 @@ private: int32_t fb_buf_[2]; int32_t fb_shift_; int32_t ampmodsens_[6]; + int32_t opMode[6]; int ampmoddepth_; int algorithm_; diff --git a/env.cc b/env.cc new file mode 100644 index 0000000..58478a3 --- /dev/null +++ b/env.cc @@ -0,0 +1,188 @@ +/* + * Copyright 2017 Pascal Gauthier. + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "synth.h" +#include "env.h" + +//using namespace std; + +uint32_t Env::sr_multiplier = (1<<24); + +const int levellut[] = { + 0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46 +}; + +#ifdef ACCURATE_ENVELOPE +const int statics[] = { + 1764000, 1764000, 1411200, 1411200, 1190700, 1014300, 992250, + 882000, 705600, 705600, 584325, 507150, 502740, 441000, 418950, + 352800, 308700, 286650, 253575, 220500, 220500, 176400, 145530, + 145530, 125685, 110250, 110250, 88200, 88200, 74970, 61740, + 61740, 55125, 48510, 44100, 37485, 31311, 30870, 27562, 27562, + 22050, 18522, 17640, 15435, 14112, 13230, 11025, 9261, 9261, 7717, + 6615, 6615, 5512, 5512, 4410, 3969, 3969, 3439, 2866, 2690, 2249, + 1984, 1896, 1808, 1411, 1367, 1234, 1146, 926, 837, 837, 705, + 573, 573, 529, 441, 441 + // and so on, I stopped measuring after R=76 (needs to be double-checked anyway) +}; +#endif + +void Env::init_sr(double sampleRate) { + sr_multiplier = (44100.0 / sampleRate) * (1<<24); +} + +void Env::init(const int r[4], const int l[4], int ol, int rate_scaling) { + for (int i = 0; i < 4; i++) { + rates_[i] = r[i]; + levels_[i] = l[i]; + } + outlevel_ = ol; + rate_scaling_ = rate_scaling; + level_ = 0; + down_ = true; + advance(0); +} + +int32_t Env::getsample() { +#ifdef ACCURATE_ENVELOPE + if (staticcount_) { + staticcount_ -= N; + if (staticcount_ <= 0) { + staticcount_ = 0; + advance(ix_ + 1); + } + } +#endif + + if (ix_ < 3 || ((ix_ < 4) && !down_)) { + if (rising_) { + const int jumptarget = 1716; + if (level_ < (jumptarget << 16)) { + level_ = jumptarget << 16; + } + level_ += (((17 << 24) - level_) >> 24) * inc_; + // TODO: should probably be more accurate when inc is large + if (level_ >= targetlevel_) { + level_ = targetlevel_; + advance(ix_ + 1); + } + } + else if (staticcount_) { + ; + } + else { // !rising + level_ -= inc_; + if (level_ <= targetlevel_) { + level_ = targetlevel_; + advance(ix_ + 1); + } + } + } + // TODO: this would be a good place to set level to 0 when under threshold + return level_; +} + +void Env::keydown(bool d) { + if (down_ != d) { + down_ = d; + advance(d ? 0 : 3); + } +} + +int Env::scaleoutlevel(int outlevel) { + return outlevel >= 20 ? 28 + outlevel : levellut[outlevel]; +} + +void Env::advance(int newix) { + ix_ = newix; + if (ix_ < 4) { + int newlevel = levels_[ix_]; + int actuallevel = scaleoutlevel(newlevel) >> 1; + actuallevel = (actuallevel << 6) + outlevel_ - 4256; + actuallevel = actuallevel < 16 ? 16 : actuallevel; + // level here is same as Java impl + targetlevel_ = actuallevel << 16; + rising_ = (targetlevel_ > level_); + + // rate + int qrate = (rates_[ix_] * 41) >> 6; + qrate += rate_scaling_; + qrate = min(qrate, 63); + +#ifdef ACCURATE_ENVELOPE + if (targetlevel_ == level_) { + // approximate number of samples at 44.100 kHz to achieve the time + // empirically gathered using 2 TF1s, could probably use some double-checking + // and cleanup, but it's pretty close for now. + int staticrate = rates_[ix_]; + staticrate += rate_scaling_; // needs to be checked, as well, but seems correct + staticrate = min(staticrate, 99); + staticcount_ = staticrate < 77 ? statics[staticrate] : 20 * (99 - staticrate); + staticcount_ = (int)(((int64_t)staticcount_ * (int64_t)sr_multiplier) >> 24); + } + else { + staticcount_ = 0; + } +#endif + inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2)); + // meh, this should be fixed elsewhere + inc_ = (int)(((int64_t)inc_ * (int64_t)sr_multiplier) >> 24); + } +} + +void Env::update(const int r[4], const int l[4], int ol, int rate_scaling) { + for (int i = 0; i < 4; i++) { + rates_[i] = r[i]; + levels_[i] = l[i]; + } + outlevel_ = ol; + rate_scaling_ = rate_scaling; + if ( down_ ) { + // for now we simply reset ourselve at level 3 + int newlevel = levels_[2]; + int actuallevel = scaleoutlevel(newlevel) >> 1; + actuallevel = (actuallevel << 6) - 4256; + actuallevel = actuallevel < 16 ? 16 : actuallevel; + targetlevel_ = actuallevel << 16; + advance(2); + } +} + +void Env::getPosition(char *step) { + *step = ix_; +} + +void Env::transfer(Env &src) { + for(int i=0;i<4;i++) { + rates_[i] = src.rates_[i]; + levels_[i] = src.levels_[i]; + } + outlevel_ = src.outlevel_; + rate_scaling_ = src.rate_scaling_; + level_ = src.level_; + targetlevel_ = src.targetlevel_; + rising_= src.rising_; + ix_ = src.ix_; + down_ = src.down_; +#ifdef ACCURATE_ENVELOPE + staticcount_ = src.staticcount_; +#endif + inc_ = src.inc_; +} + diff --git a/env.h b/env.h index 76227df..1285412 100644 --- a/env.h +++ b/env.h @@ -1,4 +1,5 @@ /* + * Copyright 2017 Pascal Gauthier. * Copyright 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +22,8 @@ // DX7 envelope generation +#define ACCURATE_ENVELOPE + class Env { public: @@ -33,7 +36,6 @@ class Env { void update(const int rates[4], const int levels[4], int32_t outlevel, int rate_scaling); - // Result is in Q24/doubling log format. Also, result is subsampled // for every N samples. // A couple more things need to happen for this to be used as a gain @@ -50,10 +52,11 @@ class Env { void transfer(Env &src); private: + // PG: This code is normalized to 44100, need to put a multiplier // if we are not using 44100. static uint32_t sr_multiplier; - + int rates_[4]; int levels_[4]; int outlevel_; @@ -66,6 +69,9 @@ class Env { bool rising_; int ix_; int inc_; +#ifdef ACCURATE_ENVELOPE + int staticcount_; +#endif bool down_; diff --git a/fm_core.cpp b/fm_core.cpp index 51f548a..102e52d 100644 --- a/fm_core.cpp +++ b/fm_core.cpp @@ -23,6 +23,7 @@ #include "fm_op_kernel.h" #include "fm_core.h" + //using namespace std; const FmAlgorithm FmCore::algorithms[32] = { @@ -77,6 +78,8 @@ uint8_t FmCore::get_carrier_operators(uint8_t algorithm) { if((alg.ops[i]&OUT_BUS_ADD)==OUT_BUS_ADD) op_out|=1<buf_[2]; const static FmAlgorithm algorithms[32]; diff --git a/fm_op_kernel.cc b/fm_op_kernel.cc new file mode 100644 index 0000000..20a61a8 --- /dev/null +++ b/fm_op_kernel.cc @@ -0,0 +1,283 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#ifdef HAVE_NEON +#include +#endif + +#include "synth.h" +#include "sin.h" +#include "fm_op_kernel.h" + +#ifdef HAVE_NEONx +static bool hasNeon() { + return true; + return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; +} + +extern "C" +void neon_fm_kernel(const int *in, const int *busin, int *out, int count, + int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); + +const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; + +#else +static bool hasNeon() { + return false; +} +#endif + +void FmOpKernel::compute(int32_t *output, const int32_t *input, + int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; + if (hasNeon()) { +#ifdef HAVE_NEON + neon_fm_kernel(input, add ? output : zeros, output, _N_, + phase0, freq, gain, dgain); +#endif + } else { + if (add) { + for (int i = 0; i < _N_; i++) { + gain += dgain; + int32_t y = Sin::lookup(phase + input[i]); + int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; + output[i] += y1; + phase += freq; + } + } else { + for (int i = 0; i < _N_; i++) { + gain += dgain; + int32_t y = Sin::lookup(phase + input[i]); + int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; + output[i] = y1; + phase += freq; + } + } + } +} + +void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; + if (hasNeon()) { +#ifdef HAVE_NEON + neon_fm_kernel(zeros, add ? output : zeros, output, _N_, + phase0, freq, gain, dgain); +#endif + } else { + if (add) { + for (int i = 0; i < _N_; i++) { + gain += dgain; + int32_t y = Sin::lookup(phase); + int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; + output[i] += y1; + phase += freq; + } + } else { + for (int i = 0; i < _N_; i++) { + gain += dgain; + int32_t y = Sin::lookup(phase); + int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; + output[i] = y1; + phase += freq; + } + } + } +} + +#define noDOUBLE_ACCURACY +#define HIGH_ACCURACY + +void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, + int32_t *fb_buf, int fb_shift, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; + int32_t y0 = fb_buf[0]; + int32_t y = fb_buf[1]; + if (add) { + for (int i = 0; i < _N_; i++) { + gain += dgain; + int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); + y0 = y; + y = Sin::lookup(phase + scaled_fb); + y = ((int64_t)y * (int64_t)gain) >> 24; + output[i] += y; + phase += freq; + } + } else { + for (int i = 0; i < _N_; i++) { + gain += dgain; + int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); + y0 = y; + y = Sin::lookup(phase + scaled_fb); + y = ((int64_t)y * (int64_t)gain) >> 24; + output[i] = y; + phase += freq; + } + } + fb_buf[0] = y0; + fb_buf[1] = y; +} + +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////// + +// Experimental sine wave generators below +#if 0 +// Results: accuracy 64.3 mean, 170 worst case +// high accuracy: 5.0 mean, 49 worst case +void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; +#ifdef HIGH_ACCURACY + int32_t u = Sin::compute10(phase << 6); + u = ((int64_t)u * gain) >> 30; + int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle + v = ((int64_t)v * gain) >> 30; + int32_t s = Sin::compute10(freq << 6); + int32_t c = Sin::compute10((freq << 6) + (1 << 28)); +#else + int32_t u = Sin::compute(phase); + u = ((int64_t)u * gain) >> 24; + int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle + v = ((int64_t)v * gain) >> 24; + int32_t s = Sin::compute(freq) << 6; + int32_t c = Sin::compute(freq + (1 << 22)) << 6; +#endif + for (int i = 0; i < _N_; i++) { + output[i] = u; + int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; + u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; + v = t; + } +} +#endif + +#if 0 +// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5) +// for freq < 0.25, 275.2 mean, 716 worst +// high accuracy: 57.4 mean, 7559 worst +// freq < 0.25: 17.9 mean, 78 worst +void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; +#ifdef HIGH_ACCURACY + int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); + int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); + int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); +#else + int32_t u = Sin::compute(phase); + u = ((int64_t)u * gain) >> 24; + int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); + v = ((int64_t)v * gain) >> 24; + int32_t a = Sin::compute(freq >> 1) << 1; +#endif + for (int i = 0; i < _N_; i++) { + output[i] = u; + v -= ((int64_t)a * (int64_t)u) >> 24; + u += ((int64_t)a * (int64_t)v) >> 24; + } +} +#endif + +#if 0 +// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5) +// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0) +// with high accuracy: mean 4.2, worst 292 (near freq = 0.5) +void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; +#ifdef DOUBLE_ACCURACY + int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); + double a_d = sin(freq * (M_PI / (1 << 24))); + int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * + (M_PI / (1 << 23))) + 0.5); + int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); +#else +#ifdef HIGH_ACCURACY + int32_t u = Sin::compute10(phase << 6); + int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); + int32_t a = Sin::compute10(freq << 5); + v = ((int64_t)v * (int64_t)a) >> 29; + int32_t aa = ((int64_t)a * (int64_t)a) >> 29; +#else + int32_t u = Sin::compute(phase) << 6; + int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); + int32_t a = Sin::compute(freq >> 1); + v = ((int64_t)v * (int64_t)a) >> 17; + int32_t aa = ((int64_t)a * (int64_t)a) >> 17; +#endif +#endif + + if (aa < 0) aa = (1 << 31) - 1; + for (int i = 0; i < _N_; i++) { + gain += dgain; + output[i] = ((int64_t)u * (int64_t)gain) >> 30; + v -= ((int64_t)aa * (int64_t)u) >> 29; + u += v; + } +} +#endif + +#if 0 +// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5) +// high accuracy 2.9 mean, 143 worst +void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, + int32_t gain1, int32_t gain2, bool add) { + int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; + int32_t gain = gain1; + int32_t phase = phase0; +#ifdef HIGH_ACCURACY + int32_t u = Sin::compute10(phase << 6); + int32_t lastu = Sin::compute10((phase - freq) << 6); + int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; +#else + int32_t u = Sin::compute(phase) << 6; + int32_t lastu = Sin::compute(phase - freq) << 6; + int32_t a = Sin::compute(freq + (1 << 22)) << 7; +#endif + if (a < 0 && freq < 256) a = (1 << 31) - 1; + if (a > 0 && freq > 0x7fff00) a = -(1 << 31); + for (int i = 0; i < _N_; i++) { + gain += dgain; + output[i] = ((int64_t)u * (int64_t)gain) >> 30; + //output[i] = u; + int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; + lastu = u; + u = newu; + } +} +#endif + diff --git a/fm_op_kernel.cpp b/fm_op_kernel.cpp index 6b36de2..83c8f3b 100644 --- a/fm_op_kernel.cpp +++ b/fm_op_kernel.cpp @@ -52,7 +52,7 @@ void FmOpKernel::compute(int32_t *output, const int32_t *input, int32_t phase = phase0; if (hasNeon()) { #ifdef HAVE_NEON - neon_fm_kernel(input, add ? output : zeros, output, N, + neon_fm_kernel(input, add ? output : zeros, output, _N_, phase0, freq, gain, dgain); #endif } else { @@ -172,7 +172,7 @@ void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t s = Sin::compute(freq) << 6; int32_t c = Sin::compute(freq + (1 << 22)) << 6; #endif - for (int i = 0; i < N; i++) { + for (int i = 0; i < _N_; i++) { output[i] = u; int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; @@ -270,7 +270,7 @@ void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, #endif if (a < 0 && freq < 256) a = (1 << 31) - 1; if (a > 0 && freq > 0x7fff00) a = -(1 << 31); - for (int i = 0; i < N; i++) { + for (int i = 0; i < _N_; i++) { gain += dgain; output[i] = ((int64_t)u * (int64_t)gain) >> 30; //output[i] = u; diff --git a/lfo.cpp b/lfo.cpp index 046e67c..1486811 100644 --- a/lfo.cpp +++ b/lfo.cpp @@ -95,4 +95,3 @@ void Lfo::keydown() { } delaystate_ = 0; } - diff --git a/synth.h b/synth.h index b1166c3..2dfda9f 100644 --- a/synth.h +++ b/synth.h @@ -47,7 +47,6 @@ typedef __int16 SInt16; #define SynthMemoryBarrier() #endif -/* template inline static T min(const T& a, const T& b) { return a < b ? a : b; @@ -57,13 +56,6 @@ template inline static T max(const T& a, const T& b) { return a > b ? a : b; } -*/ - -#undef max -#define max(a,b) ((a)>(b)?(a):(b)) -#undef min -#define min(a,b) ((a)<(b)?(a):(b)) - #define QER(n,b) ( ((float)n)/(1<