diff --git a/MicroDexed.ino b/MicroDexed.ino index 38069a8..120913b 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -43,7 +43,7 @@ #include "UI.hpp" // Audio engines -AudioSourceMicroDexed* MicroDexed[NUM_DEXED]; +AudioSynthDexed* MicroDexed[NUM_DEXED]; #if defined(USE_FX) AudioSynthWaveform* chorus_modulator[NUM_DEXED]; #if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT @@ -173,7 +173,7 @@ AudioConnection * dynamicConnections[NUM_DEXED * 4]; #endif void create_audio_engine_chain(uint8_t instance_id) { - MicroDexed[instance_id] = new AudioSourceMicroDexed(SAMPLE_RATE); + MicroDexed[instance_id] = new AudioSynthDexed(SAMPLE_RATE); mono2stereo[instance_id] = new AudioEffectMonoStereo(); #if defined(USE_FX) chorus_modulator[instance_id] = new AudioSynthWaveform(); diff --git a/UI.hpp b/UI.hpp index 582c367..9b4d0a9 100644 --- a/UI.hpp +++ b/UI.hpp @@ -70,7 +70,7 @@ extern void change_disp_sd(bool d); #endif extern AudioControlSGTL5000 sgtl5000_1; -extern AudioSourceMicroDexed* MicroDexed[NUM_DEXED]; +extern AudioSynthDexed* MicroDexed[NUM_DEXED]; #if defined(USE_FX) extern AudioSynthWaveform* chorus_modulator[NUM_DEXED]; extern AudioEffectModulatedDelay* modchorus[NUM_DEXED]; diff --git a/dexed_sd.h b/dexed_sd.h index f2ead00..4a00eb9 100644 --- a/dexed_sd.h +++ b/dexed_sd.h @@ -30,7 +30,7 @@ extern uint8_t sd_card; extern Dexed* dexed; -extern AudioSourceMicroDexed * MicroDexed[NUM_DEXED]; +extern AudioSynthDexed * MicroDexed[NUM_DEXED]; extern void show_patch(uint8_t instance_id); extern void send_sysex_voice(uint8_t midi_channel, uint8_t* data); diff --git a/synth_dexed.cpp b/synth_dexed.cpp index ab80013..519fe7c 100644 --- a/synth_dexed.cpp +++ b/synth_dexed.cpp @@ -1,4 +1,5 @@ #include "synth_dexed.h" + /** Copyright (c) 2013-2014 Pascal Gauthier. @@ -388,13 +389,76 @@ void Dexed::getSamples(uint16_t n_samples, int16_t* buffer) else if (vuSignal > 0.001f) vuSignal *= decayFactor; else - vuSignal = 0; + vuSignal = 0.0; } //arm_scale_f32(sumbuf, 0.00015, sumbuf, AUDIO_BLOCK_SAMPLES); arm_float_to_q15(sumbuf, buffer, AUDIO_BLOCK_SAMPLES); } +void Dexed::getSamples(uint16_t n_samples, float32_t* buffer) +{ + uint16_t i, j; + uint8_t note; + float s; + const double decayFactor = 0.99992; + + 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, voices[i].porta, &controllers); + } + lfo.reset(data + 137); + refreshVoice = false; + } + + for (i = 0; i < n_samples; i += _N_) + { + AlignedBuf audiobuf; + + for (uint8_t j = 0; j < _N_; ++j) + { + audiobuf.get()[j] = 0; + buffer[i + j] = 0.0; + } + + int32_t lfovalue = lfo.getsample(); + int32_t lfodelay = lfo.getdelay(); + + for (note = 0; note < max_notes; note++) + { + if (voices[note].live) + { + voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); + + for (j = 0; j < _N_; ++j) + { + buffer[i + j] += static_cast(audiobuf.get()[j]) / 32768; + audiobuf.get()[j] = 0; + } + } + } + } + + fx.process(buffer, n_samples); // Needed for fx.Gain()!!! + + // mild compression + for (i = 0; i < n_samples; i++) + { + s = abs(buffer[i]); + if (s > vuSignal) + vuSignal = s; + else if (vuSignal > 0.001f) + vuSignal *= decayFactor; + else + vuSignal = 0; + } + + arm_scale_f32(buffer, 0.00015, buffer, AUDIO_BLOCK_SAMPLES); +} + void Dexed::keydown(int16_t pitch, uint8_t velo) { if ( velo == 0 ) { keyup(pitch); @@ -1591,20 +1655,20 @@ void Env::transfer(Env &src) { inc_ = src.inc_; } /* - * Copyright 2013 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. - */ + Copyright 2013 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. +*/ #ifdef _MSC_VER #define exp2(arg) pow(2.0, arg) @@ -2323,20 +2387,20 @@ void Porta::init_sr(double sampleRate) int32_t Porta::rates[128]; /* - * 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. - */ + 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. +*/ #define R (1 << 29) @@ -2386,7 +2450,7 @@ int32_t Sin::lookup(int32_t phase) { int y0 = sintab[phase_int + 1]; return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); -#else +#else int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1); int y0 = sintab[phase_int]; int y1 = sintab[phase_int + 1]; @@ -2413,9 +2477,9 @@ int32_t Sin::compute(int32_t phase) { int32_t x4 = ((int64_t)x2 * (int64_t)x2) >> 24; int32_t x6 = ((int64_t)x2 * (int64_t)x4) >> 24; int32_t y = C0 - - (((int64_t)C1 * (int64_t)x2) >> 24) + - (((int64_t)C2 * (int64_t)x4) >> 24) - - (((int64_t)C3 * (int64_t)x6) >> 24); + (((int64_t)C1 * (int64_t)x2) >> 24) + + (((int64_t)C2 * (int64_t)x4) >> 24) - + (((int64_t)C3 * (int64_t)x6) >> 24); y ^= -((phase >> 23) & 1); return y; } @@ -2433,10 +2497,10 @@ int32_t Sin::compute(int32_t phase) { int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22); int32_t x2 = ((int64_t)x * (int64_t)x) >> 16; int32_t y = (((((((((((((int64_t)C8_8 - * (int64_t)x2) >> 32) + C8_6) - * (int64_t)x2) >> 32) + C8_4) - * (int64_t)x2) >> 32) + C8_2) - * (int64_t)x2) >> 32) + C8_0); + * (int64_t)x2) >> 32) + C8_6) + * (int64_t)x2) >> 32) + C8_4) + * (int64_t)x2) >> 32) + C8_2) + * (int64_t)x2) >> 32) + C8_0); y ^= -((phase >> 23) & 1); return y; } @@ -2452,11 +2516,11 @@ int32_t Sin::compute10(int32_t phase) { int32_t x = (phase & ((1 << 29) - 1)) - (1 << 28); int32_t x2 = ((int64_t)x * (int64_t)x) >> 26; int32_t y = ((((((((((((((((int64_t)C10_10 - * (int64_t)x2) >> 34) + C10_8) - * (int64_t)x2) >> 34) + C10_6) - * (int64_t)x2) >> 34) + C10_4) - * (int64_t)x2) >> 32) + C10_2) - * (int64_t)x2) >> 30) + C10_0); + * (int64_t)x2) >> 34) + C10_8) + * (int64_t)x2) >> 34) + C10_6) + * (int64_t)x2) >> 34) + C10_4) + * (int64_t)x2) >> 32) + C10_2) + * (int64_t)x2) >> 30) + C10_0); y ^= -((phase >> 29) & 1); return y; } diff --git a/synth_dexed.h b/synth_dexed.h index 57dd096..94a2e7c 100644 --- a/synth_dexed.h +++ b/synth_dexed.h @@ -1,8 +1,12 @@ #include #include #include "config.h" +#if defined(USE_OPEN_AUDIO_LIB) +#include "OpenAudio_ArduinoLibrary.h" +#endif + /***************************************************** - * CODE; orig_code/synth.h + CODE; orig_code/synth.h *****************************************************/ /* Copyright 2012 Google Inc. @@ -76,7 +80,7 @@ inline static T max(const T& a, const T& b) { //===================================================== /***************************************************** - * CODE; orig_code/aligned_buf.h + CODE; orig_code/aligned_buf.h *****************************************************/ /* Copyright 2013 Google Inc. @@ -111,7 +115,7 @@ class AlignedBuf { //===================================================== /***************************************************** - * CODE; orig_code/sin.h + CODE; orig_code/sin.h *****************************************************/ /* Copyright 2012 Google Inc. @@ -178,7 +182,7 @@ int32_t Sin::lookup(int32_t phase) { //===================================================== /***************************************************** - * CODE; orig_code/exp2.h + CODE; orig_code/exp2.h *****************************************************/ /* Copyright 2012 Google Inc. @@ -263,16 +267,16 @@ int32_t Tanh::lookup(int32_t x) { //===================================================== /***************************************************** - * CODE; orig_code/fast_log.h + CODE; orig_code/fast_log.h *****************************************************/ /* ---------------------------------------------------------------------- -* https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621 -* Fast approximation to the log2() function. It uses a two step -* process. First, it decomposes the floating-point number into -* a fractional component F and an exponent E. The fraction component -* is used in a polynomial approximation and then the exponent added -* to the result. A 3rd order polynomial is used and the result -* when computing db20() is accurate to 7.984884e-003 dB. + https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621 + Fast approximation to the log2() function. It uses a two step + process. First, it decomposes the floating-point number into + a fractional component F and an exponent E. The fraction component + is used in a polynomial approximation and then the exponent added + to the result. A 3rd order polynomial is used and the result + when computing db20() is accurate to 7.984884e-003 dB. ** ------------------------------------------------------------------- */ static float log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f}; @@ -296,7 +300,7 @@ static float log2f_approx(float X) Y *= F; Y += (*C++); Y += E; - return(Y); + return (Y); } // https://codingforspeed.com/using-faster-exponential-approximation/ @@ -309,16 +313,16 @@ inline float expf_approx(float x) { } inline float unitToDb(float unit) { - return 6.02f * log2f_approx(unit); + return 6.02f * log2f_approx(unit); } inline float dbToUnit(float db) { - return expf_approx(db * 2.302585092994046f * 0.05f); + return expf_approx(db * 2.302585092994046f * 0.05f); } //===================================================== /***************************************************** - * CODE; orig_code/freqlut.h + CODE; orig_code/freqlut.h *****************************************************/ /* Copyright 2012 Google Inc. @@ -344,7 +348,7 @@ class Freqlut { //===================================================== /***************************************************** - * CODE; orig_code/lfo.h + CODE; orig_code/lfo.h *****************************************************/ /* Copyright 2013 Google Inc. @@ -392,7 +396,7 @@ class Lfo { //===================================================== /***************************************************** - * CODE; orig_code/env.h + CODE; orig_code/env.h *****************************************************/ /* Copyright 2017 Pascal Gauthier. @@ -472,7 +476,7 @@ class Env { //===================================================== /***************************************************** - * CODE; orig_code/pitchenv.h + CODE; orig_code/pitchenv.h *****************************************************/ /* Copyright 2013 Google Inc. @@ -525,7 +529,7 @@ extern const int8_t pitchenv_tab[]; //===================================================== /***************************************************** - * CODE; orig_code/controllers.h + CODE; orig_code/controllers.h *****************************************************/ /* Copyright 2013 Google Inc. @@ -571,12 +575,12 @@ class FmMod { void setRange(uint8_t r) { - range = r < 0 || r > 99 ? 0 : r; + range = r < 0 && r > 99 ? 0 : r; } void setTarget(uint8_t assign) { - assign = assign < 0 || assign > 7 ? 0 : assign; + assign = assign < 0 && assign > 7 ? 0 : assign; pitch = assign & 1; // PITCH amp = assign & 2; // AMP eg = assign & 4; // EG @@ -664,7 +668,7 @@ class Controllers { //===================================================== /***************************************************** - * CODE; orig_code/PluginFx.h + CODE; orig_code/PluginFx.h *****************************************************/ /** @@ -738,7 +742,7 @@ class PluginFx { //===================================================== /***************************************************** - * CODE; orig_code/fm_op_kernel.h + CODE; orig_code/fm_op_kernel.h *****************************************************/ /* Copyright 2012 Google Inc. @@ -786,7 +790,7 @@ class FmOpKernel { //===================================================== /***************************************************** - * CODE; orig_code/fm_core.h + CODE; orig_code/fm_core.h *****************************************************/ /* Copyright 2012 Google Inc. @@ -839,7 +843,7 @@ class FmCore { //===================================================== /***************************************************** - * CODE; orig_code/dx7note.h + CODE; orig_code/dx7note.h *****************************************************/ /* Copyright 2016-2017 Pascal Gauthier. @@ -918,7 +922,7 @@ class Dx7Note { //===================================================== /***************************************************** - * CODE; orig_code/dexed.h + CODE; orig_code/dexed.h *****************************************************/ /* MicroDexed @@ -1043,7 +1047,7 @@ class Dexed bool isMonoMode(void); void setMonoMode(bool mode); void setRefreshMode(bool mode); - void getSamples(uint16_t n_samples, int16_t* buffer); + //void getSamples(uint16_t n_samples, int16_t* buffer); void panic(void); void notesOff(void); void resetControllers(void); @@ -1102,12 +1106,14 @@ class Dexed VoiceStatus voiceStatus; Lfo lfo; FmCore* engineMsfa; + void getSamples(uint16_t n_samples, float32_t* buffer); + void getSamples(uint16_t n_samples, int16_t* buffer); }; //===================================================== /***************************************************** - * CODE; orig_code/porta.h + CODE; orig_code/porta.h *****************************************************/ /* Copyright 2019 Jean Pierre Cimalando. @@ -1134,16 +1140,16 @@ struct Porta { //===================================================== /***************************************************** - * CODE; orig_code/source_microdexed.h + CODE; orig_code/source_microdexed.h *****************************************************/ -class AudioSourceMicroDexed : public AudioStream, public Dexed { +class AudioSynthDexed : public AudioStream, public Dexed { public: const uint16_t audio_block_time_us = 1000000 / (SAMPLE_RATE / AUDIO_BLOCK_SAMPLES); uint32_t xrun = 0; uint16_t render_time_max = 0; - AudioSourceMicroDexed(int sample_rate) : AudioStream(0, NULL), Dexed(sample_rate) { }; + AudioSynthDexed(int sample_rate) : AudioStream(0, NULL), Dexed(sample_rate) { }; void update(void) { @@ -1184,64 +1190,113 @@ class AudioSourceMicroDexed : public AudioStream, public Dexed { volatile bool in_update = false; }; +#if defined(USE_OPEN_AUDIO_LIB) +class AudioSynthDexed_F32 : public AudioStream_F32, public Dexed { + public: + const uint16_t audio_block_time_us = 1000000 / (SAMPLE_RATE / AUDIO_BLOCK_SAMPLES); + uint32_t xrun = 0; + uint16_t render_time_max = 0; + + AudioSynthDexed_F32(int sample_rate) : AudioStream_F32(0, NULL), Dexed(sample_rate) { }; + + void update(void) + { + if (in_update == true) + { + xrun++; + return; + } + else + in_update = true; + + elapsedMicros render_time; + audio_block_f32_t *lblock; + + lblock = allocate_f32(); + + if (!lblock) + { + in_update = false; + return; + } + + getSamples(AUDIO_BLOCK_SAMPLES, lblock->data); + + if (render_time > audio_block_time_us) // everything greater audio_block_time_us (2.9ms for buffer size of 128) is a buffer underrun! + xrun++; + + if (render_time > render_time_max) + render_time_max = render_time; + + AudioStream_F32::transmit(lblock, 0); + AudioStream_F32::release(lblock); + + in_update = false; + }; + + private: + volatile bool in_update = false; +}; +#endif + //===================================================== /***************************************************** - * CODE; orig_code/PluginFx.cpp + CODE; orig_code/PluginFx.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/dexed.cpp + CODE; orig_code/dexed.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/dx7note.cpp + CODE; orig_code/dx7note.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/env.cpp + CODE; orig_code/env.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/exp2.cpp + CODE; orig_code/exp2.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/fm_core.cpp + CODE; orig_code/fm_core.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/fm_op_kernel.cpp + CODE; orig_code/fm_op_kernel.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/freqlut.cpp + CODE; orig_code/freqlut.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/lfo.cpp + CODE; orig_code/lfo.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/pitchenv.cpp + CODE; orig_code/pitchenv.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/porta.cpp + CODE; orig_code/porta.cpp *****************************************************/ //===================================================== /***************************************************** - * CODE; orig_code/sin.cpp + CODE; orig_code/sin.cpp *****************************************************/ //=====================================================