Added compressor code.

Fixes.
chorus
Holger Wirtz 5 years ago
parent 94c5d375df
commit be8f93b976
  1. 104
      MicroMDAEPiano.ino
  2. 3
      config.h
  3. 144
      effect_dynamics.cpp
  4. 196
      effect_dynamics.h
  5. 50
      fast_log.h
  6. 21
      mdaEPiano.cpp
  7. 4
      mdaEPiano.h

@ -22,17 +22,14 @@
*/
#include "config.h"
#ifdef USE_F32
#include <OpenAudio_ArduinoLibrary.h> //for AudioConvert_I16toF32, AudioConvert_F32toI16, and AudioEffectGain_F32
#else
#include <Audio.h>
#endif
#include <Wire.h>
#include <SPI.h>
#include <MIDI.h>
#include <EEPROM.h>
#include <limits.h>
#include "mdaEPiano.h"
#include "effect_dynamics.h"
#ifdef USE_XFADE_DATA
#include "mdaEPianoDataXfade.h"
#else
@ -57,37 +54,23 @@ Encoder enc1(ENC1_PIN_A, ENC1_PIN_B);
Bounce but1 = Bounce(BUT1_PIN, 10); // 10 ms debounce
// GUItool: begin automatically generated code
#ifdef USE_F32
AudioPlayQueue_F32 queue_r; //xy=494,404
AudioPlayQueue_F32 queue_l; //xy=494,404
AudioConvert_F32toI16 float2Int_r;
AudioConvert_F32toI16 float2Int_l;
#else
AudioPlayQueue queue_r; //xy=494,404
AudioPlayQueue queue_l; //xy=494,404
#endif
AudioAnalyzePeak peak_r; //xy=695,491
AudioAnalyzePeak peak_l; //xy=695,491
#ifdef USE_F32
#ifdef USE_COMP_F32
AudioEffectCompressor_F32 comp_r;
AudioEffectCompressor_F32 comp_l;
AudioConnection_F32 patchCord0(queue_r, comp_r);
AudioConnection_F32 patchCord1(queue_l, comp_l);
AudioConnection_F32 patchCord2(comp_r, float2Int_r);
AudioConnection_F32 patchCord3(comp_l, float2Int_l);
#else
AudioConnection_F32 patchCord0(queue_r, float2Int_r);
AudioConnection_F32 patchCord1(queue_l, float2Int_l);
#endif
#ifdef USE_DYNAMICS
AudioEffectDynamics dyna_r;
AudioEffectDynamics dyna_l;
AudioConnection patchCord2(queue_r, dyna_r);
AudioConnection patchCord3(queue_l, dyna_l);
#endif
#ifdef TEENSY_AUDIO_BOARD
AudioOutputI2S i2s1; //xy=1072,364
#ifdef USE_F32
AudioConnection patchCord4(float2Int_r, peak_r);
AudioConnection patchCord5(float2Int_l, peak_l);
AudioConnection patchCord6(float2Int_r, 0, i2s1, 0);
AudioConnection patchCord7(float2Int_l, 0, i2s1, 1);
#ifdef USE_DYNAMICS
AudioConnection patchCord4(dyna_r, peak_r);
AudioConnection patchCord5(dyna_l, peak_l);
AudioConnection patchCord6(dyna_r, 0, i2s1, 0);
AudioConnection patchCord7(dyna_l, 0, i2s1, 1);
#else
AudioConnection patchCord4(queue_r, peak_r);
AudioConnection patchCord5(queue_l, peak_l);
@ -99,17 +82,17 @@ AudioControlSGTL5000 sgtl5000_1; //xy=700,536
AudioOutputPT8211 pt8211_1; //xy=1079,320
AudioAmplifier volume_r; //xy=818,370
AudioAmplifier volume_l; //xy=818,411
#ifdef USE_F32
AudioConnection patchCord4(float2Int_r, volume_r);
AudioConnection patchCord5(float2Int_l, volume_l);
#ifdef USE_DYNAMICS
AudioConnection patchCord2(dyna_r, volume_r);
AudioConnection patchCord3(dyna_l, volume_l);
#else
AudioConnection patchCord4(queue_r, volume_r);
AudioConnection patchCord5(queue_l, volume_l);
AudioConnection patchCord2(queue_r, volume_r);
AudioConnection patchCord3(queue_l, volume_l);
#endif
AudioConnection patchCord6(volume_r, peak_r);
AudioConnection patchCord7(volume_l, peak_l);
AudioConnection patchCord8(volume_r, 0, pt8211_1, 1);
AudioConnection patchCord9(volume_l, 0, pt8211_1, 0);
AudioConnection patchCord4(volume_r, peak_r);
AudioConnection patchCord5(volume_l, peak_l);
AudioConnection patchCord6(volume_r, 0, pt8211_1, 1);
AudioConnection patchCord7(volume_l, 0, pt8211_1, 0);
#endif
// GUItool: end automatically generated code
@ -118,7 +101,6 @@ uint8_t midi_channel = DEFAULT_MIDI_CHANNEL;
uint32_t xrun = 0;
uint32_t overload = 0;
uint32_t peak = 0;
uint32_t audio_buffer_problem = 0;
uint16_t render_time_max = 0;
float vol = VOLUME;
float vol_right = 1.0;
@ -201,9 +183,6 @@ void setup()
// start audio card
AudioMemory(AUDIO_MEM);
#ifdef USE_F32
AudioMemory_F32(AUDIO_MEM_F32);
#endif
#ifdef TEENSY_AUDIO_BOARD
sgtl5000_1.enable();
@ -236,26 +215,27 @@ void setup()
sched_note_off.begin(note_off, 6333333);
#endif
ep->setParameter(DECAY, 0.5);
ep->setParameter(RELEASE, 0.5);
ep->setParameter(HARDNESS, 0.7);
ep->setParameter(TREBLE, 0.85);
ep->setParameter(DETUNE, 0.1);
ep->setParameter(VELOCITY_SENSE, 0.2);
ep->setParameter(VELOCITY_SENSE, 1.0);
ep->setParameter(STEREO, 0.7);
ep->setParameter(MAX_POLY, 1.0);
ep->setParameter(OVERDRIVE, 0.3);
// DECAY,RELEASE,HARDNESS,TREBLE,PAN_TREM,LFO_RATE,VELOCITY_SENSE,STEREO,MAX_POLY,TUNE,DETUNE,OVERDRIVE
// DECAY,RELEASE,HARDNESS,TREBLE,PAN_TREM,LFO_RATE,VELOCIT
#if defined(USE_F32) && defined(USE_COMP_F32)
// setup compressor as limiter
setup_compressor(true, -15.0f, 5.0f, 0.005f, 0.200f);
// setup compressor like an automatic volume control
//setup_compressor(true,-50.0,5.0,1.0,2.0);
comp_r.setPreGain_dB(0.0);
comp_l.setPreGain_dB(0.0);
#ifdef USE_DYNAMICS
//dyna_r.limit();
//dyna_l.limit();
dyna_r.compression(-10.0, MIN_T, MIN_T, 20.0, 2.0);
dyna_l.compression(-10.0, MIN_T, MIN_T, 20.0, 2.0);
#endif
Serial.println(F("<setup end>"));
lcd.show(1, 0, 16, "");
#if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC)
show_cpu_and_mem_usage();
@ -265,13 +245,8 @@ void setup()
void loop()
{
#if defined(USE_F32)
float* audio_buffer_r; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(float)
float* audio_buffer_l; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(float)
#else
int16_t* audio_buffer_r; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(int16_t)
int16_t* audio_buffer_l; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(int16_t)
#endif
const uint16_t audio_block_time_ms = 1000000 / (SAMPLE_RATE / AUDIO_BLOCK_SAMPLES);
// Main sound calculation
@ -326,27 +301,10 @@ void loop()
queue_r.playBuffer();
queue_l.playBuffer();
}
else
{
audio_buffer_problem++;
}
handle_input();
}
#if defined(USE_F32) && defined(USE_COMP_F32)
void setup_compressor(boolean use_HP_filter, float knee_dBFS, float comp_ratio, float attack_sec, float release_sec)
{
comp_r.enableHPFilter(use_HP_filter); comp_l.enableHPFilter(use_HP_filter);
comp_r.setThresh_dBFS(knee_dBFS); comp_l.setThresh_dBFS(knee_dBFS);
comp_r.setCompressionRatio(comp_ratio); comp_l.setCompressionRatio(comp_ratio);
float fs_Hz = AUDIO_SAMPLE_RATE;
comp_r.setAttack_sec(attack_sec, fs_Hz); comp_l.setAttack_sec(attack_sec, fs_Hz);
comp_r.setRelease_sec(release_sec, fs_Hz); comp_l.setRelease_sec(release_sec, fs_Hz);
}
#endif
void handle_input(void)
{
#ifdef USE_ONBOARD_USB_HOST
@ -579,8 +537,6 @@ void show_cpu_and_mem_usage(void)
Serial.print(overload, DEC);
Serial.print(F(" PEAK: "));
Serial.print(peak, DEC);
Serial.print(F(" AUDIO_BUF_ERR: "));
Serial.print(audio_buffer_problem, DEC);
Serial.println();
AudioProcessorUsageMaxReset();
AudioMemoryUsageMaxReset();

@ -34,9 +34,8 @@
#define AUDIO_MEM 16
#define AUDIO_MEM_F32 16
#define SAMPLE_RATE 44100
#define REDUCE_LOUDNESS 1
#define REDUCE_LOUDNESS 0
#define USE_XFADE_DATA 1
//#define USE_COMP_F32 1
#if !defined(__MK66FX1M0__) // check for Teensy-3.6
#undef USE_ONBOARD_USB_HOST
#define NVOICES 32

@ -0,0 +1,144 @@
/* Audio Library for Teensy 3.X
* Dynamics Processor (Gate, Compressor & Limiter)
* Copyright (c) 2017, Marc Paquette (marc@dacsystemes.com)
* Based on analyse_rms & mixer objects by Paul Stoffregen
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "effect_dynamics.h"
#include "fast_log.h"
#include "utility/dspinst.h"
#include "utility/sqrt_integer.h"
static float analyse_rms(int16_t *data) {
uint32_t *p = (uint32_t *)data;
const uint32_t *end = p + AUDIO_BLOCK_SAMPLES / 2;
int64_t sum = 0;
do {
uint32_t n1 = *p++;
uint32_t n2 = *p++;
uint32_t n3 = *p++;
uint32_t n4 = *p++;
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n1, n1);
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n2, n2);
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n3, n3);
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n4, n4);
} while (p < end);
int32_t meansq = sum / AUDIO_BLOCK_SAMPLES;
return sqrt_uint32(meansq) / 32767.0f;
}
static void applyGain(int16_t *data, int32_t mult1, int32_t mult2) {
uint32_t *p = (uint32_t *)data;
const uint32_t *end = p + AUDIO_BLOCK_SAMPLES / 2;
int32_t inc = (mult2 - mult1) / (AUDIO_BLOCK_SAMPLES / 2);
do {
uint32_t tmp32 = *p; // read 2 samples from *data
int32_t val1 = signed_multiply_32x16b(mult1, tmp32);
mult1 += inc;
int32_t val2 = signed_multiply_32x16t(mult1, tmp32);
mult1 += inc;
val1 = signed_saturate_rshift(val1, 16, 0);
val2 = signed_saturate_rshift(val2, 16, 0);
*p++ = pack_16b_16b(val2, val1);
} while (p < end);
}
void AudioEffectDynamics::update(void) {
audio_block_t *block;
block = receiveWritable(0);
if (!block) return;
if (!gateEnabled && !compEnabled && !limiterEnabled) {
//Transmit & release
transmit(block);
release(block);
return;
}
//Analyze received block
float rms = analyse_rms(block->data);
//Compute block RMS level in Db
float inputdb = MIN_DB;
if (rms > 0) inputdb = unitToDb(rms);
//Gate
if (gateEnabled) {
if (inputdb >= gateThresholdOpen) gatedb = (aGateAttack * gatedb) + (aOneMinusGateAttack * MAX_DB);
else if (inputdb < gateThresholdClose) gatedb = (aGateRelease * gatedb) + (aOneMinusGateRelease * MIN_DB);
}
else gatedb = MAX_DB;
//Compressor
if (compEnabled) {
float attdb = MAX_DB; //Below knee
if (inputdb >= aLowKnee) {
if(inputdb <= aHighKnee) {
//Knee transition
float knee = inputdb - aLowKnee;
attdb = aKneeRatio * knee * knee * aTwoKneeWidth;
}
else {
//Above knee
attdb = compThreshold + ((inputdb - compThreshold) * compRatio) - inputdb;
}
}
if (attdb <= compdb) compdb = (aCompAttack * compdb) + (aOneMinusCompAttack * attdb);
else compdb = (aCompRelease * compdb) + (aOneMinusCompRelease * attdb);
}
else compdb = MAX_DB;
//Brickwall Limiter
if (limiterEnabled) {
float outdb = inputdb + compdb + makeupdb;
if (outdb >= limitThreshold) limitdb = (aLimitAttack * limitdb) + (aOneMinusLimitAttack * (limitThreshold - outdb));
else limitdb *= aLimitRelease;
}
else limitdb = MAX_DB;
//Compute linear gain
float totalGain = gatedb + compdb + makeupdb + limitdb;
int32_t mult = dbToUnit(totalGain) * 65536.0f;
//Apply gain to block
applyGain(block->data, last_mult, mult);
last_mult = mult;
//Transmit & release
transmit(block);
release(block);
}

@ -0,0 +1,196 @@
/* Audio Library for Teensy 3.X
* Dynamics Processor (Gate, Compressor & Limiter)
* Copyright (c) 2018, Marc Paquette (marc@dacsystemes.com)
* Based on analyse_rms, effect_envelope & mixer objects by Paul Stoffregen
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef effect_dynamics_h_
#define effect_dynamics_h_
#include "Arduino.h"
#include "AudioStream.h"
#define MIN_DB -110.0f
#define MAX_DB 0.0f
#define MIN_T 0.03f //Roughly 1 block
#define MAX_T 4.00f
#define RATIO_OFF 1.0f
#define RATIO_INFINITY 60.0f
class AudioEffectDynamics : public AudioStream
{
public:
AudioEffectDynamics(void) : AudioStream(1, inputQueueArray) {
gate();
compression();
limit();
autoMakeupGain();
}
//Sets the gate parameters.
//threshold is in dbFS
//attack & release are in seconds
void gate(float threshold = -50.0f, float attack = MIN_T, float release = 0.3f, float hysterisis = 6.0f) {
gateEnabled = threshold > MIN_DB;
gateThresholdOpen = constrain(threshold, MIN_DB, MAX_DB);
gateThresholdClose = gateThresholdOpen - constrain(hysterisis, 0.0f, 6.0f);
float gateAttackTime = constrain(attack, MIN_T, MAX_T);
float gateReleaseTime = constrain(release, MIN_T, MAX_T);
aGateAttack = timeToAlpha(gateAttackTime);
aOneMinusGateAttack = 1.0f - aGateAttack;
aGateRelease = timeToAlpha(gateReleaseTime);
aOneMinusGateRelease = 1.0f - aGateRelease;
gatedb = MIN_DB;
}
//Sets the compression parameters.
//threshold & kneeWidth are in db(FS)
//attack and release are in seconds
//ratio is expressed as x:1 i.e. 1 for no compression, 60 for brickwall limiting
//Set kneeWidth to 0 for hard knee
void compression(float threshold = -40.0f, float attack = MIN_T, float release = 0.5f, float ratio = 35.0f, float kneeWidth = 6.0f) {
compEnabled = threshold < MAX_DB;
compThreshold = constrain(threshold, MIN_DB, MAX_DB);
float compAttackTime = constrain(attack, MIN_T, MAX_T);
float compReleaseTime = constrain(release, MIN_T, MAX_T);
compRatio = 1.0f / constrain(abs(ratio), RATIO_OFF, RATIO_INFINITY);
float compKneeWidth = constrain(abs(kneeWidth), 0.0f, 32.0f);
computeMakeupGain();
aCompAttack = timeToAlpha(compAttackTime);
aOneMinusCompAttack = 1.0f - aCompAttack;
aCompRelease = timeToAlpha(compReleaseTime);
aOneMinusCompRelease = 1.0f - aCompRelease;
aHalfKneeWidth = compKneeWidth / 2.0f;
aTwoKneeWidth = 1.0f / (compKneeWidth * 2.0f);
aKneeRatio = compRatio - 1.0f;
aLowKnee = compThreshold - aHalfKneeWidth;
aHighKnee = compThreshold + aHalfKneeWidth;
compdb = MIN_DB;
}
//Sets the hard limiter parameters
//threshold is in dbFS
//attack & release are in seconds
void limit(float threshold = -3.0f, float attack = MIN_T, float release = MIN_T) {
limiterEnabled = threshold < MAX_DB;
limitThreshold = constrain(threshold, MIN_DB, MAX_DB);
float limitAttackTime = constrain(attack, MIN_T, MAX_T);
float limitReleaseTime = constrain(release, MIN_T, MAX_T);
computeMakeupGain();
aLimitAttack = timeToAlpha(limitAttackTime);
aOneMinusLimitAttack = 1.0f - aLimitAttack;
aLimitRelease = timeToAlpha(limitReleaseTime);
limitdb = MIN_DB;
}
//Enables automatic makeup gain setting
//headroom is in dbFS
void autoMakeupGain(float headroom = 6.0f) {
mgAutoEnabled = true;
mgHeadroom = constrain(headroom, 0.0f, 60.0f);
computeMakeupGain();
}
//Sets a fixed makeup gain value.
//gain is in dbFS
void makeupGain(float gain = 0.0f) {
mgAutoEnabled = false;
makeupdb = constrain(gain, -12.0f, 24.0f);
}
private:
audio_block_t *inputQueueArray[1];
bool gateEnabled = false;
float gateThresholdOpen;
float gateThresholdClose;
float gatedb;
bool compEnabled = false;
float compThreshold;
float compRatio;
float compdb;
bool limiterEnabled = false;
float limitThreshold;
float limitdb;
bool mgAutoEnabled;
float mgHeadroom;
float makeupdb;
float aGateAttack;
float aOneMinusGateAttack;
float aGateRelease;
float aOneMinusGateRelease;
float aHalfKneeWidth;
float aTwoKneeWidth;
float aKneeRatio;
float aLowKnee;
float aHighKnee;
float aCompAttack;
float aOneMinusCompAttack;
float aCompRelease;
float aOneMinusCompRelease;
float aLimitAttack;
float aOneMinusLimitAttack;
float aLimitRelease;
int32_t last_mult;
void computeMakeupGain() {
if (mgAutoEnabled) {
makeupdb = -compThreshold + (compThreshold * compRatio) + limitThreshold - mgHeadroom;
}
}
//Computes smoothing time constants for a 10% to 90% change
float timeToAlpha(float time) {
return expf(-0.9542f / (((float)AUDIO_SAMPLE_RATE_EXACT / (float)AUDIO_BLOCK_SAMPLES) * time));
}
virtual void update(void);
};
#endif

@ -0,0 +1,50 @@
/* ----------------------------------------------------------------------
* 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.
** ------------------------------------------------------------------- */
float log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f};
float log2f_approx(float X)
{
float *C = &log2f_approx_coeff[0];
float Y;
float F;
int E;
// This is the approximation to log2()
F = frexpf(fabsf(X), &E);
// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E;
Y = *C++;
Y *= F;
Y += (*C++);
Y *= F;
Y += (*C++);
Y *= F;
Y += (*C++);
Y += E;
return(Y);
}
// https://codingforspeed.com/using-faster-exponential-approximation/
inline float expf_approx(float x) {
x = 1.0f + x / 1024;
x *= x; x *= x; x *= x; x *= x;
x *= x; x *= x; x *= x; x *= x;
x *= x; x *= x;
return x;
}
inline float unitToDb(float unit) {
return 6.02f * log2f_approx(unit);
}
inline float dbToUnit(float db) {
return expf_approx(db * 2.302585092994046f * 0.05f);
}

