Removed compressor from AudioEffectDynamics library because of wasting CPU time and it seems to be buggy on T_3.6.

Tested all CPU clock modes on T_3.6.
Limited T_3.6 to single engine.
pull/46/head
Holger Wirtz 3 years ago
parent 75901c90d6
commit 60ac8c6b02
  1. 17
      MicroDexed.ino
  2. 44
      config.h
  3. 34
      third-party/AudioEffectDynamics/.gitignore
  4. 7
      third-party/AudioEffectDynamics/README.md
  5. 155
      third-party/AudioEffectDynamics/effect_dynamics.cpp
  6. 196
      third-party/AudioEffectDynamics/effect_dynamics.h
  7. 50
      third-party/AudioEffectDynamics/fast_log.h
  8. 4
      third-party/Synth_Dexed/README.md
  9. 65
      third-party/Synth_Dexed/examples/Banks/Banks.ino
  10. 5788
      third-party/Synth_Dexed/examples/Banks/banks.h
  11. 0
      third-party/Synth_Dexed/examples/synth_dexed.ino
  12. 16
      third-party/Synth_Dexed/src/synth_dexed.cpp
  13. 2
      third-party/Synth_Dexed/src/synth_dexed.h
  14. 151
      third-party/Synth_Dexed/tools/sysex2c.py

