From b12c2fe61956808dc79d09ffb8d2c2a42251724c Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Mon, 25 May 2020 10:51:26 +0200 Subject: [PATCH] Added simple compression for audio generation loop. --- MicroDexed.ino | 10 +-- config.h | 10 ++- dexed.cpp | 237 ++++++++++++++++++++----------------------------- dexed.h | 1 + 4 files changed, 109 insertions(+), 149 deletions(-) diff --git a/MicroDexed.ino b/MicroDexed.ino index b3c29ae..2f54967 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -135,9 +135,9 @@ AudioConnection patchCord13(stereo2mono, 1, dacOut, 1); // uint16_t nDynamic = 0; #if defined(USE_FX) && MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT -AudioConnection * dynamicConnections[NUM_DEXED * 14]; +AudioConnection * dynamicConnections[NUM_DEXED * 16]; #elif defined(USE_FX) && MOD_FILTER_OUTPUT == MOD_NO_FILTER_OUTPUT -AudioConnection * dynamicConnections[NUM_DEXED * 13]; +AudioConnection * dynamicConnections[NUM_DEXED * 15]; #else AudioConnection * dynamicConnections[NUM_DEXED * 4]; #endif @@ -177,7 +177,7 @@ void create_audio_engine_chain(uint8_t instance_id) dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 0, reverb_mixer_r, instance_id); dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 1, reverb_mixer_l, instance_id); #else - dynamicConnections[nDynamic++] = new AudioConnection(*MicoDexed[instance_id], 0, *mono2stereo[instance_id], 0); + dynamicConnections[nDynamic++] = new AudioConnection(*MicroDexed[instance_id], 0, *mono2stereo[instance_id], 0); #endif dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 0, master_mixer_r, instance_id); dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 1, master_mixer_l, instance_id); @@ -1814,9 +1814,9 @@ void _softRestart(void) float pseudo_log_curve(float value) { -// ideas from here: https://forum.pjrc.com/threads/56015-Changing-Volume-Levels-based-on-pot-wheel + // ideas from here: https://forum.pjrc.com/threads/56015-Changing-Volume-Levels-based-on-pot-wheel + - const float _pseudo_log = 1048575 / (float)(1 << 20); /* #ifdef DEBUG diff --git a/config.h b/config.h index 22b8fc9..1ed854a 100644 --- a/config.h +++ b/config.h @@ -43,6 +43,8 @@ // Tools for testing MIDI: https://github.com/gbevin/SendMIDI // https://github.com/gbevin/ReceiveMIDI // +// e.g. sendmidi dev "MicroDexed MIDI" on 80 127 && sleep 1.0 && sendmidi dev "MicroDexed MIDI" off 80 0 +// // Receiving and storing MIDI SYSEX with Linux: // amidi -p hw:2,0,0 -d -r /tmp/bkup1.syx // @@ -84,7 +86,7 @@ //************************************************************************************************* //* DEBUG OUTPUT SETTINGS //************************************************************************************************* -//#define DEBUG 1 +#define DEBUG 1 #define SERIAL_SPEED 230400 #define SHOW_XRUN 1 #define SHOW_CPU_LOAD_MSEC 5000 @@ -95,7 +97,7 @@ #define DEXED_ENGINE DEXED_ENGINE_MODERN // DEXED_ENGINE_MARKI // DEXED_ENGINE_OPL // Number of Dexed instances -#define NUM_DEXED 2 // 1 or 2 - nothing else! +#define NUM_DEXED 1 // 1 or 2 - nothing else! // FX-CHAIN ENABLE/DISABLE #define USE_FX 1 // CHORUS parameters @@ -271,9 +273,9 @@ #define MIDI_DEVICE_USB_HOST 1 #if defined(USE_FX) #if NUM_DEXED == 1 -#define MAX_NOTES 12 +#define MAX_NOTES 13 #else -#define MAX_NOTES 9 +#define MAX_NOTES 10 #endif #else #if NUM_DEXED == 1 diff --git a/dexed.cpp b/dexed.cpp index 233f15f..2653b16 100644 --- a/dexed.cpp +++ b/dexed.cpp @@ -77,6 +77,7 @@ Dexed::Dexed(int rate) controllers.opSwitch = 0x3f; // enable all operators //controllers.opSwitch=0x00; lastKeyDown = -1; + vuSignal = 0.0; lfo.reset(data + 137); @@ -115,6 +116,8 @@ void Dexed::getSamples(uint16_t n_samples, int16_t* buffer) uint16_t i, j; uint8_t note; float sumbuf[n_samples]; + float s; + const double decayFactor = 0.99992; if (refreshVoice) { @@ -148,9 +151,18 @@ void Dexed::getSamples(uint16_t n_samples, int16_t* buffer) for (j = 0; j < _N_; ++j) { - //sumbuf[i + j] += static_cast(signed_saturate_rshift(audiobuf.get()[j] >> 5, 24, 9)) / 0x8000; - sumbuf[i + j] += signed_saturate_rshift(audiobuf.get()[j] >> 5, 24, 9) / 32768.0; + sumbuf[i + j] += signed_saturate_rshift(audiobuf.get()[j] >> 4, 24, 9) / 32768.0; audiobuf.get()[j] = 0; + /* + 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 = ((float) clip_val) / (float) 0x8000; + if ( f > 1.0 ) f = 1.0; + if ( f < -1.0 ) f = -1.0; + sumbuf[j] += f; + audiobuf.get()[j] = 0; + */ } } } @@ -158,6 +170,18 @@ void Dexed::getSamples(uint16_t n_samples, int16_t* buffer) fx.process(sumbuf, n_samples); // Needed for fx.Gain()!!! + // mild compression + for (i = 0; i < n_samples; i++) + { + s = abs(sumbuf[i]); + if (s > vuSignal) + vuSignal = s; + else if (vuSignal > 0.001f) + vuSignal *= decayFactor; + else + vuSignal = 0; + } + //arm_scale_f32(sumbuf, 0.00015, sumbuf, AUDIO_BLOCK_SAMPLES); arm_float_to_q15(sumbuf, buffer, AUDIO_BLOCK_SAMPLES); } @@ -636,74 +660,6 @@ bool Dexed::loadVoiceParameters(uint8_t* new_data) return (true); } -/*bool Dexed::loadGlobalParameters(uint8_t* new_data) - { - uint8_t* p_data = data; - - controllers.values_[kControllerPitchRange] = new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PITCHBEND_RANGE]; - controllers.values_[kControllerPitchStep] = new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PITCHBEND_STEP]; - controllers.wheel.setRange(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MODWHEEL_RANGE]); - controllers.wheel.setTarget(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MODWHEEL_ASSIGN]); - controllers.foot.setRange(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_FOOTCTRL_RANGE]); - controllers.foot.setTarget(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_FOOTCTRL_ASSIGN]); - controllers.breath.setRange(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_BREATHCTRL_RANGE]); - controllers.breath.setTarget(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_BREATHCTRL_ASSIGN]); - controllers.at.setRange(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_RANGE]); - controllers.at.setTarget(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_ASSIGN]); - controllers.masterTune = (int(new_data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MASTER_TUNE] / 100.0) * 0x4000 << 11) * (1.0 / 12); - controllers.refresh(); - - setOPs((*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP1_ENABLE) << 5) | - (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP2_ENABLE) << 4) | - (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP3_ENABLE) << 3) | - (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP4_ENABLE) << 2) | - (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP5_ENABLE) << 1) | - (p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP6_ENABLE )); - setMaxNotes(*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MAX_NOTES)); - - setMaxNotes(*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MAX_NOTES)); - //panic(); - doRefreshVoice(); - //activate(); - - - #ifdef DEBUG - Serial.println(F("Global parameters loaded.")); - #endif - return (true); - }*/ - -/*bool Dexed::initGlobalParameters(void) - { - uint8_t init_data[18]; - - #ifdef DEBUG - Serial.println(F("Initializing global parameters")); - #endif - init_data[DEXED_PITCHBEND_RANGE] = 1; - init_data[DEXED_PITCHBEND_STEP] = 1; - init_data[DEXED_MODWHEEL_RANGE] = 99; - init_data[DEXED_MODWHEEL_ASSIGN] = 7; - init_data[DEXED_FOOTCTRL_RANGE] = 99; - init_data[DEXED_FOOTCTRL_ASSIGN] = 7; - init_data[DEXED_BREATHCTRL_RANGE] = 99; - init_data[DEXED_BREATHCTRL_ASSIGN] = 7; - init_data[DEXED_AT_RANGE] = 99; - init_data[DEXED_AT_ASSIGN] = 7; - init_data[DEXED_MASTER_TUNE] = 0; - init_data[DEXED_OP1_ENABLE] = 1; - init_data[DEXED_OP2_ENABLE] = 1; - init_data[DEXED_OP3_ENABLE] = 1; - init_data[DEXED_OP4_ENABLE] = 1; - init_data[DEXED_OP5_ENABLE] = 1; - init_data[DEXED_OP6_ENABLE] = 1; - init_data[DEXED_MAX_NOTES] = MAX_NOTES; - - loadGlobalParameters(init_data); - - return (true); - }*/ - void Dexed::setPBController(uint8_t pb_range, uint8_t pb_step) { #ifdef DEBUG @@ -801,81 +757,82 @@ void Dexed::setPortamentoMode(uint8_t portamento_mode, uint8_t portamento_glissa controllers.refresh(); } -// https://www.musicdsp.org/en/latest/Effects/169-compressor.html# -void compress - ( - float* wav_in, // signal - int n, // N samples - double threshold, // threshold (percents) - double slope, // slope angle (percents) - int sr, // sample rate (smp/sec) - double tla, // lookahead (ms) - double twnd, // window time (ms) - double tatt, // attack time (ms) - double trel // release time (ms) - ) -{ - typedef float stereodata[2]; - stereodata* wav = (stereodata*) wav_in; // our stereo signal - threshold *= 0.01; // threshold to unity (0...1) - slope *= 0.01; // slope to unity - tla *= 1e-3; // lookahead time to seconds - twnd *= 1e-3; // window time to seconds - tatt *= 1e-3; // attack time to seconds - trel *= 1e-3; // release time to seconds - - // attack and release "per sample decay" - double att = (tatt == 0.0) ? (0.0) : exp (-1.0 / (sr * tatt)); - double rel = (trel == 0.0) ? (0.0) : exp (-1.0 / (sr * trel)); - - // envelope - double env = 0.0; - - // sample offset to lookahead wnd start - int lhsmp = (int) (sr * tla); - - // samples count in lookahead window - int nrms = (int) (sr * twnd); - - // for each sample... - for (int i = 0; i < n; ++i) - { - // now compute RMS - double summ = 0; - - // for each sample in window - for (int j = 0; j < nrms; ++j) - { - int lki = i + j + lhsmp; - double smp; +/* + // https://www.musicdsp.org/en/latest/Effects/169-compressor.html# + void compress + ( + float* wav_in, // signal + int n, // N samples + double threshold, // threshold (percents) + double slope, // slope angle (percents) + int sr, // sample rate (smp/sec) + double tla, // lookahead (ms) + double twnd, // window time (ms) + double tatt, // attack time (ms) + double trel // release time (ms) + ) + { + typedef float stereodata[2]; + stereodata* wav = (stereodata*) wav_in; // our stereo signal + threshold *= 0.01; // threshold to unity (0...1) + slope *= 0.01; // slope to unity + tla *= 1e-3; // lookahead time to seconds + twnd *= 1e-3; // window time to seconds + tatt *= 1e-3; // attack time to seconds + trel *= 1e-3; // release time to seconds + + // attack and release "per sample decay" + double att = (tatt == 0.0) ? (0.0) : exp (-1.0 / (sr * tatt)); + double rel = (trel == 0.0) ? (0.0) : exp (-1.0 / (sr * trel)); + + // envelope + double env = 0.0; + + // sample offset to lookahead wnd start + int lhsmp = (int) (sr * tla); + + // samples count in lookahead window + int nrms = (int) (sr * twnd); + + // for each sample... + for (int i = 0; i < n; ++i) + { + // now compute RMS + double summ = 0; - // if we in bounds of signal? - // if so, convert to mono - if (lki < n) - smp = 0.5 * wav[lki][0] + 0.5 * wav[lki][1]; - else - smp = 0.0; // if we out of bounds we just get zero in smp + // for each sample in window + for (int j = 0; j < nrms; ++j) + { + int lki = i + j + lhsmp; + double smp; - summ += smp * smp; // square em.. - } + // if we in bounds of signal? + // if so, convert to mono + if (lki < n) + smp = 0.5 * wav[lki][0] + 0.5 * wav[lki][1]; + else + smp = 0.0; // if we out of bounds we just get zero in smp - double rms = sqrt (summ / nrms); // root-mean-square + summ += smp * smp; // square em.. + } - // dynamic selection: attack or release? - double theta = rms > env ? att : rel; + double rms = sqrt (summ / nrms); // root-mean-square - // smoothing with capacitor, envelope extraction... - // here be aware of pIV denormal numbers glitch - env = (1.0 - theta) * rms + theta * env; + // dynamic selection: attack or release? + double theta = rms > env ? att : rel; - // the very easy hard knee 1:N compressor - double gain = 1.0; - if (env > threshold) - gain = gain - (env - threshold) * slope; + // smoothing with capacitor, envelope extraction... + // here be aware of pIV denormal numbers glitch + env = (1.0 - theta) * rms + theta * env; - // result - two hard kneed compressed channels... - float leftchannel = wav[i][0] * gain; - float rightchannel = wav[i][1] * gain; - } -} + // the very easy hard knee 1:N compressor + double gain = 1.0; + if (env > threshold) + gain = gain - (env - threshold) * slope; + // result - two hard kneed compressed channels... + float leftchannel = wav[i][0] * gain; + float rightchannel = wav[i][1] * gain; + } + } +*/ diff --git a/dexed.h b/dexed.h index 4bd4119..f9be082 100644 --- a/dexed.h +++ b/dexed.h @@ -200,6 +200,7 @@ class Dexed uint8_t max_notes = MAX_ACTIVE_NOTES; int16_t currentNote; bool sustain; + float vuSignal; bool monoMode; bool refreshMode; bool refreshVoice;