Added simple compression for audio generation loop.

pull/32/head
Holger Wirtz 5 years ago
parent d72caf668a
commit b12c2fe619
  1. 8
      MicroDexed.ino
  2. 10
      config.h
  3. 237
      dexed.cpp
  4. 1
      dexed.h

@ -135,9 +135,9 @@ AudioConnection patchCord13(stereo2mono, 1, dacOut, 1);
// //
uint16_t nDynamic = 0; uint16_t nDynamic = 0;
#if defined(USE_FX) && MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT #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 #elif defined(USE_FX) && MOD_FILTER_OUTPUT == MOD_NO_FILTER_OUTPUT
AudioConnection * dynamicConnections[NUM_DEXED * 13]; AudioConnection * dynamicConnections[NUM_DEXED * 15];
#else #else
AudioConnection * dynamicConnections[NUM_DEXED * 4]; AudioConnection * dynamicConnections[NUM_DEXED * 4];
#endif #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], 0, reverb_mixer_r, instance_id);
dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 1, reverb_mixer_l, instance_id); dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 1, reverb_mixer_l, instance_id);
#else #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 #endif
dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 0, master_mixer_r, instance_id); 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); dynamicConnections[nDynamic++] = new AudioConnection(*mono2stereo[instance_id], 1, master_mixer_l, instance_id);
@ -1814,7 +1814,7 @@ void _softRestart(void)
float pseudo_log_curve(float value) 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); const float _pseudo_log = 1048575 / (float)(1 << 20);

@ -43,6 +43,8 @@
// Tools for testing MIDI: https://github.com/gbevin/SendMIDI // Tools for testing MIDI: https://github.com/gbevin/SendMIDI
// https://github.com/gbevin/ReceiveMIDI // 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: // Receiving and storing MIDI SYSEX with Linux:
// amidi -p hw:2,0,0 -d -r /tmp/bkup1.syx // amidi -p hw:2,0,0 -d -r /tmp/bkup1.syx
// //
@ -84,7 +86,7 @@
//************************************************************************************************* //*************************************************************************************************
//* DEBUG OUTPUT SETTINGS //* DEBUG OUTPUT SETTINGS
//************************************************************************************************* //*************************************************************************************************
//#define DEBUG 1 #define DEBUG 1
#define SERIAL_SPEED 230400 #define SERIAL_SPEED 230400
#define SHOW_XRUN 1 #define SHOW_XRUN 1
#define SHOW_CPU_LOAD_MSEC 5000 #define SHOW_CPU_LOAD_MSEC 5000
@ -95,7 +97,7 @@
#define DEXED_ENGINE DEXED_ENGINE_MODERN // DEXED_ENGINE_MARKI // DEXED_ENGINE_OPL #define DEXED_ENGINE DEXED_ENGINE_MODERN // DEXED_ENGINE_MARKI // DEXED_ENGINE_OPL
// Number of Dexed instances // 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 // FX-CHAIN ENABLE/DISABLE
#define USE_FX 1 #define USE_FX 1
// CHORUS parameters // CHORUS parameters
@ -271,9 +273,9 @@
#define MIDI_DEVICE_USB_HOST 1 #define MIDI_DEVICE_USB_HOST 1
#if defined(USE_FX) #if defined(USE_FX)
#if NUM_DEXED == 1 #if NUM_DEXED == 1
#define MAX_NOTES 12 #define MAX_NOTES 13
#else #else
#define MAX_NOTES 9 #define MAX_NOTES 10
#endif #endif
#else #else
#if NUM_DEXED == 1 #if NUM_DEXED == 1