@ -45,7 +45,6 @@
// Audio engines // Audio engines
AudioSynthDexed* MicroDexed[NUM_DEXED]; AudioSynthDexed* MicroDexed[NUM_DEXED];
AudioEffectDynamics* dynamics[NUM_DEXED];
#if defined(USE_FX) #if defined(USE_FX)
AudioSynthWaveform* chorus_modulator[NUM_DEXED]; AudioSynthWaveform* chorus_modulator[NUM_DEXED];
@ -177,7 +176,7 @@ AudioConnection * dynamicConnections[NUM_DEXED * 5];
void create_audio_engine_chain(uint8_t instance_id) void create_audio_engine_chain(uint8_t instance_id)
{ {
MicroDexed[instance_id] = new AudioSynthDexed(MAX_NOTES / NUM_DEXED, SAMPLE_RATE); MicroDexed[instance_id] = new AudioSynthDexed(MAX_NOTES / NUM_DEXED, SAMPLE_RATE);
dynamics[instance_id] = new AudioEffectDynamics(); //[instance_id] = new AudioEffectDynamics();
mono2stereo[instance_id] = new AudioEffectMonoStereo(); mono2stereo[instance_id] = new AudioEffectMonoStereo();
#if defined(USE_FX) #if defined(USE_FX)
chorus_modulator[instance_id] = new AudioSynthWaveform(); chorus_modulator[instance_id] = new AudioSynthWaveform();
@ -191,8 +190,7 @@ void create_audio_engine_chain(uint8_t instance_id)
delay_mixer[instance_id] = new AudioMixer4(); delay_mixer[instance_id] = new AudioMixer4();
#endif #endif
dynamicConnections[nDynamic++] = new AudioConnection(*MicroDexed[instance_id], 0, *dynamics[instance_id], 0); dynamicConnections[nDynamic++] = new AudioConnection(*MicroDexed[instance_id], 0, microdexed_peak_mixer, instance_id);
dynamicConnections[nDynamic++] = new AudioConnection(*dynamics[instance_id], 0, microdexed_peak_mixer, instance_id);
#if defined(USE_FX) #if defined(USE_FX)
dynamicConnections[nDynamic++] = new AudioConnection(*MicroDexed[instance_id], 0, *chorus_mixer[instance_id], 0); dynamicConnections[nDynamic++] = new AudioConnection(*MicroDexed[instance_id], 0, *chorus_mixer[instance_id], 0);
@ -1872,15 +1870,6 @@ void set_fx_params(void)
#if defined(USE_FX) #if defined(USE_FX)
for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++) for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++)
{ {
// COMPRESSOR
#if defined(LIMIT_THRESHOLD)
dynamics[instance_id]->limit(LIMIT_THRESHOLD, LIMIT_ATTACK, LIMIT_RELEASE);
#endif
#if defined(COMP_THRESHOLD)
dynamics[instance_id]->compression(COMP_THRESHOLD, COMP_ATTACK, COMP_RELEASE, COMP_RATIO, COMP_KNEE_WIDTH);
dynamics[instance_id]->makeupGain(COMP_MAKEUPGAIN);
#endif
// CHORUS // CHORUS
switch (configuration.fx.chorus_waveform[instance_id]) switch (configuration.fx.chorus_waveform[instance_id])
{ {
@ -2326,6 +2315,8 @@ void show_cpu_and_mem_usage(void)
Serial.print(instance_id, DEC); Serial.print(instance_id, DEC);
Serial.print(F("=")); Serial.print(F("="));
Serial.print(active_voices[instance_id], DEC); Serial.print(active_voices[instance_id], DEC);
Serial.print(F("/"));
Serial.print(MAX_NOTES, DEC);
if (instance_id != NUM_DEXED - 1) if (instance_id != NUM_DEXED - 1)
Serial.print(F(",")); Serial.print(F(","));
} }

@ -106,18 +106,7 @@
#endif #endif
// FX-CHAIN ENABLE/DISABLE // FX-CHAIN ENABLE/DISABLE
#define USE_FX 1 //#define USE_FX 1
// COMPRESSOR/ LIMITER
#define COMP_THRESHOLD -20.0
#define COMP_ATTACK 0.2
#define COMP_RELEASE 0.5
#define COMP_RATIO 2.0
#define COMP_KNEE_WIDTH 1.0
#define COMP_MAKEUPGAIN 1.0
#define LIMIT_THRESHOLD -1.0
#define LIMIT_ATTACK 0.03
#define LIMIT_RELEASE 0.2
// CHORUS parameters // CHORUS parameters
#define MOD_DELAY_SAMPLE_BUFFER int32_t(TIME_MS2SAMPLES(20.0)) // 20.0 ms delay buffer. #define MOD_DELAY_SAMPLE_BUFFER int32_t(TIME_MS2SAMPLES(20.0)) // 20.0 ms delay buffer.
@ -316,21 +305,29 @@
// Teensy-3.6 settings // Teensy-3.6 settings
#if defined(TEENSY3_6) #if defined(TEENSY3_6)
# if defined(USE_FX) # if defined(USE_FX)
# warning >>> With enabled FX only maximum of 8 voices is possible # warning >>> With enabled FX a maximum of 14 voices is possible (due to CPU limitations)
# define MAX_NOTES 8 # define MAX_NOTES 14
# if F_CPU > 180000000
# warning >>> Enabled FX with clockrate more than 180MHz is not useful due to RAM limitations.
# endif
# else # else
# if F_CPU == 256000000 # if F_CPU == 256000000
# warning >>> Maximum of 16 voices. # warning >>> Maximum of 24 voices.
# define MAX_NOTES 16 # define MAX_NOTES 24
# elif F_CPU == 240000000 # elif F_CPU == 240000000
# warning >>> Maximum of 14 voices. You should consider to use 256MHz overclocking to get a maximum of 16 voices # warning >>> Maximum of 22 voices. You should consider to use 256MHz overclocking to get a maximum of 24 voices.
# define MAX_NOTES 14 # define MAX_NOTES 22
# elif F_CPU == 216000000 # elif F_CPU == 216000000
# warning >>> Maximum of 12 voices. You should consider to use 256MHz overclocking to get a maximum of 16 voices # warning >>> Maximum of 20 voices. You should consider to use 256MHz overclocking to get a maximum of 24 voices.
# define MAX_NOTES 12 # define MAX_NOTES 20
# elif F_CPU == 192000000
# warning >>> Maximum of 18 voices. You should consider to use 256MHz overclocking to get a maximum of 24 voices.
# define MAX_NOTES 18
# elif F_CPU == 180000000
# warning >>> Maximum of 16 voices. You should consider to use 256MHz overclocking to get a maximum of 24 voices.
# define MAX_NOTES 16
# else # else
# warning >>> Maximum of 10 voices. You should consider to use 256MHz overclocking to get a maximum of 16 voices # error >>> CPU Clock below 180MHz is not supported
# define MAX_NOTES 10
# endif # endif
# endif # endif
#endif #endif
@ -354,6 +351,9 @@
// HELPER MACROS // HELPER MACROS
#define TIME_MS2SAMPLES(x) floor(uint32_t(x) * AUDIO_SAMPLE_RATE / 1000) * sizeof(int16_t) #define TIME_MS2SAMPLES(x) floor(uint32_t(x) * AUDIO_SAMPLE_RATE / 1000) * sizeof(int16_t)
#define SAMPLES2TIME_MS(x) float(uint32_t(x) * 1000 / AUDIO_SAMPLE_RATE * sizeof(int16_t) #define SAMPLES2TIME_MS(x) float(uint32_t(x) * 1000 / AUDIO_SAMPLE_RATE * sizeof(int16_t)
#define XSTR(x) STR(x)
#define STR(x) #x
// Modulated delay options // Modulated delay options
#define MOD_NO_FILTER_OUTPUT 0 #define MOD_NO_FILTER_OUTPUT 0
#define MOD_BUTTERWORTH_FILTER_OUTPUT 1 #define MOD_BUTTERWORTH_FILTER_OUTPUT 1

@ -1,34 +0,0 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
cmake-build-debug

@ -1,7 +0,0 @@
# AudioEffectDynamics
Dynamics Processor (Gate, Compressor &amp; Limiter) for the Teensy Audio Library
# my version of [MarkzP/AudioEffectDynamics](https://github.com/MarkzP/AudioEffectDynamics)
* removes theoretical divide by zero when rms == 0 (sqrt_uint32(0) will divide by zero, thou it doesnt seem to cause any issues)
* uses running rms calculation and applies gain per sample instead of rms calculation per audio block and applying a constant gain to entire audio buffer
* adds some build stuff

@ -1,155 +0,0 @@
/* 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);
if (sum == 0) return 0;
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;
}
for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
unsigned int sampleIndexPlus1 = (sampleIndex + 1) % sampleBufferSize;
uint32_t sampleToRemove = samplesSquared[sampleIndexPlus1];
sumOfSamplesSquared -= (sampleToRemove * sampleToRemove);
int16_t sample = block->data[i];
samplesSquared[sampleIndex] = abs(sample);
uint32_t sampleSquared = sample * sample;
sumOfSamplesSquared += sampleSquared;
sampleIndex = (sampleIndex + 1) % sampleBufferSize;
float rms = sqrt(sumOfSamplesSquared / float(sampleBufferSize)) / 32768.0;
//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;
float multiplier = dbToUnit(totalGain);
int16_t result = sample * multiplier;
block->data[i] = result;
//Apply gain to block
}
//Transmit & release
transmit(block);
release(block);
}

@ -1,196 +0,0 @@
/* 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();
gatedb = MIN_DB;
compdb = MIN_DB;
limitdb = MIN_DB;
}
//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;
}
//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;
}
//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);
}
//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;
const static unsigned int sampleBufferSize = 4410;
u_int64_t sumOfSamplesSquared = 0;
uint32_t samplesSquared[sampleBufferSize] = {0};
uint16_t sampleIndex = 0;
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

@ -1,50 +0,0 @@
/* ----------------------------------------------------------------------
* 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);
}

@ -36,7 +36,7 @@ See example sketch.
void setMaxNotes(uint8_t n); void setMaxNotes(uint8_t n);
uint8_t getMaxNotes(void); uint8_t getMaxNotes(void);
void doRefreshVoice(void); void doRefreshVoice(void);
bool decodeVoice(uint8_t* data, uint8_t* encoded_data); bool decodeVoice(uint8_t* encoded_data, uint8_t* data);
bool encodeVoice(uint8_t* encoded_data); bool encodeVoice(uint8_t* encoded_data);
bool getVoiceData(uint8_t* data_copy); bool getVoiceData(uint8_t* data_copy);
void setVoiceDataElement(uint8_t address, uint8_t value); void setVoiceDataElement(uint8_t address, uint8_t value);
@ -169,4 +169,4 @@ See example sketch.
void setTranspose(uint8_t transpose); void setTranspose(uint8_t transpose);
uint8_t getTranspose(void); uint8_t getTranspose(void);
void setName(char* name); void setName(char* name);
void getName(char* buffer); void getName(char* buffer);

@ -1,65 +0,0 @@
#include <Audio.h>
#include "synth_dexed.h"
#include "banks.h"
AudioSynthDexed dexed(4, SAMPLE_RATE); // 4 voices max
AudioOutputI2S i2s1;
AudioControlSGTL5000 sgtl5000_1;
AudioConnection patchCord1(dexed, 0, i2s1, 0);
AudioConnection patchCord2(dexed, 0, i2s1, 1);
void setup()
{
Serial.begin(230400);
AudioMemory(32);
sgtl5000_1.enable();
sgtl5000_1.lineOutLevel(29);
sgtl5000_1.dacVolumeRamp();
sgtl5000_1.dacVolume(1.0);
sgtl5000_1.unmuteHeadphone();
sgtl5000_1.unmuteLineout();
sgtl5000_1.volume(0.8, 0.8); // Headphone volume
randomSeed(analogRead(0));
}
void loop()
{
uint8_t b = random(0, 10);
uint8_t v = random(0, 32);
uint8_t t = random(0, 36);
char voice_name[11];
uint8_t decoded_voice[156];
memset(voice_name, 0, 11);
memcpy(voice_name, &progmem_bank[b][v][117], 10);
Serial.print("Voice: ");
Serial.print(voice_name);
Serial.print(" ");
Serial.print("Transpose: ");
Serial.println(t);
dexed.decodeVoice(progmem_bank[b][v],decoded_voice);
dexed.loadVoiceParameters(decoded_voice);
dexed.setTranspose(t);
Serial.println("Key-Down");
dexed.keydown(48, 100);
delay(100);
dexed.keydown(52, 100);
delay(100);
dexed.keydown(55, 100);
delay(100);
dexed.keydown(60, 100);
delay(2000);
Serial.println("Key-Up");
dexed.keyup(48);
dexed.keyup(52);
dexed.keyup(55);
dexed.keyup(60);
delay(1000);
}

File diff suppressed because it is too large Load Diff

@ -338,7 +338,6 @@ void Dexed::setMaxNotes(uint8_t new_max_notes)
Serial.print("Allocating memory for "); Serial.print("Allocating memory for ");
Serial.print(max_notes,DEC); Serial.print(max_notes,DEC);
Serial.println(" notes."); Serial.println(" notes.");
Serial.println();
#endif #endif
if(voices) if(voices)
@ -349,17 +348,17 @@ void Dexed::setMaxNotes(uint8_t new_max_notes)
if(voices[i].dx7_note) if(voices[i].dx7_note)
delete voices[i].dx7_note; delete voices[i].dx7_note;
} }
delete voices; delete(voices);
} }
max_notes=new_max_notes; max_notes=constrain(new_max_notes,0,_MAX_NOTES);
if(max_notes>0) if(max_notes>0)
{ {
voices=new ProcessorVoice[max_notes]; // sizeof(ProcessorVoice) = 20 voices=new ProcessorVoice[max_notes];
for (i = 0; i < max_notes; i++) for (i = 0; i < max_notes; i++)
{ {
voices[i].dx7_note = new Dx7Note; // sizeof(Dx7Note) = 692 voices[i].dx7_note = new Dx7Note;
voices[i].keydown = false; voices[i].keydown = false;
voices[i].sustained = false; voices[i].sustained = false;
voices[i].live = false; voices[i].live = false;
@ -386,10 +385,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];
#ifdef USE_SIMPLE_COMPRESSOR
float s; float s;
const double decayFactor = 0.99992; const double decayFactor = 0.99992;
#endif
if (refreshVoice) if (refreshVoice)
{ {
@ -442,19 +439,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()!!!
#ifdef USE_SIMPLE_COMPRESSOR
// mild compression // mild compression
for (i = 0; i < n_samples; i++) for (i = 0; i < n_samples; i++)
{ {
s = abs(sumbuf[i]); s = abs(sumbuf[i]);
if (s > vuSignal) if (s > vuSignal)
vuSignal = s; vuSignal = s;
else if (vuSignal > 0.001f) //else if (vuSignal > 0.001f)
else if (vuSignal > 0.0005f)
vuSignal *= decayFactor; vuSignal *= decayFactor;
else else
vuSignal = 0.0; vuSignal = 0.0;
} }
#endif
//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);

@ -59,8 +59,6 @@
#define PORTAMENTO_GLISSANDO_DEFAULT 0 #define PORTAMENTO_GLISSANDO_DEFAULT 0
#define PORTAMENTO_TIME_DEFAULT 0 #define PORTAMENTO_TIME_DEFAULT 0
//#define USE_SIMPLE_COMPRESSOR 1
/***************************************************** /*****************************************************
CODE: orig_code/synth.h CODE: orig_code/synth.h
*****************************************************/ *****************************************************/

@ -1,151 +0,0 @@
#!/usr/bin/python3
import sys
import os.path
# From: https://github.com/bwhitman/learnfm/blob/master/dx7db.py
def unpack_packed_patch(p):
# Input is a 128 byte thing from compact.bin
# Output is a 156 byte thing that the synth knows about
o = [0]*156
for op in range(6):
o[op*21:op*21 + 11] = p[op*17:op*17+11]
leftrightcurves = p[op*17+11]
o[op * 21 + 11] = leftrightcurves & 3
o[op * 21 + 12] = (leftrightcurves >> 2) & 3
detune_rs = p[op * 17 + 12]
o[op * 21 + 13] = detune_rs & 7
o[op * 21 + 20] = detune_rs >> 3
kvs_ams = p[op * 17 + 13]
o[op * 21 + 14] = kvs_ams & 3
o[op * 21 + 15] = kvs_ams >> 2
o[op * 21 + 16] = p[op * 17 + 14]
fcoarse_mode = p[op * 17 + 15]
o[op * 21 + 17] = fcoarse_mode & 1
o[op * 21 + 18] = fcoarse_mode >> 1
o[op * 21 + 19] = p[op * 17 + 16]
o[126:126+9] = p[102:102+9]
oks_fb = p[111]
o[135] = oks_fb & 7
o[136] = oks_fb >> 3
o[137:137+4] = p[112:112+4]
lpms_lfw_lks = p[116]
o[141] = lpms_lfw_lks & 1
o[142] = (lpms_lfw_lks >> 1) & 7
o[143] = lpms_lfw_lks >> 4
o[144:144+11] = p[117:117+11]
o[155] = 0x3f
# Clamp the unpacked patches to a known max.
maxes = [
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, # osc6
3, 3, 7, 3, 7, 99, 1, 31, 99, 14,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, # osc5
3, 3, 7, 3, 7, 99, 1, 31, 99, 14,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, # osc4
3, 3, 7, 3, 7, 99, 1, 31, 99, 14,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, # osc3
3, 3, 7, 3, 7, 99, 1, 31, 99, 14,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, # osc2
3, 3, 7, 3, 7, 99, 1, 31, 99, 14,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, # osc1
3, 3, 7, 3, 7, 99, 1, 31, 99, 14,
99, 99, 99, 99, 99, 99, 99, 99, # pitch eg rate & level
31, 7, 1, 99, 99, 99, 99, 1, 5, 7, 48, # algorithm etc
126, 126, 126, 126, 126, 126, 126, 126, 126, 126, # name
127 # operator on/off
]
for i in range(156):
if(o[i] > maxes[i]): o[i] = maxes[i]
if(o[i] < 0): o[i] = 0
return o
def print_header_data(voice_data):
print("\t\t\t",end="")
for y in range(0,len(voice_data)):
if(y!=len(voice_data)-1):
print("%3d, " % voice_data[y],end="")
else:
print("%3d\n" % voice_data[y],end="")
if((y+1)%8==0 and y!=len(voice_data)-1):
print("\n\t\t\t",end="")
def help_message():
print(progname+" [--decode] <sysex1> [<sysex2> ... <sysexn>]")
#---------------------------------------------------------------------------
progname=sys.argv.pop(0)
if(len(sys.argv)==0):
help_message()
exit(1)
if(sys.argv[0]=="-h" or sys.argv[0]=="--help"):
help_message()
exit(1)
if(sys.argv[0]=="--decode"):
decode=True
sys.argv.pop(0)
else:
decode=False
print("""
//
// File generated with sysex2c.py
//
#pragma once
""")
if(decode==True):
print("uint8_t progmem_bank[%d][32][156] PROGMEM =\n{" % int(len(sys.argv)))
else:
print("uint8_t progmem_bank[%d][32][128] PROGMEM =\n{" % int(len(sys.argv)))
for sysex in sys.argv:
if(not os.path.isfile(sysex)):
print("* File "+sysex+" does not exists.")
exit(10)
if(not os.access(sysex,os.R_OK)):
print("* File "+sysex+" does not readable.")
exit(11)
print("\t{\t// %s" % os.path.basename(sysex))
with open(sysex, "rb") as f:
header = f.read(6)
if(header[0]!=240):
print("* %s: Start of sysex not found." % sysex)
exit(200)
if(header[1]!=67):
print("* %s: Manufactorer-ID not Yamaha." % sysex)
exit(201)
if(header[3]!=9):
print("* %s: Not a 32 voice sysex file." % sysex)
exit(202)
byte_count = header[4]*128+header[5]
if(byte_count!=4096):
print("* %s: Byte count mismatch." % sysex)
exit(203)
patch_data=f.read(4096)
check = ~sum(patch_data) + 1 & 0x7F
f.seek(4102) # Bulk checksum
checksum=int.from_bytes(f.read(1),"little")
if(check!=checksum):
print("* %s: Checksum mismatch!" % sysex)
exit(204)
f.seek(6)
for v in range(1,33):
data=f.read(128)
patchname=str(data[118:128].decode('ascii')).upper().replace('\\','')
print("\t\t{\t// %d: %s" % (v, patchname))
if(decode==True):
print_header_data(unpack_packed_patch(data))
else:
print_header_data(data)
if(v!=32):
print("\t\t},")
else:
print("\t\t}")
if(sys.argv[len(sys.argv)-1]==sysex):
print("\t}")
else:
print("\t},")
print("};")
Loading…
Cancel
Save