diff --git a/Synth_Dexed b/Synth_Dexed index e414a87..24af490 160000 --- a/Synth_Dexed +++ b/Synth_Dexed @@ -1 +1 @@ -Subproject commit e414a8718300815aefc3fe0acd8df5c12ad0b58a +Subproject commit 24af490e0f7d1d5b816a130f7d8c0f4a8b20946d diff --git a/src/Makefile b/src/Makefile index fd8987a..8612096 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ CMSIS_DIR = ../CMSIS_5/CMSIS OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \ mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \ sysexfileloader.o performanceconfig.o perftimer.o \ - effect_platervbstereo.o + effect_platervbstereo.o effect_compressor.o include ./Synth_Dexed.mk include ./Rules.mk diff --git a/src/Synth_Dexed.mk b/src/Synth_Dexed.mk index 498038a..20d6218 100644 --- a/src/Synth_Dexed.mk +++ b/src/Synth_Dexed.mk @@ -5,7 +5,9 @@ CMSIS_CORE_INCLUDE_DIR = $(CMSIS_DIR)/Core/Include CMSIS_DSP_INCLUDE_DIR = $(CMSIS_DIR)/DSP/Include CMSIS_DSP_PRIVATE_INCLUDE_DIR = $(CMSIS_DIR)/DSP/PrivateInclude +CMSIS_DSP_COMPUTELIB_INCLUDE_DIR = $(CMSIS_DIR)/DSP/ComputeLibrary/Include CMSIS_DSP_SOURCE_DIR = $(CMSIS_DIR)/DSP/Source +CMSIS_DSP_COMPUTELIB_SRC_DIR = $(CMSIS_DIR)/DSP/ComputeLibrary/Source OBJS += \ $(SYNTH_DEXED_DIR)/PluginFx.o \ @@ -24,12 +26,14 @@ OBJS += \ $(CMSIS_DSP_SOURCE_DIR)/BasicMathFunctions/BasicMathFunctions.o \ $(CMSIS_DSP_SOURCE_DIR)/FastMathFunctions/FastMathFunctions.o \ $(CMSIS_DSP_SOURCE_DIR)/FilteringFunctions/FilteringFunctions.o \ - $(CMSIS_DSP_SOURCE_DIR)/CommonTables/CommonTables.o + $(CMSIS_DSP_SOURCE_DIR)/CommonTables/CommonTables.o \ + $(CMSIS_DSP_COMPUTELIB_SRC_DIR)/arm_cl_tables.o INCLUDE += -I $(SYNTH_DEXED_DIR) INCLUDE += -I $(CMSIS_CORE_INCLUDE_DIR) INCLUDE += -I $(CMSIS_DSP_INCLUDE_DIR) INCLUDE += -I $(CMSIS_DSP_PRIVATE_INCLUDE_DIR) -CXXFLAGS += -DARM_MATH_NEON -DHAVE_NEON +INCLUDE += -I $(CMSIS_DSP_COMPUTELIB_INCLUDE_DIR) +DEFINE += -DARM_MATH_NEON -DHAVE_NEON -EXTRACLEAN = $(SYNTH_DEXED_DIR)/*.[od] $(CMSIS_DSP_SOURCE_DIR)/SupportFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/SupportFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/BasicMathFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/FastMathFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/FilteringFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/CommonTables/*.[od] +EXTRACLEAN = $(SYNTH_DEXED_DIR)/*.[od] $(CMSIS_DSP_SOURCE_DIR)/SupportFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/SupportFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/BasicMathFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/FastMathFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/FilteringFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/CommonTables/*.[od] $(CMSIS_DSP_COMPUTELIB_SRC_DIR)/*.[od] diff --git a/src/effect_compressor.cpp b/src/effect_compressor.cpp new file mode 100644 index 0000000..785e1a9 --- /dev/null +++ b/src/effect_compressor.cpp @@ -0,0 +1,380 @@ +/* From https://github.com/chipaudette/OpenAudio_ArduinoLibrary */ + +/* + AudioEffectCompressor + + Created: Chip Audette, Dec 2016 - Jan 2017 + Purpose; Apply dynamic range compression to the audio stream. + Assumes floating-point data. + + This processes a single stream fo audio data (ie, it is mono) + + MIT License. use at your own risk. +*/ + +#include +#include +#include "effect_compressor.h" + +LOGMODULE ("compressor"); + +Compressor::Compressor(const float32_t sample_rate_Hz) { + //setDefaultValues(AUDIO_SAMPLE_RATE); resetStates(); + setDefaultValues(sample_rate_Hz); + resetStates(); +} + +void Compressor::setDefaultValues(const float32_t sample_rate_Hz) { + setThresh_dBFS(-20.0f); //set the default value for the threshold for compression + setCompressionRatio(5.0f); //set the default copression ratio + setAttack_sec(0.005f, sample_rate_Hz); //default to this value + setRelease_sec(0.200f, sample_rate_Hz); //default to this value + setHPFilterCoeff(); enableHPFilter(true); //enable the HP filter to remove any DC offset from the audio +} + +//Compute the instantaneous desired gain, including the compression ratio and +//threshold for where the comrpession kicks in +void Compressor::calcInstantaneousTargetGain(float32_t *audio_level_dB_block, float32_t *inst_targ_gain_dB_block, uint16_t len) +{ + // how much are we above the compression threshold? + float32_t* above_thresh_dB_block=(float32_t*)malloc(sizeof(float32_t)*len); + if(!above_thresh_dB_block) + { + LOGERR("Cannot allocate memory for \"above_thresh_dB_block\" - stopping\n"); + while(1); + } + + //arm_copy_f32(zeroblock_f32,above_thresh_dB_block,len); + + arm_offset_f32(audio_level_dB_block, //CMSIS DSP for "add a constant value to all elements" + -thresh_dBFS, //this is the value to be added + above_thresh_dB_block, //this is the output + len); + + // scale by the compression ratio...this is what the output level should be (this is our target level) + arm_scale_f32(above_thresh_dB_block, //CMSIS DSP for "multiply all elements by a constant value" + 1.0f / comp_ratio, //this is the value to be multiplied + inst_targ_gain_dB_block, //this is the output + len); + + // compute the instantaneous gain...which is the difference between the target level and the original level + arm_sub_f32(inst_targ_gain_dB_block, //CMSIS DSP for "subtract two vectors element-by-element" + above_thresh_dB_block, //this is the vector to be subtracted + inst_targ_gain_dB_block, //this is the output + len); + + // limit the target gain to attenuation only (this part of the compressor should not make things louder!) + for (uint16_t i=0; i < len; i++) { + if (inst_targ_gain_dB_block[i] > 0.0f) inst_targ_gain_dB_block[i] = 0.0f; + } + + // release memory before returning + if(above_thresh_dB_block) + delete(above_thresh_dB_block); + + return; //output is passed through inst_targ_gain_dB_block +} + +//this method applies the "attack" and "release" constants to smooth the +//target gain level through time. +void Compressor::calcSmoothedGain_dB(float32_t *inst_targ_gain_dB_block, float32_t *gain_dB_block, uint16_t len) +{ + float32_t gain_dB; + float32_t one_minus_attack_const = 1.0f - attack_const; + float32_t one_minus_release_const = 1.0f - release_const; + for (uint16_t i = 0; i < len; i++) { + gain_dB = inst_targ_gain_dB_block[i]; + + //smooth the gain using the attack or release constants + if (gain_dB < prev_gain_dB) { //are we in the attack phase? + gain_dB_block[i] = attack_const*prev_gain_dB + one_minus_attack_const*gain_dB; + } else { //or, we're in the release phase + gain_dB_block[i] = release_const*prev_gain_dB + one_minus_release_const*gain_dB; + } + + //save value for the next time through this loop + prev_gain_dB = gain_dB_block[i]; + } + + return; //the output here is gain_block +} + +// Here's the method that estimates the level of the audio (in dB) +// It squares the signal and low-pass filters to get a time-averaged +// signal power. It then +void Compressor::calcAudioLevel_dB(float32_t *wav_block, float32_t *level_dB_block, uint16_t len) { + + // calculate the instantaneous signal power (square the signal) + float32_t* wav_pow_block=(float32_t*)malloc(sizeof(float32_t)*len); + if(!wav_pow_block) + { + LOGERR("Cannot allocate memory for \"wav_pow_block\" - stopping\n"); + while(1); + } + + //arm_copy_f32(zeroblock_f32,wav_pow_block,len); + + arm_mult_f32(wav_block, wav_block, wav_pow_block, len); + + // low-pass filter and convert to dB + float32_t c1 = level_lp_const, c2 = 1.0f - c1; //prepare constants + for (uint16_t i = 0; i < len; i++) { + // first-order low-pass filter to get a running estimate of the average power + wav_pow_block[i] = c1*prev_level_lp_pow + c2*wav_pow_block[i]; + + // save the state of the first-order low-pass filter + prev_level_lp_pow = wav_pow_block[i]; + + //now convert the signal power to dB (but not yet multiplied by 10.0) + level_dB_block[i] = log10f_approx(wav_pow_block[i]); + } + + //limit the amount that the state of the smoothing filter can go toward negative infinity + if (prev_level_lp_pow < (1.0E-13)) prev_level_lp_pow = 1.0E-13; //never go less than -130 dBFS + + //scale the wav_pow_block by 10.0 to complete the conversion to dB + arm_scale_f32(level_dB_block, 10.0f, level_dB_block, len); //use ARM DSP for speed! + + //release memory and return + if(wav_pow_block) + delete(wav_pow_block); + + return; //output is passed through level_dB_block + } + + //This method computes the desired gain from the compressor, given an estimate + //of the signal level (in dB) +void Compressor::calcGain(float32_t *audio_level_dB_block, float32_t *gain_block,uint16_t len) +{ + //first, calculate the instantaneous target gain based on the compression ratio + float32_t* inst_targ_gain_dB_block=(float32_t*)malloc(sizeof(float32_t)*len); + if(!inst_targ_gain_dB_block) + { + LOGERR("Cannot allocate memory for \"inst_targ_gain_dB_block\" - stopping\n"); + while(1); + } + //arm_copy_f32(zeroblock_f32,inst_targ_gain_dB_block,len); + + calcInstantaneousTargetGain(audio_level_dB_block, inst_targ_gain_dB_block,len); + + //second, smooth in time (attack and release) by stepping through each sample + float32_t *gain_dB_block = (float32_t*)malloc(sizeof(float32_t)*len); + if(!gain_dB_block) + { + LOGERR("Cannot allocate memory for \"gain_dB_block\" - stopping\n"); + while(1); + } + //arm_copy_f32(zeroblock_f32,gain_dB_block,len); + + calcSmoothedGain_dB(inst_targ_gain_dB_block,gain_dB_block, len); + + //finally, convert from dB to linear gain: gain = 10^(gain_dB/20); (ie this takes care of the sqrt, too!) + arm_scale_f32(gain_dB_block, 1.0f/20.0f, gain_dB_block, len); //divide by 20 + for (uint16_t i = 0; i < len; i++) gain_block[i] = pow10f(gain_dB_block[i]); //do the 10^(x) + + + //release memory and return + if(inst_targ_gain_dB_block) + delete(inst_targ_gain_dB_block); + if(gain_dB_block) + delete(gain_dB_block); + + return; //output is passed through gain_block +} + +//here's the method that does all the work +void Compressor::doCompression(float32_t *audio_block, uint16_t len) { + //Serial.println("AudioEffectGain_F32: updating."); //for debugging. + if (!audio_block) { + LOGERR("No audio_block available for Compressor!\n"); + return; + } + + //apply a high-pass filter to get rid of the DC offset + if (use_HP_prefilter) + arm_biquad_cascade_df1_f32(&hp_filt_struct, audio_block, audio_block, len); + + //apply the pre-gain...a negative gain value will disable + if (pre_gain > 0.0f) + arm_scale_f32(audio_block, pre_gain, audio_block, len); //use ARM DSP for speed! + + //calculate the level of the audio (ie, calculate a smoothed version of the signal power) + float32_t* audio_level_dB_block = (float32_t*)malloc(sizeof(float32_t)*len); + if(!audio_level_dB_block) + { + LOGERR("Cannot allocate memory for \"audio_level_dB_block\" - stopping\n"); + while(1); + } + + //arm_copy_f32(zeroblock_f32,audio_level_dB_block,len); + + if(audio_level_dB_block) + calcAudioLevel_dB(audio_block, audio_level_dB_block, len); //returns through audio_level_dB_block + + //compute the desired gain based on the observed audio level + float32_t* gain_block=(float32_t*)malloc(sizeof(float32_t)*len); + if(!gain_block) + { + LOGERR("Cannot allocate memory for \"gain_block\" - stopping\n"); + while(1); + } + + //arm_copy_f32(zeroblock_f32,gain_block,len); + + if(gain_block) + { + calcGain(audio_level_dB_block, gain_block, len); //returns through gain_block + + //apply the desired gain...store the processed audio back into audio_block + arm_mult_f32(audio_block, gain_block, audio_block, len); + } + + //release memory + if(audio_level_dB_block) + delete(audio_level_dB_block); + if(gain_block) + delete(gain_block); +} + +//methods to set parameters of this module +void Compressor::resetStates(void) +{ + prev_level_lp_pow = 1.0f; + prev_gain_dB = 0.0f; + + //initialize the HP filter. (This also resets the filter states,) + arm_biquad_cascade_df1_init_f32(&hp_filt_struct, hp_nstages, hp_coeff, hp_state); +} + +void Compressor::setPreGain(float32_t g) +{ + pre_gain = g; +} + +void Compressor::setPreGain_dB(float32_t gain_dB) +{ + setPreGain(pow(10.0, gain_dB / 20.0)); +} + +void Compressor::setCompressionRatio(float32_t cr) +{ + comp_ratio = max(0.001f, cr); //limit to positive values + updateThresholdAndCompRatioConstants(); +} + +void Compressor::setAttack_sec(float32_t a, float32_t fs_Hz) +{ + attack_sec = a; + attack_const = expf(-1.0f / (attack_sec * fs_Hz)); //expf() is much faster than exp() + + //also update the time constant for the envelope extraction + setLevelTimeConst_sec(min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants +} + +void Compressor::setRelease_sec(float32_t r, float32_t fs_Hz) +{ + release_sec = r; + release_const = expf(-1.0f / (release_sec * fs_Hz)); //expf() is much faster than exp() + + //also update the time constant for the envelope extraction + setLevelTimeConst_sec(min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants +} + +void Compressor::setLevelTimeConst_sec(float32_t t_sec, float32_t fs_Hz) +{ + const float32_t min_t_sec = 0.002f; //this is the minimum allowed value + level_lp_sec = max(min_t_sec,t_sec); + level_lp_const = expf(-1.0f / (level_lp_sec * fs_Hz)); //expf() is much faster than exp() +} + +void Compressor::setThresh_dBFS(float32_t val) +{ + thresh_dBFS = val; + setThreshPow(pow(10.0, thresh_dBFS / 10.0)); +} + +void Compressor::enableHPFilter(boolean flag) +{ + use_HP_prefilter = flag; +} + +void Compressor::setHPFilterCoeff_N2IIR_Matlab(float32_t b[], float32_t a[]) +{ + //https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5 + //Use matlab to compute the coeff for HP at 20Hz: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100 + hp_coeff[0] = b[0]; hp_coeff[1] = b[1]; hp_coeff[2] = b[2]; //here are the matlab "b" coefficients + hp_coeff[3] = -a[1]; hp_coeff[4] = -a[2]; //the DSP needs the "a" terms to have opposite sign vs Matlab +} + +void Compressor::setHPFilterCoeff(void) +{ + //https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5 + //Use matlab to compute the coeff for HP at 20Hz: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100 + float32_t b[] = {9.979871156751189e-01, -1.995974231350238e+00, 9.979871156751189e-01}; //from Matlab + float32_t a[] = { 1.000000000000000e+00, -1.995970179642828e+00, 9.959782830576472e-01}; //from Matlab + setHPFilterCoeff_N2IIR_Matlab(b, a); + //hp_coeff[0] = b[0]; hp_coeff[1] = b[1]; hp_coeff[2] = b[2]; //here are the matlab "b" coefficients + //hp_coeff[3] = -a[1]; hp_coeff[4] = -a[2]; //the DSP needs the "a" terms to have opposite sign vs Matlab +} + +void Compressor::updateThresholdAndCompRatioConstants(void) +{ + comp_ratio_const = 1.0f-(1.0f / comp_ratio); + thresh_pow_FS_wCR = powf(thresh_pow_FS, comp_ratio_const); +} + +void Compressor::setThreshPow(float32_t t_pow) +{ + thresh_pow_FS = t_pow; + updateThresholdAndCompRatioConstants(); +} + +// Accelerate the powf(10.0,x) function +static float32_t pow10f(float32_t x) +{ + //return powf(10.0f,x) //standard, but slower + return expf(2.302585092994f*x); //faster: exp(log(10.0f)*x) +} + +// Accelerate the log10f(x) function? +static float32_t log10f_approx(float32_t x) +{ + //return log10f(x); //standard, but slower + return log2f_approx(x)*0.3010299956639812f; //faster: log2(x)/log2(10) +} + +/* ---------------------------------------------------------------------- +** 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 +//float32_t log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f}; +static float32_t log2f_approx(float32_t X) +{ + //float32_t *C = &log2f_approx_coeff[0]; + float32_t Y; + float32_t 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 = 1.23149591368684f; + Y *= F; + //Y += (*C++); + Y += -4.11852516267426f; + Y *= F; + //Y += (*C++); + Y += 6.02197014179219f; + Y *= F; + //Y += (*C++); + Y += -3.13396450166353f; + Y += E; + + return(Y); +} diff --git a/src/effect_compressor.h b/src/effect_compressor.h new file mode 100644 index 0000000..df84092 --- /dev/null +++ b/src/effect_compressor.h @@ -0,0 +1,84 @@ +/* From https://github.com/chipaudette/OpenAudio_ArduinoLibrary */ + +/* + AudioEffectCompressor + + Created: Chip Audette, Dec 2016 - Jan 2017 + Purpose; Apply dynamic range compression to the audio stream. + Assumes floating-point data. + + This processes a single stream fo audio data (ie, it is mono) + + MIT License. use at your own risk. +*/ + +#ifndef _COMPRESSOR_H +#define _COMPRESSOR_H + +#include //ARM DSP extensions. https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html +#include "synth.h" + +class Compressor +{ + public: + //constructor + Compressor(const float32_t sample_rate_Hz); + + void doCompression(float32_t *audio_block, uint16_t len); + void setDefaultValues(const float32_t sample_rate_Hz); + void setPreGain(float32_t g); + void setPreGain_dB(float32_t gain_dB); + void setCompressionRatio(float32_t cr); + void setAttack_sec(float32_t a, float32_t fs_Hz); + void setRelease_sec(float32_t r, float32_t fs_Hz); + void setLevelTimeConst_sec(float32_t t_sec, float32_t fs_Hz); + void setThresh_dBFS(float32_t val); + void enableHPFilter(boolean flag); + float32_t getPreGain_dB(void); + float32_t getAttack_sec(void); + float32_t getRelease_sec(void); + float32_t getLevelTimeConst_sec(void); + float32_t getThresh_dBFS(void); + float32_t getCompressionRatio(void); + float32_t getCurrentLevel_dBFS(void); + float32_t getCurrentGain_dB(void); + + protected: + void calcAudioLevel_dB(float32_t *wav_block, float32_t *level_dB_block, uint16_t len); + void calcGain(float32_t *audio_level_dB_block, float32_t *gain_block,uint16_t len); + void calcInstantaneousTargetGain(float32_t *audio_level_dB_block, float32_t *inst_targ_gain_dB_block, uint16_t len); + void calcSmoothedGain_dB(float32_t *inst_targ_gain_dB_block, float32_t *gain_dB_block, uint16_t len); + void resetStates(void); + void setHPFilterCoeff_N2IIR_Matlab(float32_t b[], float32_t a[]); + + //state-related variables + float32_t *inputQueueArray_f32[1]; //memory pointer for the input to this module + float32_t prev_level_lp_pow = 1.0; + float32_t prev_gain_dB = 0.0; //last gain^2 used + + //HP filter state-related variables + arm_biquad_casd_df1_inst_f32 hp_filt_struct; + static const uint8_t hp_nstages = 1; + float32_t hp_coeff[5 * hp_nstages] = {1.0, 0.0, 0.0, 0.0, 0.0}; //no filtering. actual filter coeff set later + float32_t hp_state[4 * hp_nstages]; + void setHPFilterCoeff(void); + //private parameters related to gain calculation + float32_t attack_const, release_const, level_lp_const; //used in calcGain(). set by setAttack_sec() and setRelease_sec(); + float32_t comp_ratio_const, thresh_pow_FS_wCR; //used in calcGain(); set in updateThresholdAndCompRatioConstants() + void updateThresholdAndCompRatioConstants(void); + //settings + float32_t attack_sec, release_sec, level_lp_sec; + float32_t thresh_dBFS = 0.0; //threshold for compression, relative to digital full scale + float32_t thresh_pow_FS = 1.0f; //same as above, but not in dB + void setThreshPow(float32_t t_pow); + float32_t comp_ratio = 1.0; //compression ratio + float32_t pre_gain = -1.0; //gain to apply before the compression. negative value disables + boolean use_HP_prefilter; +}; + +// Accelerate the powf(10.0,x) function +static float32_t pow10f(float32_t x); +// Accelerate the log10f(x) function? +static float32_t log10f_approx(float32_t x); +static float32_t log2f_approx(float32_t X); +#endif diff --git a/src/mixer.cpp b/src/effect_mixer.cpp similarity index 78% rename from src/mixer.cpp rename to src/effect_mixer.cpp index 16f4b51..2a4861c 100644 --- a/src/mixer.cpp +++ b/src/effect_mixer.cpp @@ -31,7 +31,7 @@ #include #include #include "arm_math.h" -#include "mixer.h" +#include "effect_mixer.h" template void AudioMixer::gain(uint8_t channel, float32_t gain) { @@ -68,7 +68,14 @@ template void AudioMixer::doAddMix(uint8_t channel, float32_t* in, free(tmp); } -template void AudioStereoMixer::doAddMix(uint8_t channel, float32_t* in[], float32_t* out[], uint16_t len) +template void AudioMixer::get_mix(float32_t* buffer, uint16_t len) +{ + assert(buffer); + assert(sumbuf); + arm_copy_f32 (sumbuf, buffer, len); +} + +template void AudioStereoMixer::doAddMix(uint8_t channel, float32_t* inL, float32_t* inR, float32_t* outL, float32_t* outR, uint16_t len) { float32_t* tmp=malloc(sizeof(float32_t)*len); @@ -78,10 +85,21 @@ template void AudioStereoMixer::doAddMix(uint8_t channel, float32_t for(uint16_t i=0;i::multiplier[channel],tmp,len); + arm_add_f32(outL, tmp, outL, len); // right + arm_scale_f32(inR,AudioMixer::multiplier[channel],tmp,len); + arm_add_f32(outR, tmp, outR, len); } free(tmp); } + +template void AudioMixer::get_mix(float32_t* bufferL, float32_t bufferL, uint16_t len) +{ + assert(bufferR); + assert(bufferL); + assert(sumbuf); + arm_copy_f32 (sumbuf[0], bufferL, len); + arm_copy_f32 (sumbuf[1], bufferR, len); +} diff --git a/src/mixer.h b/src/effect_mixer.h similarity index 77% rename from src/mixer.h rename to src/effect_mixer.h index 7d44cc4..1df26ce 100644 --- a/src/mixer.h +++ b/src/effect_mixer.h @@ -40,12 +40,15 @@ template class AudioMixer { public: - AudioMixer(void) + AudioMixer(uint16_t len) { for (uint8_t i=0; i class AudioStereoMixer : public AudioMixer { public: - AudioStereoMixer(void) + AudioStereoMixer(uint16_t len) { - AudioMixer(); - for (uint8_t i=0; i(len); + for (uint8_t i=0; i(pConfig->GetChunkSize()); + // END setup tgmixer + // BEGIN setup reverb reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate()); SetParameter (ParameterReverbEnable, 1); @@ -762,24 +766,30 @@ void CMiniDexed::ProcessSound (void) assert (CConfig::ToneGenerators == 8); - // BEGIN stereo panorama + // BEGIN stereo panorama and TG mixing for (uint8_t i = 0; i < CConfig::ToneGenerators; i++) { - float32_t tmpBuffer[nFrames]; + float32_t tmpBuffer[2][nFrames]; + + assert (tmpBuffer[0]!=NULL); + arm_fill_f32(0.0, tmpBuffer[0], nFrames); + assert (tmpBuffer[1]!=NULL); + arm_fill_f32(0.0, tmpBuffer[1], nFrames); m_PanoramaSpinLock.Acquire (); // calculate left panorama of this TG - arm_scale_f32(m_OutputLevel[i], 1.0f-m_fPan[i], tmpBuffer, nFrames); + arm_scale_f32(m_OutputLevel[i], 1.0f-m_fPan[i], tmpBuffer[0], nFrames); // add left panorama output of this TG to sum output - arm_add_f32(SampleBuffer[indexL], tmpBuffer, SampleBuffer[indexL], nFrames); + arm_add_f32(SampleBuffer[indexL], tmpBuffer[0], SampleBuffer[indexL], nFrames); // calculate right panorama of this TG - arm_scale_f32(m_OutputLevel[i], m_fPan[i], tmpBuffer, nFrames); + arm_scale_f32(m_OutputLevel[i], m_fPan[i], tmpBuffer[1], nFrames); // add right panaorama output of this TG to sum output - arm_add_f32(SampleBuffer[indexR], tmpBuffer, SampleBuffer[indexR], nFrames); + arm_add_f32(SampleBuffer[indexR], tmpBuffer[1], SampleBuffer[indexR], nFrames); + //tg_mixer->doAddMix(i,tmpBuffer[0],tmpBuffer[1],nFrames); m_PanoramaSpinLock.Release (); } - // END stereo panorama + // END stereo panorama and TG mixing // BEGIN adding reverb if (m_nParameter[ParameterReverbEnable]) diff --git a/src/minidexed.cpp.O b/src/minidexed.cpp.O deleted file mode 100644 index 17a9c77..0000000 --- a/src/minidexed.cpp.O +++ /dev/null @@ -1,853 +0,0 @@ -// -// minidexed.cpp -// -// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi -// Copyright (C) 2022 The MiniDexed Team -// -// 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, see . -// -#include "minidexed.h" -#include -#include -#include -#include -#include -#include -#include -#include - -LOGMODULE ("minidexed"); - -CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, - CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, FATFS *pFileSystem) -: -#ifdef ARM_ALLOW_MULTI_CORE - CMultiCoreSupport (CMemorySystem::Get ()), -#endif - m_pConfig (pConfig), - m_UI (this, pGPIOManager, pConfig), - m_PerformanceConfig (pFileSystem), - m_PCKeyboard (this, pConfig), - m_SerialMIDI (this, pInterrupt, pConfig), - m_bUseSerial (false), - m_pSoundDevice (0), - m_bChannelsSwapped (pConfig->GetChannelsSwapped ()), -#ifdef ARM_ALLOW_MULTI_CORE - m_nActiveTGsLog2 (0), -#endif - m_GetChunkTimer ("GetChunk", - 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), - m_bProfileEnabled (m_pConfig->GetProfileEnabled ()) -{ - assert (m_pConfig); - - for (unsigned i = 0; i < CConfig::ToneGenerators; i++) - { - m_nVoiceBankID[i] = 0; - m_nProgram[i] = 0; - m_nVolume[i] = 100; - m_nPan[i] = 64; - pan_float[i]=0.0f; - m_nMasterTune[i] = 0; - m_nMIDIChannel[i] = CMIDIDevice::Disabled; - - m_nNoteLimitLow[i] = 0; - m_nNoteLimitHigh[i] = 127; - m_nNoteShift[i] = 0; - - m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ()); - assert (m_pTG[i]); - - m_pTG[i]->activate (); - } - - for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) - { - m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i); - assert (m_pMIDIKeyboard[i]); - } - - // select the sound device - const char *pDeviceName = pConfig->GetSoundDevice (); - if (strcmp (pDeviceName, "i2s") == 0) - { - LOGNOTE ("I2S mode"); - - m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), - pConfig->GetChunkSize (), false, - pI2CMaster, pConfig->GetDACI2CAddress ()); - } - else if (strcmp (pDeviceName, "hdmi") == 0) - { - LOGNOTE ("HDMI mode"); - - m_pSoundDevice = new CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), - pConfig->GetChunkSize ()); - - // The channels are swapped by default in the HDMI sound driver. - // TODO: Remove this line, when this has been fixed in the driver. - m_bChannelsSwapped = !m_bChannelsSwapped; - } - else - { - LOGNOTE ("PWM mode"); - - m_pSoundDevice = new CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), - pConfig->GetChunkSize ()); - } - -#ifdef ARM_ALLOW_MULTI_CORE - for (unsigned nCore = 0; nCore < CORES; nCore++) - { - m_CoreStatus[nCore] = CoreStatusInit; - } -#endif - - // BEGIN setup tg_mixer - //tg_mixer = new AudioStereoMixer<8>(); - // END setup tg_mixer - - SetParameter (ParameterCompressorEnable, 1); - - // BEGIN setup reverb - reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate()); - SetParameter (ParameterReverbEnable, 1); - SetParameter (ParameterReverbSize, 70); - SetParameter (ParameterReverbHighDamp, 50); - SetParameter (ParameterReverbLowDamp, 50); - SetParameter (ParameterReverbLowPass, 30); - SetParameter (ParameterReverbDiffusion, 20); - SetParameter (ParameterReverbLevel, 80); - // END setup reverb -}; - -bool CMiniDexed::Initialize (void) -{ - assert (m_pConfig); - assert (m_pSoundDevice); - - if (!m_UI.Initialize ()) - { - return false; - } - - m_SysExFileLoader.Load (); - - if (m_SerialMIDI.Initialize ()) - { - LOGNOTE ("Serial MIDI interface enabled"); - - m_bUseSerial = true; - } - - for (unsigned i = 0; i < CConfig::ToneGenerators; i++) - { - assert (m_pTG[i]); - - SetVolume (100, i); - ProgramChange (0, i); - - m_pTG[i]->setTranspose (24); - - m_pTG[i]->setPBController (12, 1); - m_pTG[i]->setMWController (99, 7, 0); - } - - if (m_PerformanceConfig.Load ()) - { - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) - { - BankSelectLSB (m_PerformanceConfig.GetBankNumber (nTG), nTG); - ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG); - SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG); - SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG); - SetPan (m_PerformanceConfig.GetPan (nTG), nTG); - SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG); - - m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG); - m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG); - m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG); - } - - // Effects - SetParameter (ParameterCompressorEnable, m_PerformanceConfig.GetCompressorEnable () ? 1 : 0); - SetParameter (ParameterReverbEnable, m_PerformanceConfig.GetReverbEnable () ? 1 : 0); - SetParameter (ParameterReverbSize, m_PerformanceConfig.GetReverbSize ()); - SetParameter (ParameterReverbHighDamp, m_PerformanceConfig.GetReverbHighDamp ()); - SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ()); - SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ()); - SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ()); - SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ()); - } - else - { - SetMIDIChannel (CMIDIDevice::OmniMode, 0); - } - - // setup and start the sound device - if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ())) - { - LOGERR ("Cannot allocate sound queue"); - - return false; - } - -#ifndef ARM_ALLOW_MULTI_CORE - m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono -#else - m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 2); // 16-bit Stereo -#endif - - m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames (); - - m_pSoundDevice->Start (); - -#ifdef ARM_ALLOW_MULTI_CORE - // start secondary cores - if (!CMultiCoreSupport::Initialize ()) - { - return false; - } -#endif - - return true; -} - -void CMiniDexed::Process (bool bPlugAndPlayUpdated) -{ -#ifndef ARM_ALLOW_MULTI_CORE - ProcessSound (); -#endif - - for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) - { - assert (m_pMIDIKeyboard[i]); - m_pMIDIKeyboard[i]->Process (bPlugAndPlayUpdated); - } - - m_PCKeyboard.Process (bPlugAndPlayUpdated); - - if (m_bUseSerial) - { - m_SerialMIDI.Process (); - } - - m_UI.Process (); - - if (m_bProfileEnabled) - { - m_GetChunkTimer.Dump (); - } -} - -#ifdef ARM_ALLOW_MULTI_CORE - -void CMiniDexed::Run (unsigned nCore) -{ - assert (1 <= nCore && nCore < CORES); - - if (nCore == 1) - { - m_CoreStatus[nCore] = CoreStatusIdle; // core 1 ready - - // wait for cores 2 and 3 to be ready - for (unsigned nCore = 2; nCore < CORES; nCore++) - { - while (m_CoreStatus[nCore] != CoreStatusIdle) - { - // just wait - } - } - - while (m_CoreStatus[nCore] != CoreStatusExit) - { - ProcessSound (); - } - } - else // core 2 and 3 - { - while (1) - { - m_CoreStatus[nCore] = CoreStatusIdle; // ready to be kicked - while (m_CoreStatus[nCore] == CoreStatusIdle) - { - // just wait - } - - // now kicked from core 1 - - if (m_CoreStatus[nCore] == CoreStatusExit) - { - m_CoreStatus[nCore] = CoreStatusUnknown; - - break; - } - - assert (m_CoreStatus[nCore] == CoreStatusBusy); - - // process the TGs, assigned to this core (2 or 3) - - assert (m_nFramesToProcess <= CConfig::MaxChunkSize); - unsigned nTG = CConfig::TGsCore1 + (nCore-2)*CConfig::TGsCore23; - for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++) - { - assert (m_pTG[nTG]); - m_pTG[nTG]->getSamples (m_OutputLevel[nTG], m_nFramesToProcess); - } - } - } -} - -#endif - -CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void) -{ - return &m_SysExFileLoader; -} - -void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG) -{ - if (nBankLSB > 127) - { - return; - } - - assert (nTG < CConfig::ToneGenerators); - m_nVoiceBankID[nTG] = nBankLSB; - - m_UI.ParameterChanged (); -} - -void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG) -{ - if (nProgram > 31) - { - return; - } - - assert (nTG < CConfig::ToneGenerators); - m_nProgram[nTG] = nProgram; - - uint8_t Buffer[156]; - m_SysExFileLoader.GetVoice (m_nVoiceBankID[nTG], nProgram, Buffer); - - assert (m_pTG[nTG]); - m_pTG[nTG]->loadVoiceParameters (Buffer); - - m_UI.ParameterChanged (); -} - -void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG) -{ - if (nVolume > 127) - { - return; - } - - assert (nTG < CConfig::ToneGenerators); - m_nVolume[nTG] = nVolume; - - assert (m_pTG[nTG]); - m_pTG[nTG]->setGain (nVolume / 127.0); - - m_UI.ParameterChanged (); -} - -void CMiniDexed::SetPan (unsigned nPan, unsigned nTG) -{ - constrain(nPan,-1.0f,1.0f); - - assert (nTG < CConfig::ToneGenerators); - m_nPan[nTG] = nPan; - pan_float[nTG]=mapfloat(nPan,0,127,-1.0,1.0); - - m_UI.ParameterChanged (); -} - -void CMiniDexed::SetMasterTune (int nMasterTune, unsigned nTG) -{ - if (!(-99 <= nMasterTune && nMasterTune <= 99)) - { - return; - } - - assert (nTG < CConfig::ToneGenerators); - m_nMasterTune[nTG] = nMasterTune; - - assert (m_pTG[nTG]); - m_pTG[nTG]->setMasterTune ((int8_t) nMasterTune); - - m_UI.ParameterChanged (); -} - -void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - m_nMIDIChannel[nTG] = uchChannel; - - for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) - { - assert (m_pMIDIKeyboard[i]); - m_pMIDIKeyboard[i]->SetChannel (uchChannel, nTG); - } - - m_PCKeyboard.SetChannel (uchChannel, nTG); - - if (m_bUseSerial) - { - m_SerialMIDI.SetChannel (uchChannel, nTG); - } - -#ifdef ARM_ALLOW_MULTI_CORE - unsigned nActiveTGs = 0; - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) - { - if (m_nMIDIChannel[nTG] != CMIDIDevice::Disabled) - { - nActiveTGs++; - } - } - - assert (nActiveTGs <= 8); - static const unsigned Log2[] = {0, 0, 1, 2, 2, 3, 3, 3, 3}; - m_nActiveTGsLog2 = Log2[nActiveTGs]; -#endif - - m_UI.ParameterChanged (); -} - -void CMiniDexed::keyup (int16_t pitch, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - - pitch = ApplyNoteLimits (pitch, nTG); - if (pitch >= 0) - { - m_pTG[nTG]->keyup (pitch); - } -} - -void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - - pitch = ApplyNoteLimits (pitch, nTG); - if (pitch >= 0) - { - m_pTG[nTG]->keydown (pitch, velocity); - } -} - -int16_t CMiniDexed::ApplyNoteLimits (int16_t pitch, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - - if ( pitch < (int16_t) m_nNoteLimitLow[nTG] - || pitch > (int16_t) m_nNoteLimitHigh[nTG]) - { - return -1; - } - - pitch += m_nNoteShift[nTG]; - - if ( pitch < 0 - || pitch > 127) - { - return -1; - } - - return pitch; -} - -void CMiniDexed::setSustain(bool sustain, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - m_pTG[nTG]->setSustain (sustain); -} - -void CMiniDexed::setModWheel (uint8_t value, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - m_pTG[nTG]->setModWheel (value); -} - -void CMiniDexed::setPitchbend (int16_t value, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - m_pTG[nTG]->setPitchbend (value); -} - -void CMiniDexed::ControllersRefresh (unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - m_pTG[nTG]->ControllersRefresh (); -} - -void CMiniDexed::SetParameter (TParameter Parameter, int nValue) -{ - assert (reverb); - - assert (Parameter < ParameterUnknown); - m_nParameter[Parameter] = nValue; - - switch (Parameter) - { -<<<<<<< HEAD - case ParameterReverbSize: reverb->size (fValue); break; - case ParameterReverbHighDamp: reverb->hidamp (fValue); break; - case ParameterReverbLowDamp: reverb->lodamp (fValue); break; - case ParameterReverbLowPass: reverb->lowpass (fValue); break; - case ParameterReverbDiffusion: reverb->diffusion (fValue); break; - case ParameterReverbLevel: reverb->level (fValue); break; -======= - case ParameterCompressorEnable: - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) - { - assert (m_pTG[nTG]); - m_pTG[nTG]->setCompressor (!!nValue); - } - break; - - case ParameterReverbEnable: - m_ReverbSpinLock.Acquire (); - reverb->set_bypass (!nValue); - m_ReverbSpinLock.Release (); - break; - - case ParameterReverbSize: - m_ReverbSpinLock.Acquire (); - reverb->size (nValue / 99.0); - m_ReverbSpinLock.Release (); - break; - - case ParameterReverbHighDamp: - m_ReverbSpinLock.Acquire (); - reverb->hidamp (nValue / 99.0); - m_ReverbSpinLock.Release (); - break; - - case ParameterReverbLowDamp: - m_ReverbSpinLock.Acquire (); - reverb->lodamp (nValue / 99.0); - m_ReverbSpinLock.Release (); - break; - - case ParameterReverbLowPass: - m_ReverbSpinLock.Acquire (); - reverb->lowpass (nValue / 99.0); - m_ReverbSpinLock.Release (); - break; - - case ParameterReverbDiffusion: - m_ReverbSpinLock.Acquire (); - reverb->diffusion (nValue / 99.0); - m_ReverbSpinLock.Release (); - break; - - case ParameterReverbLevel: - m_ReverbSpinLock.Acquire (); - reverb->level (nValue / 99.0); - m_ReverbSpinLock.Release (); - break; - - default: - assert (0); - break; - } -} - -int CMiniDexed::GetParameter (TParameter Parameter) -{ - assert (Parameter < ParameterUnknown); - return m_nParameter[Parameter]; -} - -void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - - switch (Parameter) - { - case TGParameterVoiceBank: BankSelectLSB (nValue, nTG); break; - case TGParameterProgram: ProgramChange (nValue, nTG); break; - case TGParameterVolume: SetVolume (nValue, nTG); break; - case TGParameterPan: SetPan (nValue, nTG); break; - case TGParameterMasterTune: SetMasterTune (nValue, nTG); break; - - case TGParameterMIDIChannel: - assert (0 <= nValue && nValue <= 255); - SetMIDIChannel ((uint8_t) nValue, nTG); - break; - - default: - assert (0); - break; - } -} - -int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - - switch (Parameter) - { - case TGParameterVoiceBank: return m_nVoiceBankID[nTG]; - case TGParameterProgram: return m_nProgram[nTG]; - case TGParameterVolume: return m_nVolume[nTG]; - case TGParameterPan: return m_nPan[nTG]; - case TGParameterMasterTune: return m_nMasterTune[nTG]; - case TGParameterMIDIChannel: return m_nMIDIChannel[nTG]; - - default: - assert (0); - return 0; - } -} - -void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - assert (nOP <= 6); - - if (nOP < 6) - { - nOP = 5 - nOP; // OPs are in reverse order - } - - uchOffset += nOP * 21; - assert (uchOffset < 156); - - m_pTG[nTG]->setVoiceDataElement (uchOffset, uchValue); -} - -uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned nTG) -{ - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - assert (nOP <= 6); - - if (nOP < 6) - { - nOP = 5 - nOP; // OPs are in reverse order - } - - uchOffset += nOP * 21; - assert (uchOffset < 156); - - return m_pTG[nTG]->getVoiceDataElement (uchOffset); -} - -std::string CMiniDexed::GetVoiceName (unsigned nTG) -{ - char VoiceName[11]; - memset (VoiceName, 0, sizeof VoiceName); - - assert (nTG < CConfig::ToneGenerators); - assert (m_pTG[nTG]); - m_pTG[nTG]->setName (VoiceName); - - std::string Result (VoiceName); - - return Result; -} - -#ifndef ARM_ALLOW_MULTI_CORE - -void CMiniDexed::ProcessSound (void) -{ - assert (m_pSoundDevice); - - unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail (); - if (nFrames >= m_nQueueSizeFrames/2) - { - if (m_bProfileEnabled) - { - m_GetChunkTimer.Start (); - } - - //int16_t SampleBuffer[nFrames]; // TODO float->int - m_pTG[0]->getSamples (SampleBuffer, nFrames); - - if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer) - != (int) sizeof SampleBuffer) - { - LOGERR ("Sound data dropped"); - } - - if (m_bProfileEnabled) - { - m_GetChunkTimer.Stop (); - } - } -} - -#else // #ifdef ARM_ALLOW_MULTI_CORE - -void CMiniDexed::ProcessSound (void) -{ - assert (m_pSoundDevice); - - unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail (); - if (nFrames >= m_nQueueSizeFrames/2) - { - if (m_bProfileEnabled) - { - m_GetChunkTimer.Start (); - } - - m_nFramesToProcess = nFrames; - - // kick secondary cores - for (unsigned nCore = 2; nCore < CORES; nCore++) - { - assert (m_CoreStatus[nCore] == CoreStatusIdle); - m_CoreStatus[nCore] = CoreStatusBusy; - } - - // process the TGs assigned to core 1 - assert (nFrames <= CConfig::MaxChunkSize); - for (unsigned i = 0; i < CConfig::TGsCore1; i++) - { - assert (m_pTG[i]); - m_pTG[i]->getSamples (m_OutputLevel[i], nFrames); - } - - // wait for cores 2 and 3 to complete their work - for (unsigned nCore = 2; nCore < CORES; nCore++) - { - while (m_CoreStatus[nCore] != CoreStatusIdle) - { - // just wait - } - } - - // - // Audio signal path after tone generators starts here - // - - // now mix the output of all TGs - float32_t SampleBuffer[2][nFrames]; - uint8_t indexL=0, indexR=1; - - if (m_bChannelsSwapped) - { - indexL=1; - indexR=0; - } - - // init left sum output - assert (SampleBuffer[0]!=NULL); - arm_fill_f32(0.0, SampleBuffer[0], nFrames); - // init right sum output - assert (SampleBuffer[1]!=NULL); - arm_fill_f32(0.0, SampleBuffer[1], nFrames); - - assert (CConfig::ToneGenerators == 8); - - // BEGIN stereo panorama - for (uint8_t i = 0; i < CConfig::ToneGenerators; i++) - { - float32_t tmpBuffer[nFrames]; - - m_PanoramaSpinLock.Acquire (); - // calculate left panorama of this TG - arm_scale_f32(m_OutputLevel[i], 1.0f-pan_float[i], tmpBuffer, nFrames); - // add left panorama output of this TG to sum output - arm_add_f32(SampleBuffer[indexL], tmpBuffer, SampleBuffer[indexL], nFrames); - - // calculate right panorama of this TG - arm_scale_f32(m_OutputLevel[i], pan_float[i], tmpBuffer, nFrames); - // add right panaorama output of this TG to sum output - arm_add_f32(SampleBuffer[indexR], tmpBuffer, SampleBuffer[indexR], nFrames); - - m_PanoramaSpinLock.Release (); - } - // END stereo panorama - - // BEGIN adding reverb - if (m_nParameter[ParameterReverbEnable]) - { - float32_t ReverbBuffer[2][nFrames]; - - m_ReverbSpinLock.Acquire (); - reverb->doReverb(SampleBuffer[indexL],SampleBuffer[indexR],ReverbBuffer[0], ReverbBuffer[1],nFrames); - m_ReverbSpinLock.Release (); - - // scale down and add left reverb buffer by reverb level - arm_scale_f32(ReverbBuffer[0], reverb->get_level(), ReverbBuffer[0], nFrames); - arm_add_f32(SampleBuffer[indexL], ReverbBuffer[0], SampleBuffer[indexL], nFrames); - // scale down and add right reverb buffer by reverb level - arm_scale_f32(ReverbBuffer[1], reverb->get_level(), ReverbBuffer[1], nFrames); - arm_add_f32(SampleBuffer[indexR], ReverbBuffer[1], SampleBuffer[indexR], nFrames); - } - // END adding reverb - - // Convert dual float array (left, right) to single int16 array (left/right) - float32_t tmp_float[nFrames*2]; - int16_t tmp_int[nFrames*2]; - for(uint16_t i=0; iWrite (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int)) - { - LOGERR ("Sound data dropped"); - } - - if (m_bProfileEnabled) - { - m_GetChunkTimer.Stop (); - } - } -} - -#endif - -bool CMiniDexed::SavePerformance (void) -{ - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) - { - m_PerformanceConfig.SetBankNumber (m_nVoiceBankID[nTG], nTG); - m_PerformanceConfig.SetVoiceNumber (m_nProgram[nTG], nTG); - m_PerformanceConfig.SetMIDIChannel (m_nMIDIChannel[nTG], nTG); - m_PerformanceConfig.SetVolume (m_nVolume[nTG], nTG); - m_PerformanceConfig.SetPan (m_nPan[nTG], nTG); - m_PerformanceConfig.SetDetune (m_nMasterTune[nTG], nTG); - - m_PerformanceConfig.SetNoteLimitLow (m_nNoteLimitLow[nTG], nTG); - m_PerformanceConfig.SetNoteLimitHigh (m_nNoteLimitHigh[nTG], nTG); - m_PerformanceConfig.SetNoteShift (m_nNoteShift[nTG], nTG); - } - - m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[ParameterCompressorEnable]); - m_PerformanceConfig.SetReverbEnable (!!m_nParameter[ParameterReverbEnable]); - m_PerformanceConfig.SetReverbSize (m_nParameter[ParameterReverbSize]); - m_PerformanceConfig.SetReverbHighDamp (m_nParameter[ParameterReverbHighDamp]); - m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]); - m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]); - m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]); - m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]); - - return m_PerformanceConfig.Save (); -} diff --git a/src/minidexed.h b/src/minidexed.h index 1aed730..f343879 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -41,7 +41,8 @@ #include #include "common.h" #include "effect_platervbstereo.h" -#include "mixer.h" +#include "effect_compressor.h" +//#include "effect_mixer.h" class CMiniDexed #ifdef ARM_ALLOW_MULTI_CORE @@ -175,7 +176,7 @@ private: bool m_bProfileEnabled; AudioEffectPlateReverb* reverb; - AudioStereoMixer<8>* tg_mixer; + //AudioStereoMixer<8>* tg_mixer; CSpinLock m_PanoramaSpinLock; CSpinLock m_ReverbSpinLock;