@ -171,10 +171,6 @@ void mdaEPiano::setParameter(int32_t index, float value)
{
programs[curProgram].param[index] = value;
update();
//if(editor) editor->postUpdate(); ///For GUI
guiUpdate = index + 0x100 + (guiUpdate & 0xFFFF00);
}
@ -194,11 +190,7 @@ float mdaEPiano::getParameter(int32_t index) {
return programs[curProgram].param[index];
}
#ifdef USE_F32
void mdaEPiano::process(float* outputs_r, float* outputs_l)
#else
void mdaEPiano::process(int16_t* outputs_r, int16_t* outputs_l)
#endif
{
int16_t v;
float x, l, r, od = overdrive;
@ -241,13 +233,16 @@ void mdaEPiano::process(int16_t* outputs_r, int16_t* outputs_l)
l += l * lmod * lfo1;
r += r * rmod * lfo1; //worth making all these local variables?
#ifdef USE_F32
outputs_l[frame] += l;
outputs_r[frame] += r;
#else
if (r > 1.0)
r = 1.0;
else if (r < -1.0)
r = -1.0;
if (l > 1.0)
l = 1.0;
else if (l < -1.0)
l = -1.0;
outputs_l[frame] = static_cast<int16_t>(l * 0x7fff) >> REDUCE_LOUDNESS;
outputs_r[frame] = static_cast<int16_t>(r * 0x7fff) >> REDUCE_LOUDNESS;
#endif
}
if (fabs(tl) < 1.0e-10) tl = 0.0f; //anti-denormal

@ -79,11 +79,7 @@ class mdaEPiano //: public AudioEffectX
mdaEPiano(); // mdaEPiano(audioMasterCallback audioMaster);
~mdaEPiano();
#ifdef USE_F32
virtual void process(float *outputs_r, float *outputs_l);
#else
virtual void process(int16_t *outputs_r, int16_t *outputs_l);
#endif
virtual bool processMidiMessage(uint8_t type, uint8_t data1, uint8_t data2);
virtual void setProgram(int32_t program);
virtual void setParameter(int32_t index, float value);

Loading…
Cancel
Save