@ -77,6 +77,7 @@ Dexed::Dexed(int rate)
controllers.opSwitch = 0x3f; // enable all operators controllers.opSwitch = 0x3f; // enable all operators
//controllers.opSwitch=0x00; //controllers.opSwitch=0x00;
lastKeyDown = -1; lastKeyDown = -1;
vuSignal = 0.0;
lfo.reset(data + 137); lfo.reset(data + 137);
@ -115,6 +116,8 @@ void Dexed::getSamples(uint16_t n_samples, int16_t* buffer)
uint16_t i, j; uint16_t i, j;
uint8_t note; uint8_t note;
float sumbuf[n_samples]; float sumbuf[n_samples];
float s;
const double decayFactor = 0.99992;
if (refreshVoice) if (refreshVoice)
{ {
@ -148,9 +151,18 @@ void Dexed::getSamples(uint16_t n_samples, int16_t* buffer)
for (j = 0; j < _N_; ++j) for (j = 0; j < _N_; ++j)
{ {
//sumbuf[i + j] += static_cast<float>(signed_saturate_rshift(audiobuf.get()[j] >> 5, 24, 9)) / 0x8000; sumbuf[i + j] += signed_saturate_rshift(audiobuf.get()[j] >> 4, 24, 9) / 32768.0;
sumbuf[i + j] += signed_saturate_rshift(audiobuf.get()[j] >> 5, 24, 9) / 32768.0;
audiobuf.get()[j] = 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()!!! 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_scale_f32(sumbuf, 0.00015, sumbuf, AUDIO_BLOCK_SAMPLES);
arm_float_to_q15(sumbuf, buffer, 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); 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) void Dexed::setPBController(uint8_t pb_range, uint8_t pb_step)
{ {
#ifdef DEBUG #ifdef DEBUG
@ -801,81 +757,82 @@ void Dexed::setPortamentoMode(uint8_t portamento_mode, uint8_t portamento_glissa
controllers.refresh(); controllers.refresh();
} }
// https://www.musicdsp.org/en/latest/Effects/169-compressor.html# /*
void compress // https://www.musicdsp.org/en/latest/Effects/169-compressor.html#
( void compress
float* wav_in, // signal (
int n, // N samples float* wav_in, // signal
double threshold, // threshold (percents) int n, // N samples
double slope, // slope angle (percents) double threshold, // threshold (percents)
int sr, // sample rate (smp/sec) double slope, // slope angle (percents)
double tla, // lookahead (ms) int sr, // sample rate (smp/sec)
double twnd, // window time (ms) double tla, // lookahead (ms)
double tatt, // attack time (ms) double twnd, // window time (ms)
double trel // release time (ms) double tatt, // attack time (ms)
) double trel // release time (ms)
{ )
typedef float stereodata[2]; {
stereodata* wav = (stereodata*) wav_in; // our stereo signal typedef float stereodata[2];
threshold *= 0.01; // threshold to unity (0...1) stereodata* wav = (stereodata*) wav_in; // our stereo signal
slope *= 0.01; // slope to unity threshold *= 0.01; // threshold to unity (0...1)
tla *= 1e-3; // lookahead time to seconds slope *= 0.01; // slope to unity
twnd *= 1e-3; // window time to seconds tla *= 1e-3; // lookahead time to seconds
tatt *= 1e-3; // attack time to seconds twnd *= 1e-3; // window time to seconds
trel *= 1e-3; // release 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)); // attack and release "per sample decay"
double rel = (trel == 0.0) ? (0.0) : exp (-1.0 / (sr * trel)); 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; // envelope
double env = 0.0;
// sample offset to lookahead wnd start
int lhsmp = (int) (sr * tla); // sample offset to lookahead wnd start
int lhsmp = (int) (sr * tla);
// samples count in lookahead window
int nrms = (int) (sr * twnd); // samples count in lookahead window
int nrms = (int) (sr * twnd);
// for each sample...
for (int i = 0; i < n; ++i) // for each sample...
{ for (int i = 0; i < n; ++i)
// now compute RMS {
double summ = 0; // 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;
// if we in bounds of signal? // for each sample in window
// if so, convert to mono for (int j = 0; j < nrms; ++j)
if (lki < n) {
smp = 0.5 * wav[lki][0] + 0.5 * wav[lki][1]; int lki = i + j + lhsmp;
else double smp;
smp = 0.0; // if we out of bounds we just get zero in 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 rms = sqrt (summ / nrms); // root-mean-square
double theta = rms > env ? att : rel;
// smoothing with capacitor, envelope extraction... // dynamic selection: attack or release?
// here be aware of pIV denormal numbers glitch double theta = rms > env ? att : rel;
env = (1.0 - theta) * rms + theta * env;
// the very easy hard knee 1:N compressor // smoothing with capacitor, envelope extraction...
double gain = 1.0; // here be aware of pIV denormal numbers glitch
if (env > threshold) env = (1.0 - theta) * rms + theta * env;
gain = gain - (env - threshold) * slope;
// result - two hard kneed compressed channels... // the very easy hard knee 1:N compressor
float leftchannel = wav[i][0] * gain; double gain = 1.0;
float rightchannel = wav[i][1] * gain; 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;
}
}
*/

@ -200,6 +200,7 @@ class Dexed
uint8_t max_notes = MAX_ACTIVE_NOTES; uint8_t max_notes = MAX_ACTIVE_NOTES;
int16_t currentNote; int16_t currentNote;
bool sustain; bool sustain;
float vuSignal;
bool monoMode; bool monoMode;
bool refreshMode; bool refreshMode;
bool refreshVoice; bool refreshVoice;

Loading…
Cancel
Save