Several small fixes.

Added newer third-party libraries.
dev
Holger Wirtz 5 months ago
parent be9fa9eda2
commit 136fec3f86
  1. 8
      MicroDexed.ino
  2. 4
      UI.hpp
  3. 2
      config.h
  4. 2
      dexed_sd.h
  5. 349
      third-party/Synth_Dexed/EngineMkI.cpp
  6. 44
      third-party/Synth_Dexed/EngineMkI.h
  7. 66
      third-party/Synth_Dexed/EngineMsfa.cpp
  8. 29
      third-party/Synth_Dexed/EngineMsfa.h
  9. 209
      third-party/Synth_Dexed/EngineOpl.cpp
  10. 39
      third-party/Synth_Dexed/EngineOpl.h
  11. 233
      third-party/Synth_Dexed/PluginFx.cpp
  12. 72
      third-party/Synth_Dexed/PluginFx.h
  13. 37
      third-party/Synth_Dexed/aligned_buf.h
  14. 429
      third-party/Synth_Dexed/compressor.h
  15. 152
      third-party/Synth_Dexed/controllers.h
  16. 1792
      third-party/Synth_Dexed/dexed.cpp
  17. 391
      third-party/Synth_Dexed/dexed.h
  18. 388
      third-party/Synth_Dexed/dx7note.cpp
  19. 82
      third-party/Synth_Dexed/dx7note.h
  20. 190
      third-party/Synth_Dexed/env.cpp
  21. 81
      third-party/Synth_Dexed/env.h
  22. 70
      third-party/Synth_Dexed/exp2.cpp
  23. 80
      third-party/Synth_Dexed/exp2.h
  24. 100
      third-party/Synth_Dexed/fm_core.cpp
  25. 59
      third-party/Synth_Dexed/fm_core.h
  26. 384
      third-party/Synth_Dexed/fm_op_kernel.cpp
  27. 47
      third-party/Synth_Dexed/fm_op_kernel.h
  28. 56
      third-party/Synth_Dexed/freqlut.cpp
  29. 23
      third-party/Synth_Dexed/freqlut.h
  30. 99
      third-party/Synth_Dexed/lfo.cpp
  31. 43
      third-party/Synth_Dexed/lfo.h
  32. 93
      third-party/Synth_Dexed/pitchenv.cpp
  33. 52
      third-party/Synth_Dexed/pitchenv.h
  34. 35
      third-party/Synth_Dexed/porta.cpp
  35. 28
      third-party/Synth_Dexed/porta.h
  36. 144
      third-party/Synth_Dexed/sin.cpp
  37. 62
      third-party/Synth_Dexed/sin.h
  38. 49
      third-party/Synth_Dexed/src/EngineMkI.cpp
  39. 8
      third-party/Synth_Dexed/src/EngineMkI.h
  40. 10
      third-party/Synth_Dexed/src/EngineMsfa.cpp
  41. 2
      third-party/Synth_Dexed/src/EngineMsfa.h
  42. 30
      third-party/Synth_Dexed/src/EngineOpl.cpp
  43. 4
      third-party/Synth_Dexed/src/EngineOpl.h
  44. 10
      third-party/Synth_Dexed/src/PluginFx.cpp
  45. 15
      third-party/Synth_Dexed/src/PluginFx.h
  46. 144
      third-party/Synth_Dexed/src/compressor.h
  47. 18
      third-party/Synth_Dexed/src/controllers.h
  48. 145
      third-party/Synth_Dexed/src/dexed.cpp
  49. 39
      third-party/Synth_Dexed/src/dexed.h
  50. 26
      third-party/Synth_Dexed/src/dx7note.cpp
  51. 2
      third-party/Synth_Dexed/src/dx7note.h
  52. 28
      third-party/Synth_Dexed/src/env.cpp
  53. 26
      third-party/Synth_Dexed/src/env.h
  54. 24
      third-party/Synth_Dexed/src/exp2.h
  55. 4
      third-party/Synth_Dexed/src/fm_core.cpp
  56. 6
      third-party/Synth_Dexed/src/fm_core.h
  57. 4
      third-party/Synth_Dexed/src/fm_op_kernel.cpp
  58. 2
      third-party/Synth_Dexed/src/fm_op_kernel.h
  59. 6
      third-party/Synth_Dexed/src/freqlut.cpp
  60. 10
      third-party/Synth_Dexed/src/lfo.cpp
  61. 6
      third-party/Synth_Dexed/src/pitchenv.cpp
  62. 14
      third-party/Synth_Dexed/src/pitchenv.h
  63. 4
      third-party/Synth_Dexed/src/porta.cpp
  64. 16
      third-party/Synth_Dexed/src/sin.cpp
  65. 16
      third-party/Synth_Dexed/src/sin.h
  66. 8
      third-party/Synth_Dexed/src/synth.h
  67. 2
      third-party/Synth_Dexed/src/synth_dexed.h
  68. 91
      third-party/Synth_Dexed/synth.h
  69. 63
      third-party/Synth_Dexed/synth_dexed.cpp
  70. 72
      third-party/Synth_Dexed/synth_dexed.h
  71. 0
      third-party/teensy-variable-playback/.github/FUNDING.yml
  72. 0
      third-party/teensy-variable-playback/.github/workflows/soundio.yml
  73. 0
      third-party/teensy-variable-playback/.github/workflows/teensy41_ex_array.yml
  74. 0
      third-party/teensy-variable-playback/.github/workflows/teensy41_ex_littlefs.yml
  75. 0
      third-party/teensy-variable-playback/.github/workflows/teensy41_ex_sdraw.yml
  76. 0
      third-party/teensy-variable-playback/.github/workflows/teensy41_ex_sdwav.yml
  77. 0
      third-party/teensy-variable-playback/.github/workflows/teensy41_ex_serialflash.yml
  78. 0
      third-party/teensy-variable-playback/.github/workflows/teensy41_lib.yml
  79. 0
      third-party/teensy-variable-playback/.github/workflows/ubuntu_x64_cmake.yml
  80. 0
      third-party/teensy-variable-playback/.gitignore
  81. 0
      third-party/teensy-variable-playback/CMakeLists.txt
  82. 0
      third-party/teensy-variable-playback/LICENSE
  83. 0
      third-party/teensy-variable-playback/README.md
  84. 0
      third-party/teensy-variable-playback/build-linux.sh
  85. 0
      third-party/teensy-variable-playback/build-t41.sh
  86. 0
      third-party/teensy-variable-playback/cmake/install_arduino_library.cmake
  87. 0
      third-party/teensy-variable-playback/cmake/teensy_variable_playback.cmake.in
  88. 0
      third-party/teensy-variable-playback/cmake/toolchains/teensy41.cmake
  89. 0
      third-party/teensy-variable-playback/cmake/uninstall.cmake
  90. 0
      third-party/teensy-variable-playback/docs/InstallArduino.gif
  91. 0
      third-party/teensy-variable-playback/docs/dependencies.png
  92. 0
      third-party/teensy-variable-playback/examples/CMakeLists.txt
  93. 0
      third-party/teensy-variable-playback/examples/LittleFS/CMakeLists.txt
  94. 0
      third-party/teensy-variable-playback/examples/LittleFS/littlefs_raw.ino
  95. 0
      third-party/teensy-variable-playback/examples/SerialFlash/CMakeLists.txt
  96. 0
      third-party/teensy-variable-playback/examples/SerialFlash/serialflash.ino
  97. 0
      third-party/teensy-variable-playback/examples/array/CMakeLists.txt
  98. 0
      third-party/teensy-variable-playback/examples/array/array.ino
  99. 0
      third-party/teensy-variable-playback/examples/midilooper/CMakeLists.txt
  100. 0
      third-party/teensy-variable-playback/examples/midilooper/midilooper.ino
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1276,7 +1276,7 @@ void handleProgramChange(uint8_t inChannel, uint8_t inProgram) {
}
}
void handleSystemExclusive(byte* sysex, unsigned int len) {
void handleSystemExclusive(uint8_t* sysex, unsigned int len) {
int16_t sysex_return;
for (uint8_t instance_id = 0; instance_id < NUM_DEXED; instance_id++) {
@ -2124,7 +2124,7 @@ void set_voiceconfig_params(uint8_t instance_id) {
microdexed_peak_mixer.gain(instance_id, 1.0);
// Controller
MicroDexed[instance_id]->setMaxNotes(configuration.dexed[instance_id].polyphony);
//MicroDexed[instance_id]->setMaxNotes(configuration.dexed[instance_id].polyphony);
MicroDexed[instance_id]->setPBController(configuration.dexed[instance_id].pb_range, configuration.dexed[instance_id].pb_step);
MicroDexed[instance_id]->setMWController(configuration.dexed[instance_id].mw_range, configuration.dexed[instance_id].mw_assign, configuration.dexed[instance_id].mw_mode);
MicroDexed[instance_id]->setFCController(configuration.dexed[instance_id].fc_range, configuration.dexed[instance_id].fc_assign, configuration.dexed[instance_id].fc_mode);
@ -2347,7 +2347,7 @@ float volume_transform(float in) {
return powf(in, VOLUME_TRANSFORM_EXP);
}
uint32_t crc32(byte* calc_start, uint16_t calc_bytes) // base code from https://www.arduino.cc/en/Tutorial/EEPROMCrc
uint32_t crc32(uint8_t* calc_start, uint16_t calc_bytes) // base code from https://www.arduino.cc/en/Tutorial/EEPROMCrc
{
const uint32_t crc_table[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
@ -2357,7 +2357,7 @@ uint32_t crc32(byte* calc_start, uint16_t calc_bytes) // base code from https:/
};
uint32_t crc = ~0L;
for (byte* index = calc_start; index < (calc_start + calc_bytes); ++index) {
for (uint8_t* index = calc_start; index < (calc_start + calc_bytes); ++index) {
crc = crc_table[(crc ^ *index) & 0x0f] ^ (crc >> 4);
crc = crc_table[(crc ^ (*index >> 4)) & 0x0f] ^ (crc >> 4);
crc = ~crc;

@ -1673,7 +1673,7 @@ void getNoteName(char* noteName, uint8_t noteNumber) {
uint8_t oct_index = noteNumber - 12;
noteNumber -= 21;
snprintf_P(noteName, sizeof(noteName), PSTR("%2s%1d"), notes[noteNumber % 12], oct_index / 12);
snprintf_P(noteName, sizeof(noteName), PSTR("%2S%1d"), notes[noteNumber % 12], oct_index / 12);
}
void UI_func_lowest_note(uint8_t param) {
@ -5529,7 +5529,7 @@ void UI_func_sysex_receive_bank(uint8_t param) {
#endif
char tmp[CONFIG_FILENAME_LEN];
strlcpy(tmp, receive_bank_filename, sizeof(tmp));
snprintf_P(receive_bank_filename, sizeof(receive_bank_filename), PSTR("/%d/%s.syx"), bank_number, tmp);
snprintf_P(receive_bank_filename, sizeof(receive_bank_filename), PSTR("/%2d/%12s.syx"), bank_number, tmp);
#ifdef DEBUG
Serial.print(F("Receiving into bank "));
Serial.print(bank_number);

@ -263,7 +263,7 @@
#define VOICE_NAME_LEN 12 // 11 (plus '\0')
#define FILENAME_LEN BANK_NAME_LEN + VOICE_NAME_LEN
#define CONFIG_FILENAME_LEN 50
#define DRUM_NAME_LEN 16
#define DRUM_NAME_LEN 17
#define PERFORMANCE_NAME_LEN 11
#define FAV_CONFIG_PATH "FAVCFG"

@ -40,7 +40,7 @@ extern uint8_t voice;
extern uint8_t ui_state;
extern uint8_t ui_main_state;
extern config_t configuration;
extern uint32_t crc32(byte* calc_start, uint16_t calc_bytes);
extern uint32_t crc32(uint8_t* calc_start, uint16_t calc_uint8_ts);
extern void set_fx_params(void);
extern void set_voiceconfig_params(uint8_t instance_id);
extern void set_sys_params(void);

@ -0,0 +1,349 @@
/*
* Copyright (C) 2015-2017 Pascal Gauthier.
*
* 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The code is based on ppplay https://github.com/stohrendorf/ppplay and opl3
* math documentation :
* https://github.com/gtaylormb/opl3_fpga/blob/master/docs/opl3math/opl3math.pdf
*
*/
#include "EngineMkI.h"
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstdlib>
#include "sin.h"
#include "exp2.h"
#ifdef DEBUG
#include "time.h"
//#define MKIDEBUG
#endif
const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0};
static const uint16_t NEGATIVE_BIT = 0x8000;
static const uint16_t ENV_BITDEPTH = 14;
static const uint16_t SINLOG_BITDEPTH = 10;
static const uint16_t SINLOG_TABLESIZE = 1<<SINLOG_BITDEPTH;
static uint16_t sinLogTable[SINLOG_TABLESIZE];
static const uint16_t SINEXP_BITDEPTH = 10;
static const uint16_t SINEXP_TABLESIZE = 1<<SINEXP_BITDEPTH;
static uint16_t sinExpTable[SINEXP_TABLESIZE];
const uint16_t ENV_MAX = 1<<ENV_BITDEPTH;
static inline uint16_t sinLog(uint16_t phi) {
const uint16_t SINLOG_TABLEFILTER = SINLOG_TABLESIZE-1;
const uint16_t index = (phi & SINLOG_TABLEFILTER);
switch( ( phi & (SINLOG_TABLESIZE * 3) ) ) {
case 0:
return sinLogTable[index];
case SINLOG_TABLESIZE:
return sinLogTable[index ^ SINLOG_TABLEFILTER];
case SINLOG_TABLESIZE * 2 :
return sinLogTable[index] | NEGATIVE_BIT;
default:
return sinLogTable[index ^ SINLOG_TABLEFILTER] | NEGATIVE_BIT;
}
}
EngineMkI::EngineMkI() {
float bitReso = SINLOG_TABLESIZE;
for(int32_t i=0;i<SINLOG_TABLESIZE;i++) {
float x1 = sin(((0.5+i)/bitReso) * M_PI/2.0);
sinLogTable[i] = round(-1024 * log2(x1));
}
bitReso = SINEXP_TABLESIZE;
for(int32_t i=0;i<SINEXP_TABLESIZE;i++) {
float x1 = (pow(2, float(i)/bitReso)-1) * 4096;
sinExpTable[i] = round(x1);
}
#ifdef MKIDEBUG
uint8_t buffer[4096];
int32_t pos = 0;
TRACE("****************************************");
for(int32_t i=0;i<SINLOG_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinLogTable[i]);
if ( pos > 90 ) {
TRACE("SINLOGTABLE: %s" ,buffer);
buffer[0] = 0;
pos = 0;
}
}
TRACE("SINLOGTABLE: %s", buffer);
buffer[0] = 0;
pos = 0;
TRACE("----------------------------------------");
for(int32_t i=0;i<SINEXP_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinExpTable[i]);
if ( pos > 90 ) {
TRACE("SINEXTTABLE: %s" ,buffer);
buffer[0] = 0;
pos = 0;
}
}
TRACE("SINEXTTABLE: %s", buffer);
TRACE("****************************************");
#endif
}
inline int32_t mkiSin(int32_t phase, uint16_t env) {
uint16_t expVal = sinLog(phase >> (22 - SINLOG_BITDEPTH)) + (env);
//int16_t expValShow = expVal;
const bool isSigned = expVal & NEGATIVE_BIT;
expVal &= ~NEGATIVE_BIT;
const uint16_t SINEXP_FILTER = 0x3FF;
uint16_t result = 4096 + sinExpTable[( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER];
//uint16_t resultB4 = result;
result >>= ( expVal >> 10 ); // exp
#ifdef MKIDEBUG
if ( ( time(NULL) % 5 ) == 0 ) {
if ( expValShow < 0 ) {
expValShow = (expValShow + 0x7FFF) * -1;
}
//TRACE(",%d,%d,%d,%d,%d,%d", phase >> (22 - SINLOG_BITDEPTH), env, expValShow, ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER, resultB4, result);
}
#endif
if( isSigned )
return (-result - 1) << 13;
else
return result << 13;
}
void EngineMkI::compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = mkiSin((phase+input[i]), gain);
output[i] = y + adder[i];
phase += freq;
}
}
void EngineMkI::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = mkiSin(phase , gain);
output[i] = y + adder[i];
phase += freq;
}
}
void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int32_t fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = mkiSin((phase+scaled_fb), gain);
output[i] = y + adder[i];
phase += freq;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
// exclusively used for ALGO 6 with feedback
void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift) {
int32_t dgain[2];
int32_t gain[2];
int32_t phase[2];
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
phase[0] = parms[0].phase;
phase[1] = parms[1].phase;
parms[1].gain_out = (ENV_MAX-(parms[1].level_in >> (28-ENV_BITDEPTH)));
gain[0] = gain01;
gain[1] = parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out;
dgain[0] = (gain02 - gain01 + (_N_ >> 1)) >> LG_N;
dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out));
for (uint8_t i = 0; i < _N_; i++) {
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
// op 0
gain[0] += dgain[0];
y0 = y;
y = mkiSin(phase[0]+scaled_fb, gain[0]);
phase[0] += parms[0].freq;
// op 1
gain[1] += dgain[1];
y = mkiSin(phase[1]+y, gain[1]);
phase[1] += parms[1].freq;
output[i] = y;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
// exclusively used for ALGO 4 with feedback
void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift) {
int32_t dgain[3];
int32_t gain[3];
int32_t phase[3];
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
phase[0] = parms[0].phase;
phase[1] = parms[1].phase;
phase[2] = parms[2].phase;
parms[1].gain_out = (ENV_MAX-(parms[1].level_in >> (28-ENV_BITDEPTH)));
parms[2].gain_out = (ENV_MAX-(parms[2].level_in >> (28-ENV_BITDEPTH)));
gain[0] = gain01;
gain[1] = parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out;
gain[2] = parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out;
dgain[0] = (gain02 - gain01 + (_N_ >> 1)) >> LG_N;
dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out));
dgain[2] = (parms[2].gain_out - (parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out));
for (uint8_t i = 0; i < _N_; i++) {
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
// op 0
gain[0] += dgain[0];
y0 = y;
y = mkiSin(phase[0]+scaled_fb, gain[0]);
phase[0] += parms[0].freq;
// op 1
gain[1] += dgain[1];
y = mkiSin(phase[1]+y, gain[1]);
phase[1] += parms[1].freq;
// op 2
gain[2] += dgain[2];
y = mkiSin(phase[2]+y, gain[2]);
phase[2] += parms[2].freq;
output[i] = y;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
void EngineMkI::render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int32_t kLevelThresh = ENV_MAX-100;
FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
bool fb_on = feedback_shift < 16;
switch(algorithm) {
case 3 : case 5 :
if ( fb_on )
alg.ops[0] = 0xc4;
}
for (int32_t op = 0; op < 6; op++) {
int32_t flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int32_t inbus = (flags >> 4) & 3;
int32_t outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain_out == 0 ? (ENV_MAX-1) : param.gain_out;
int32_t gain2 = ENV_MAX-(param.level_in >> (28-ENV_BITDEPTH));
param.gain_out = gain2;
if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// PG: this is my 'dirty' implementation of FB for 2 and 3 operators...
if ((flags & 0xc0) == 0xc0 && fb_on) {
switch ( algorithm ) {
// three operator feedback, process exception for ALGO 4
case 3 :
compute_fb3(outptr, params, gain1, gain2, fb_buf, std::min((feedback_shift+2), (int32_t)16));
params[1].phase += params[1].freq << LG_N; // hack, we already processed op-5 - op-4
params[2].phase += params[2].freq << LG_N; // yuk yuk
op += 2; // ignore the 2 other operators
break;
// two operator feedback, process exception for ALGO 6
case 5 :
compute_fb2(outptr, params, gain1, gain2, fb_buf, std::min((feedback_shift+2), (int32_t)16));
params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5
op++; // ignore next operator;
break;
case 31 :
// one operator feedback, process exception for ALGO 32
compute_fb(outptr, param.phase, param.freq, gain1, gain2, fb_buf, std::min((feedback_shift+2), (int32_t)16), add);
break;
default:
// one operator feedback, normal process
compute_fb(outptr, param.phase, param.freq, gain1, gain2, fb_buf, feedback_shift, add);
break;
}
} else {
compute_pure(outptr, param.phase, param.freq, gain1, gain2, add);
}
} else {
compute(outptr, buf_[inbus - 1].get(), param.phase, param.freq, gain1, gain2, add);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}
param.phase += param.freq << LG_N;
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2014 Pascal Gauthier.
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ENGINEMKI_H_INCLUDED
#define ENGINEMKI_H_INCLUDED
#include "aligned_buf.h"
#include "fm_op_kernel.h"
#include "controllers.h"
#include "fm_core.h"
class EngineMkI : public FmCore {
public:
EngineMkI();
~EngineMkI() {};
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) override;
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int32_t fb_gain, bool add);
void compute_fb2(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift);
void compute_fb3(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift);
};
#endif // ENGINEMKI_H_INCLUDED

@ -0,0 +1,66 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//using namespace std;
#include "synth.h"
#include "exp2.h"
#include "fm_op_kernel.h"
#include "EngineMsfa.h"
void EngineMsfa::render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int32_t kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (uint8_t op = 0; op < 6; op++) {
int32_t flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int32_t inbus = (flags >> 4) & 3;
int32_t outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain_out;
int32_t gain2 = Exp2::lookup(param.level_in - (14 * (1 << 24)));
param.gain_out = gain2;
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// todo: more than one op in a feedback loop
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl;
FmOpKernel::compute_fb(outptr, param.phase, param.freq,
gain1, gain2,
fb_buf, feedback_shift, add);
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
FmOpKernel::compute_pure(outptr, param.phase, param.freq,
gain1, gain2, add);
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
FmOpKernel::compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}
param.phase += param.freq << LG_N;
}
}

@ -0,0 +1,29 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "fm_op_kernel.h"
#include "aligned_buf.h"
#include "controllers.h"
#include "fm_core.h"
class EngineMsfa : public FmCore {
public:
EngineMsfa() {};
~EngineMsfa() {};
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_gain) override;
};

@ -0,0 +1,209 @@
/*
* Copyright (C) 2014 Pascal Gauthier.
* Copyright (C) 2012 Steffen Ohrendorf <steffen.ohrendorf@gmx.de>
*
* 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Original Java Code: Copyright (C) 2008 Robson Cozendey <robson@cozendey.com>
*
* Some code based on forum posts in: http://forums.submarine.org.uk/phpBB/viewforum.php?f=9,
* Copyright (C) 2010-2013 by carbon14 and opl3
*
*/
#include "EngineOpl.h"
const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0};
uint16_t SignBit = 0x8000;
uint16_t sinLogTable[256] = {
2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869,
846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609,
598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461,
453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358,
352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280,
276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219,
215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169,
167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129,
127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96,
94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69,
67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47,
46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30,
29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17,
16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7,
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
};
uint16_t sinExpTable[256] = {
0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42,
45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90,
93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139,
142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190,
194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244,
248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300,
304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359,
363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420,
424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484,
488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551,
555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621,
625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693,
698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770,
774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849,
854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932,
937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018
};
inline uint16_t sinLog(uint16_t phi) {
const uint8_t index = (phi & 0xff);
switch( ( phi & 0x0300 ) ) {
case 0x0000:
// rising quarter wave Shape A
return sinLogTable[index];
case 0x0100:
// falling quarter wave Shape B
return sinLogTable[index ^ 0xFF];
case 0x0200:
// rising quarter wave -ve Shape C
return sinLogTable[index] | SignBit;
default:
// falling quarter wave -ve Shape D
return sinLogTable[index ^ 0xFF] | SignBit;
}
}
// 16 env units are ~3dB and halve the output
/**
* @brief OPL Sine Wave calculation
* @param[in] phase Wave phase (0..1023)
* @param[in] env Envelope value (0..511)
* @warning @a env will not be checked for correct values.
*/
inline int16_t oplSin( uint16_t phase, uint16_t env ) {
uint16_t expVal = sinLog(phase) + (env << 3);
const bool isSigned = expVal & SignBit;
expVal &= ~SignBit;
// expVal: 0..2137+511*8 = 0..6225
// result: 0..1018+1024
uint32_t result = 0x0400 + sinExpTable[( expVal & 0xff ) ^ 0xFF];
result <<= 1;
result >>= ( expVal >> 8 ); // exp
if( isSigned ) {
// -1 for one's complement
return -result - 1;
} else {
return result;
}
}
void EngineOpl::compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = oplSin((phase+input[i]) >> 14, gain);
output[i] = (y << 14) + adder[i];
phase += freq;
}
}
void EngineOpl::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = oplSin(phase >> 14, gain);
output[i] = (y << 14) + adder[i];
phase += freq;
}
}
void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int32_t fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = oplSin((phase+scaled_fb) >> 14, gain) << 14;
output[i] = y + adder[i];
phase += freq;
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
void EngineOpl::render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int32_t kLevelThresh = 507; // really ????
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (uint8_t op = 0; op < 6; op++) {
int32_t flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int32_t inbus = (flags >> 4) & 3;
int32_t outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain_out == 0 ? 511 : param.gain_out;
int32_t gain2 = 512-(param.level_in >> 19);
param.gain_out = gain2;
if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// todo: more than one op in a feedback loop
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl;
compute_fb(outptr, param.phase, param.freq,
gain1, gain2,
fb_buf, feedback_shift, add);
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
compute_pure(outptr, param.phase, param.freq,
gain1, gain2, add);
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}
param.phase += param.freq << LG_N;
}
}

@ -0,0 +1,39 @@
/**
*
* Copyright (c) 2014 Pascal Gauthier.
*
* 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, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef ENGINEOPL_H_INCLUDED
#define ENGINEOPL_H_INCLUDED
#include "aligned_buf.h"
#include "fm_op_kernel.h"
#include "controllers.h"
#include "fm_core.h"
class EngineOpl : public FmCore {
public:
EngineOpl() {};
~EngineOpl() {};
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) override;
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int32_t fb_gain, bool add);
};
#endif // ENGINEOPL_H_INCLUDED

@ -0,0 +1,233 @@
/**
Copyright (c) 2013-2014 Pascal Gauthier.
Copyright (c) 2013-2014 Filatov Vadim.
Filter taken from the Obxd project :
https://github.com/2DaT/Obxd
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, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _USE_MATH_DEFINES
#include <math.h>
#include "PluginFx.h"
#include "synth.h"
const float dc = 1e-18;
inline static float tptpc(float& state, float inp, float cutoff) {
float v = (inp - state) * cutoff / (1 + cutoff);
float res = v + state;
state = res + v;
return res;
}
inline static float tptlpupw(float & state , float inp , float cutoff , float srInv) {
cutoff = (cutoff * srInv) * M_PI;
float v = (inp - state) * cutoff / (1 + cutoff);
float res = v + state;
state = res + v;
return res;
}
//static float linsc(float param,const float min,const float max) {
// return (param) * (max - min) + min;
//}
static float logsc(float param, const float min, const float max, const float rolloff = 19.0f) {
return ((EXP_FUNC(param * LOG_FUNC(rolloff + 1)) - 1.0f) / (rolloff)) * (max - min) + min;
}
PluginFx::PluginFx() {
Cutoff = 1.0;
Reso = 0.0;
Gain = 1.0;
}
void PluginFx::init(uint16_t sr) {
mm = 0;
s1 = s2 = s3 = s4 = c = d = 0;
R24 = 0;
mmch = (int)(mm * 3);
mmt = mm * 3 - mmch;
sampleRate = sr;
sampleRateInv = 1 / sampleRate;
#if defined(ARM_SQRT_FUNC)
float rcrate; ARM_SQRT_FUNC(44000 / sampleRate, &rcrate);
#else
float rcrate = SQRT_FUNC((44000 / sampleRate));
#endif
rcor24 = (970.0 / 44000) * rcrate;
rcor24Inv = 1 / rcor24;
bright = tanf((sampleRate * 0.5f - 10) * M_PI * sampleRateInv);
R = 1;
rcor = (480.0 / 44000) * rcrate;
rcorInv = 1 / rcor;
bandPassSw = false;
pCutoff = -1;
pReso = -1;
dc_r = 1.0 - (126.0 / sr);
dc_id = 0;
dc_od = 0;
}
inline float PluginFx::NR24(float sample, float g, float lpc) {
float ml = 1 / (1 + g);
float S = (lpc * (lpc * (lpc * s1 + s2) + s3) + s4) * ml;
float G = lpc * lpc * lpc * lpc;
float y = (sample - R24 * S) / (1 + R24 * G);
return y + 1e-8;
};
inline float PluginFx::NR(float sample, float g) {
float y = ((sample - R * s1 * 2 - g * s1 - s2) / (1 + g * (2 * R + g))) + dc;
return y;
}
void PluginFx::process(float *work, uint16_t sampleSize) {
// very basic DC filter
float t_fd = work[0];
work[0] = work[0] - dc_id + dc_r * dc_od;
dc_id = t_fd;
for (int i = 1; i < sampleSize; i++) {
t_fd = work[i];
work[i] = work[i] - dc_id + dc_r * work[i - 1];
dc_id = t_fd;
}
dc_od = work[sampleSize - 1];
// Gain
if (Gain == 0.0)
{
for (uint16_t i = 0; i < sampleSize; i++ )
work[i] = 0.0;
}
else if ( Gain != 1.0)
{
for (uint16_t i = 0; i < sampleSize; i++ )
work[i] *= Gain;
}
// don't apply the LPF if the cutoff is to maximum
if ( Cutoff == 1.0 )
return;
if ( Cutoff != pCutoff || Reso != pReso ) {
rReso = (0.991 - logsc(1 - Reso, 0, 0.991));
R24 = 3.5 * rReso;
float cutoffNorm = logsc(Cutoff, 60, 19000);
rCutoff = (float)tanf(cutoffNorm * sampleRateInv * M_PI);
pCutoff = Cutoff;
pReso = Reso;
R = 1 - rReso;
}
// THIS IS MY FAVORITE 4POLE OBXd filter
// maybe smooth this value
float g = rCutoff;
float lpc = g / (1 + g);
for (uint16_t i = 0; i < sampleSize; i++ ) {
float s = work[i];
s = s - 0.45 * tptlpupw(c, s, 15, sampleRateInv);
s = tptpc(d, s, bright);
float y0 = NR24(s, g, lpc);
//first low pass in cascade
float v = (y0 - s1) * lpc;
float res = v + s1;
s1 = res + v;
//damping
s1 = atanf(s1 * rcor24) * rcor24Inv;
float y1 = res;
float y2 = tptpc(s2, y1, g);
float y3 = tptpc(s3, y2, g);
float y4 = tptpc(s4, y3, g);
float mc = 0.0;
switch (mmch) {
case 0:
mc = ((1 - mmt) * y4 + (mmt) * y3);
break;
case 1:
mc = ((1 - mmt) * y3 + (mmt) * y2);
break;
case 2:
mc = ((1 - mmt) * y2 + (mmt) * y1);
break;
case 3:
mc = y1;
break;
}
//half volume comp
work[i] = mc * (1 + R24 * 0.45);
}
}
/*
// THIS IS THE 2POLE FILTER
for(int i=0; i < sampleSize; i++ ) {
float s = work[i];
s = s - 0.45*tptlpupw(c,s,15,sampleRateInv);
s = tptpc(d,s,bright);
//float v = ((sample- R * s1*2 - g2*s1 - s2)/(1+ R*g1*2 + g1*g2));
float v = NR(s,g);
float y1 = v*g + s1;
//damping
s1 = atanf(s1 * rcor) * rcorInv;
float y2 = y1*g + s2;
s2 = y2 + y1*g;
float mc;
if(!bandPassSw)
mc = (1-mm)*y2 + (mm)*v;
else
{
mc =2 * ( mm < 0.5 ?
((0.5 - mm) * y2 + (mm) * y1):
((1-mm) * y1 + (mm-0.5) * v)
);
}
work[i] = mc;
}
*/
float PluginFx::getGain(void)
{
return (Gain);
}

@ -0,0 +1,72 @@
/**
Copyright (c) 2013 Pascal Gauthier.
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, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "stdint.h"
class PluginFx {
float s1, s2, s3, s4;
float sampleRate;
float sampleRateInv;
float d, c;
float R24;
float rcor24, rcor24Inv;
float bright;
// 24 db multimode
float mm;
float mmt;
int32_t mmch;
inline float NR24(float sample, float g, float lpc);
// preprocess values taken the UI
float rCutoff;
float rReso;
float rGain;
// thread values; if these are different from the UI,
// it needs to be recalculated.
float pReso;
float pCutoff;
float pGain;
// I am still keeping the 2pole w/multimode filter
inline float NR(float sample, float g);
bool bandPassSw;
float rcor, rcorInv;
int32_t R;
float dc_id;
float dc_od;
float dc_r;
public:
PluginFx();
// this is set directly by the ui / parameter
float Cutoff;
float Reso;
float Gain;
void init(uint16_t sampleRate);
void process(float *work, uint16_t sampleSize);
float getGain(void);
};

@ -0,0 +1,37 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// A convenient wrapper for buffers with alignment constraints
// Note that if we were on C++11, we'd use aligned_storage or somesuch.
#ifndef __ALIGNED_BUF_H
#define __ALIGNED_BUF_H
#include <stdlib.h>
#include <stdint.h>
template<typename T, size_t size, size_t alignment = 16>
class AlignedBuf {
public:
T *get() {
return (T *)((((intptr_t)storage_) + alignment - 1) & -alignment);
}
private:
unsigned char storage_[size * sizeof(T) + alignment];
};
#endif

@ -0,0 +1,429 @@
/* 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
#ifndef TEENSYDUINO
#include <arm_math.h> //ARM DSP extensions. https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html
#include "synth.h"
/*
static const float zeroblock_f32[] = {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#if AUDIO_BLOCK_SAMPLES > 16
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 32
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 48
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 64
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 80
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 96
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 112
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#endif
#if AUDIO_BLOCK_SAMPLES > 128
#error AUDIO_BLOCK_SAMPLES > 128 is a problem for this class
#endif
};
*/
class Compressor
{
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
public:
//constructor
Compressor(const float sample_rate_Hz) {
//setDefaultValues(AUDIO_SAMPLE_RATE); resetStates();
setDefaultValues(sample_rate_Hz);
resetStates();
};
void setDefaultValues(const float 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
}
//here's the method that does all the work
void doCompression(float *audio_block, uint16_t len) {
//Serial.println("AudioEffectGain_F32: updating."); //for debugging.
if (!audio_block) {
printf("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)
float* audio_level_dB_block = float[len];
if(!audio_level_dB_block)
{
printf("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
float* gain_block=new float[len];
if(!gain_block)
{
printf("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;
}
// 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 calcAudioLevel_dB(float *wav_block, float *level_dB_block, uint16_t len) {
// calculate the instantaneous signal power (square the signal)
float* wav_pow_block=new float[len];
if(!wav_pow_block)
{
printf("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
float 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 calcGain(float *audio_level_dB_block, float *gain_block,uint16_t len) {
//first, calculate the instantaneous target gain based on the compression ratio
float* inst_targ_gain_dB_block=new float[len];
if(!inst_targ_gain_dB_block)
{
printf("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
float *gain_dB_block = new float[len];
if(!gain_dB_block)
{
printf("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
}
//Compute the instantaneous desired gain, including the compression ratio and
//threshold for where the comrpession kicks in
void calcInstantaneousTargetGain(float *audio_level_dB_block, float *inst_targ_gain_dB_block, uint16_t len) {
// how much are we above the compression threshold?
float* above_thresh_dB_block=new float[len];
if(!above_thresh_dB_block)
{
printf("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 calcSmoothedGain_dB(float *inst_targ_gain_dB_block, float *gain_dB_block, uint16_t len) {
float gain_dB;
float one_minus_attack_const = 1.0f - attack_const;
float 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
}
//methods to set parameters of this module
void 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 setPreGain(float g) { pre_gain = g; }
void setPreGain_dB(float gain_dB) { setPreGain(pow(10.0, gain_dB / 20.0)); }
void setCompressionRatio(float cr) {
comp_ratio = std::max(0.001f, cr); //limit to positive values
updateThresholdAndCompRatioConstants();
}
void setAttack_sec(float a, float 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(std::min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants
}
void setRelease_sec(float r, float 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(std::min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants
}
void setLevelTimeConst_sec(float t_sec, float fs_Hz) {
const float min_t_sec = 0.002f; //this is the minimum allowed value
level_lp_sec = std::max(min_t_sec,t_sec);
level_lp_const = expf(-1.0f / (level_lp_sec * fs_Hz)); //expf() is much faster than exp()
}
void setThresh_dBFS(float val) {
thresh_dBFS = val;
setThreshPow(pow(10.0, thresh_dBFS / 10.0));
}
void enableHPFilter(boolean flag) { use_HP_prefilter = flag; };
//methods to return information about this module
float getPreGain_dB(void) { return 20.0 * log10f_approx(pre_gain); }
float getAttack_sec(void) { return attack_sec; }
float getRelease_sec(void) { return release_sec; }
float getLevelTimeConst_sec(void) { return level_lp_sec; }
float getThresh_dBFS(void) { return thresh_dBFS; }
float getCompressionRatio(void) { return comp_ratio; }
float getCurrentLevel_dBFS(void) { return 10.0* log10f_approx(prev_level_lp_pow); }
float getCurrentGain_dB(void) { return prev_gain_dB; }
void setHPFilterCoeff_N2IIR_Matlab(float b[], float 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
}
private:
//state-related variables
float *inputQueueArray_f32[1]; //memory pointer for the input to this module
float prev_level_lp_pow = 1.0;
float 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;
float hp_coeff[5 * hp_nstages] = {1.0, 0.0, 0.0, 0.0, 0.0}; //no filtering. actual filter coeff set later
float hp_state[4 * hp_nstages];
void 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
float b[] = {9.979871156751189e-01, -1.995974231350238e+00, 9.979871156751189e-01}; //from Matlab
float 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
}
//private parameters related to gain calculation
float attack_const, release_const, level_lp_const; //used in calcGain(). set by setAttack_sec() and setRelease_sec();
float comp_ratio_const, thresh_pow_FS_wCR; //used in calcGain(); set in updateThresholdAndCompRatioConstants()
void updateThresholdAndCompRatioConstants(void) {
comp_ratio_const = 1.0f-(1.0f / comp_ratio);
thresh_pow_FS_wCR = powf(thresh_pow_FS, comp_ratio_const);
}
//settings
float attack_sec, release_sec, level_lp_sec;
float thresh_dBFS = 0.0; //threshold for compression, relative to digital full scale
float thresh_pow_FS = 1.0f; //same as above, but not in dB
void setThreshPow(float t_pow) {
thresh_pow_FS = t_pow;
updateThresholdAndCompRatioConstants();
}
float comp_ratio = 1.0; //compression ratio
float 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 float pow10f(float 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 float log10f_approx(float 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
//float log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f};
static float log2f_approx(float X) {
//float *C = &log2f_approx_coeff[0];
float Y;
float F;
int32_t 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);
}
};
#else
#warning USING TEENSYDUINO SO INTERNAL COMPRESSOR IS DISABLED!
#endif // TEENSYDUINO
#endif

@ -0,0 +1,152 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __CONTROLLERS_H
#define __CONTROLLERS_H
#include <stdint.h>
#include "synth.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "fm_core.h"
// State of MIDI controllers
const uint8_t kControllerPitch = 0;
const uint8_t kControllerPitchRange = 1;
const uint8_t kControllerPitchStep = 2;
const uint8_t kControllerPortamentoGlissando = 3;
class FmMod {
public:
uint8_t range;
bool pitch;
bool amp;
bool eg;
uint8_t ctrl_mode;
FmMod()
{
range = 0;
ctrl_mode = 0;
pitch = false;
amp = false;
eg = false;
}
void setRange(uint8_t r)
{
range = r < 0 || r > 99 ? 0 : r;
}
uint8_t getRange(void)
{
return (range);
}
void setTarget(uint8_t assign)
{
assign = assign < 0 && assign > 7 ? 0 : assign;
pitch = assign & 1; // PITCH
amp = assign & 2; // AMP
eg = assign & 4; // EG
}
uint8_t getTarget(void)
{
return (pitch & amp & eg);
}
void setMode(uint8_t m)
{
ctrl_mode = m > MIDI_CONTROLLER_MODE_MAX ? 0 : m;
}
};
class Controllers {
void applyMod(int cc, FmMod &mod)
{
uint8_t total = 0;
float range = mod.range / 100.0;
switch (mod.ctrl_mode)
{
case 0:
total = uint8_t(float(cc) * range); // LINEAR mode
break;
case 1:
total = uint8_t(127.0 * range - (float(cc) * range)); // REVERSE mode
break;
case 2:
total = uint8_t(range * float(cc) + (1.0 - range) * 127.0); // DIRECT BC mode by Thierry (opus.quatre)
break;
}
if (mod.amp)
amp_mod = std::max(amp_mod, total);
if (mod.pitch)
pitch_mod = std::max(pitch_mod, total);
if (mod.eg)
eg_mod = std::max(eg_mod, total);
}
public:
int32_t values_[4];
uint8_t amp_mod;
uint8_t pitch_mod;
uint8_t eg_mod;
uint8_t aftertouch_cc;
uint8_t breath_cc;
uint8_t foot_cc;
uint8_t modwheel_cc;
bool portamento_enable_cc;
int32_t portamento_cc;
bool portamento_gliss_cc;
int32_t masterTune;
uint8_t opSwitch;
FmMod wheel;
FmMod foot;
FmMod breath;
FmMod at;
Controllers() {
amp_mod = 0;
pitch_mod = 0;
eg_mod = 0;
}
void refresh() {
amp_mod = pitch_mod = eg_mod = 0;
applyMod(modwheel_cc, wheel);
applyMod(breath_cc, breath);
applyMod(foot_cc, foot);
applyMod(aftertouch_cc, at);
if ( ! ((wheel.eg || foot.eg) || (breath.eg || at.eg)) )
eg_mod = 127;
}
class FmCore* core;
};
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,391 @@
/*
MicroDexed
MicroDexed is a port of the Dexed sound engine
(https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield.
Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android
(c)2018-2021 H. Wirtz <wirtz@parasitstudio.de>
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, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//#define DISABLE_DEXED_COMPRESSOR 1
#ifndef DEXED_H_INCLUDED
#define DEXED_H_INCLUDED
#include <stdint.h>
#include <cstdlib>
#include <arm_math.h>
#if defined(TEENSYDUINO)
#include <Audio.h>
#endif
#include "fm_op_kernel.h"
#include "synth.h"
#include "env.h"
#include "aligned_buf.h"
#include "pitchenv.h"
#include "controllers.h"
#include "dx7note.h"
#include "lfo.h"
#include "PluginFx.h"
#include "compressor.h"
#include "EngineMsfa.h"
#include "EngineMkI.h"
#include "EngineOpl.h"
#define NUM_VOICE_PARAMETERS 156
struct ProcessorVoice {
uint8_t midi_note;
uint8_t velocity;
int16_t porta;
bool keydown;
bool sustained;
bool live;
uint32_t key_pressed_timer;
Dx7Note *dx7_note;
};
enum DexedVoiceOPParameters {
DEXED_OP_EG_R1, // 0
DEXED_OP_EG_R2, // 1
DEXED_OP_EG_R3, // 2
DEXED_OP_EG_R4, // 3
DEXED_OP_EG_L1, // 4
DEXED_OP_EG_L2, // 5
DEXED_OP_EG_L3, // 6
DEXED_OP_EG_L4, // 7
DEXED_OP_LEV_SCL_BRK_PT, // 8
DEXED_OP_SCL_LEFT_DEPTH, // 9
DEXED_OP_SCL_RGHT_DEPTH, // 10
DEXED_OP_SCL_LEFT_CURVE, // 11
DEXED_OP_SCL_RGHT_CURVE, // 12
DEXED_OP_OSC_RATE_SCALE, // 13
DEXED_OP_AMP_MOD_SENS, // 14
DEXED_OP_KEY_VEL_SENS, // 15
DEXED_OP_OUTPUT_LEV, // 16
DEXED_OP_OSC_MODE, // 17
DEXED_OP_FREQ_COARSE, // 18
DEXED_OP_FREQ_FINE, // 19
DEXED_OP_OSC_DETUNE // 20
};
#define DEXED_VOICE_OFFSET 126
enum DexedVoiceParameters {
DEXED_PITCH_EG_R1, // 0
DEXED_PITCH_EG_R2, // 1
DEXED_PITCH_EG_R3, // 2
DEXED_PITCH_EG_R4, // 3
DEXED_PITCH_EG_L1, // 4
DEXED_PITCH_EG_L2, // 5
DEXED_PITCH_EG_L3, // 6
DEXED_PITCH_EG_L4, // 7
DEXED_ALGORITHM, // 8
DEXED_FEEDBACK, // 9
DEXED_OSC_KEY_SYNC, // 10
DEXED_LFO_SPEED, // 11
DEXED_LFO_DELAY, // 12
DEXED_LFO_PITCH_MOD_DEP, // 13
DEXED_LFO_AMP_MOD_DEP, // 14
DEXED_LFO_SYNC, // 15
DEXED_LFO_WAVE, // 16
DEXED_LFO_PITCH_MOD_SENS, // 17
DEXED_TRANSPOSE, // 18
DEXED_NAME // 19
};
enum ADSR {
ATTACK,
DECAY,
SUSTAIN,
RELEASE
};
enum OPERATORS {
OP1,
OP2,
OP3,
OP4,
OP5,
OP6
};
enum CONTROLLER_ASSIGN {
NONE,
PITCH,
AMP,
PITCH_AMP,
EG,
PITCH_EG,
AMP_EG,
PITCH_AMP_EG
};
enum PORTAMENTO_MODE {
RETAIN,
FOLLOW
};
enum ON_OFF {
OFF,
ON
};
enum VELOCITY_SCALES {
MIDI_VELOCITY_SCALING_OFF,
MIDI_VELOCITY_SCALING_DX7,
MIDI_VELOCITY_SCALING_DX7II
};
enum ENGINES {
MSFA,
MKI,
OPL
};
// GLOBALS
//==============================================================================
class Dexed
{
public:
Dexed(uint8_t maxnotes, uint16_t rate);
~Dexed();
// Global methods
void activate(void);
void deactivate(void);
bool getMonoMode(void);
void setMonoMode(bool mode);
void setNoteRefreshMode(bool mode);
uint8_t getMaxNotes(void);
void doRefreshVoice(void);
void setOPAll(uint8_t ops);
bool decodeVoice(uint8_t* data, uint8_t* encoded_data);
bool encodeVoice(uint8_t* encoded_data);
bool getVoiceData(uint8_t* data_copy);
void setVoiceDataElement(uint8_t address, uint8_t value);
uint8_t getVoiceDataElement(uint8_t address);
void loadInitVoice(void);
void loadVoiceParameters(uint8_t* data);
uint8_t getNumNotesPlaying(void);
uint32_t getXRun(void);
uint16_t getRenderTimeMax(void);
void resetRenderTimeMax(void);
void ControllersRefresh(void);
void setVelocityScale(uint8_t offset, uint8_t max);
void getVelocityScale(uint8_t* offset, uint8_t* max);
void setVelocityScale(uint8_t setup);
void setMaxNotes(uint8_t n);
void setEngineType(uint8_t engine);
uint8_t getEngineType(void);
FmCore* getEngineAddress(void);
#ifndef TEENSYDUINO
void setCompressor(bool comp);
bool getCompressor(void);
void setCompressorPreGain_dB(float pre_gain);
void setCompressorAttack_sec(float attack_sec);
void setCompressorRelease_sec(float release_sec);
void setCompressorThresh_dBFS(float thresh_dBFS);
void setCompressionRatio(float comp_ratio);
float getCompressorPreGain_dB(void);
float getCompressorAttack_sec(void);
float getCompressorRelease_sec(void);
float getCompressorThresh_dBFS(void);
float getCompressionRatio(void);
#endif
int16_t checkSystemExclusive(const uint8_t* sysex, const uint16_t len);
// Sound methods
void keyup(uint8_t pitch);
void keydown(uint8_t pitch, uint8_t velo);
void setSustain(bool sustain);
bool getSustain(void);
void panic(void);
void notesOff(void);
void resetControllers(void);
void setMasterTune(int8_t mastertune);
int8_t getMasterTune(void);
void setPortamento(uint8_t portamento_mode, uint8_t portamento_glissando, uint8_t portamento_time);
void setPortamentoMode(uint8_t portamento_mode);
uint8_t getPortamentoMode(void);
void setPortamentoGlissando(uint8_t portamento_glissando);
uint8_t getPortamentoGlissando(void);
void setPortamentoTime(uint8_t portamento_time);
uint8_t getPortamentoTime(void);
void setPBController(uint8_t pb_range, uint8_t pb_step);
void setMWController(uint8_t mw_range, uint8_t mw_assign, uint8_t mw_mode);
void setFCController(uint8_t fc_range, uint8_t fc_assign, uint8_t fc_mode);
void setBCController(uint8_t bc_range, uint8_t bc_assign, uint8_t bc_mode);
void setATController(uint8_t at_range, uint8_t at_assign, uint8_t at_mode);
void setModWheel(uint8_t value);
uint8_t getModWheel(void);
void setBreathController(uint8_t value);
uint8_t getBreathController(void);
void setFootController(uint8_t value);
uint8_t getFootController(void);
void setAftertouch(uint8_t value);
uint8_t getAftertouch(void);
void setPitchbend(uint8_t value1, uint8_t value2);
void setPitchbend(int16_t value);
void setPitchbend(uint16_t value);
int16_t getPitchbend(void);
void setPitchbendRange(uint8_t range);
uint8_t getPitchbendRange(void);
void setPitchbendStep(uint8_t step);
uint8_t getPitchbendStep(void);
void setModWheelRange(uint8_t range);
uint8_t getModWheelRange(void);
void setModWheelTarget(uint8_t target);
uint8_t getModWheelTarget(void);
void setFootControllerRange(uint8_t range);
uint8_t getFootControllerRange(void);
void setFootControllerTarget(uint8_t target);
uint8_t getFootControllerTarget(void);
void setBreathControllerRange(uint8_t range);
uint8_t getBreathControllerRange(void);
void setBreathControllerTarget(uint8_t target);
uint8_t getBreathControllerTarget(void);
void setAftertouchRange(uint8_t range);
uint8_t getAftertouchRange(void);
void setAftertouchTarget(uint8_t target);
uint8_t getAftertouchTarget(void);
void setFilterCutoff(float cutoff);
float getFilterCutoff(void);
void setFilterResonance(float resonance);
float getFilterResonance(void);
void setGain(float gain);
float getGain(void);
// Voice configuration methods
void setOPRateAll(uint8_t rate);
void setOPLevelAll(uint8_t level);
void setOPRateAllCarrier(uint8_t step, uint8_t rate);
void setOPLevelAllCarrier(uint8_t step, uint8_t level);
void setOPRateAllModulator(uint8_t step, uint8_t rate);
void setOPLevelAllModulator(uint8_t step, uint8_t level);
void setOPRate(uint8_t op, uint8_t step, uint8_t rate);
uint8_t getOPRate(uint8_t op, uint8_t step);
void setOPLevel(uint8_t op, uint8_t step, uint8_t level);
uint8_t getOPLevel(uint8_t op, uint8_t step);
void setOPKeyboardLevelScalingBreakPoint(uint8_t op, uint8_t level);
uint8_t getOPKeyboardLevelScalingBreakPoint(uint8_t op);
void setOPKeyboardLevelScalingDepthLeft(uint8_t op, uint8_t depth);
uint8_t getOPKeyboardLevelScalingDepthLeft(uint8_t op);
void setOPKeyboardLevelScalingDepthRight(uint8_t op, uint8_t depth);
uint8_t getOPKeyboardLevelScalingDepthRight(uint8_t op);
void setOPKeyboardLevelScalingCurveLeft(uint8_t op, uint8_t curve);
uint8_t getOPKeyboardLevelScalingCurveLeft(uint8_t op);
void setOPKeyboardLevelScalingCurveRight(uint8_t op, uint8_t curve);
uint8_t getOPKeyboardLevelScalingCurveRight(uint8_t op);
void setOPKeyboardRateScale(uint8_t op, uint8_t scale);
uint8_t getOPKeyboardRateScale(uint8_t op);
void setOPAmpModulationSensity(uint8_t op, uint8_t sensitivity);
uint8_t getOPAmpModulationSensity(uint8_t op);
void setOPKeyboardVelocitySensity(uint8_t op, uint8_t sensitivity);
uint8_t getOPKeyboardVelocitySensity(uint8_t op);
void setOPOutputLevel(uint8_t op, uint8_t level);
uint8_t getOPOutputLevel(uint8_t op);
void setOPMode(uint8_t op, uint8_t mode);
uint8_t getOPMode(uint8_t op);
void setOPFrequencyCoarse(uint8_t op, uint8_t frq_coarse);
uint8_t getOPFrequencyCoarse(uint8_t op);
void setOPFrequencyFine(uint8_t op, uint8_t frq_fine);
uint8_t getOPFrequencyFine(uint8_t op);
void setOPDetune(uint8_t op, uint8_t detune);
uint8_t getOPDetune(uint8_t op);
void setPitchRate(uint8_t step, uint8_t rate);
uint8_t getPitchRate(uint8_t step);
void setPitchLevel(uint8_t step, uint8_t level);
uint8_t getPitchLevel(uint8_t step);
void setAlgorithm(uint8_t algorithm);
uint8_t getAlgorithm(void);
void setFeedback(uint8_t feedback);
uint8_t getFeedback(void);
void setOscillatorSync(bool sync);
bool getOscillatorSync(void);
void setLFOSpeed(uint8_t speed);
uint8_t getLFOSpeed(void);
void setLFODelay(uint8_t delay);
uint8_t getLFODelay(void);
void setLFOPitchModulationDepth(uint8_t depth);
uint8_t getLFOPitchModulationDepth(void);
void setLFOAmpModulationDepth(uint8_t delay);
uint8_t getLFOAmpModulationDepth(void);
void setLFOSync(bool sync);
bool getLFOSync(void);
void setLFOWaveform(uint8_t waveform);
uint8_t getLFOWaveform(void);
void setLFOPitchModulationSensitivity(uint8_t sensitivity);
uint8_t getLFOPitchModulationSensitivity(void);
void setTranspose(uint8_t transpose);
uint8_t getTranspose(void);
void setName(char* name);
void getName(char* buffer);
ProcessorVoice* voices;
protected:
uint8_t init_voice[NUM_VOICE_PARAMETERS] = {
99, 99, 99, 99, 99, 99, 99, 00, 33, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, // OP6 eg_rate_1-4, level_1-4, kbd_lev_scl_brk_pt, kbd_lev_scl_lft_depth, kbd_lev_scl_rht_depth, kbd_lev_scl_lft_curve, kbd_lev_scl_rht_curve, kbd_rate_scaling, amp_mod_sensitivity, key_vel_sensitivity, operator_output_level, osc_mode, osc_freq_coarse, osc_freq_fine, osc_detune
99, 99, 99, 99, 99, 99, 99, 00, 33, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, // OP5
99, 99, 99, 99, 99, 99, 99, 00, 33, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, // OP4
99, 99, 99, 99, 99, 99, 99, 00, 33, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, // OP4
99, 99, 99, 99, 99, 99, 99, 00, 33, 00, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, // OP4
99, 99, 99, 99, 99, 99, 99, 00, 33, 00, 00, 00, 00, 00, 00, 00, 99, 00, 01, 00, 00, // OP4
99, 99, 99, 99, 50, 50, 50, 50, // 4 * pitch EG rates, 4 * pitch EG level
01, 00, 01, // algorithm, feedback, osc sync
35, 00, 00, 00, 01, 00, // lfo speed, lfo delay, lfo pitch_mod_depth, lfo_amp_mod_depth, lfo_sync, lfo_waveform
03, 48, // pitch_mod_sensitivity, transpose
73, 78, 73, 84, 32, 86, 79, 73, 67, 69 // 10 * char for name ("INIT VOICE")
};
float samplerate;
uint8_t data[NUM_VOICE_PARAMETERS];
uint8_t max_notes;
uint8_t used_notes;
PluginFx fx;
Controllers controllers;
int32_t lastKeyDown;
uint32_t xrun;
uint16_t render_time_max;
int16_t currentNote;
bool sustain;
float vuSignal;
bool monoMode;
bool noteRefreshMode;
bool refreshVoice;
uint8_t engineType;
VoiceStatus voiceStatus;
Lfo lfo;
EngineMsfa* engineMsfa;
EngineMkI* engineMkI;
EngineOpl* engineOpl;
void getSamples(float* buffer, uint16_t n_samples);
void getSamples(int16_t* buffer, uint16_t n_samples);
void compress(float* wav_in, float* wav_out, uint16_t n, float threshold, float slope, uint16_t sr, float tla, float twnd, float tatt, float trel);
bool use_compressor;
uint8_t velocity_offset;
uint8_t velocity_max;
float velocity_diff;
#ifndef TEENSYDUINO
Compressor* compressor;
#endif
};
#endif

@ -0,0 +1,388 @@
/*
Copyright 2016-2017 Pascal Gauthier.
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <math.h>
#include <stdlib.h>
#include "synth.h"
#include "freqlut.h"
#include "exp2.h"
#include "controllers.h"
#include "dx7note.h"
#include "porta.h"
//#include <cmath>
const int FEEDBACK_BITDEPTH = 8;
int32_t midinote_to_logfreq(int midinote) {
//const int32_t base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int32_t base = 50857777; // (1 << 24) * (LOG_FUNC(440) / LOG_FUNC(2) - 69/12)
const int32_t step = (1 << 24) / 12;
return base + step * midinote;
}
int32_t logfreq_round2semi(int freq) {
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int step = (1 << 24) / 12;
const int rem = (freq - base) % step;
return freq - rem;
}
const int32_t coarsemul[] = {
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600,
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816,
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858,
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032,
81503396, 82323963, 83117622
};
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
// TODO: pitch randomization
int32_t logfreq;
if (mode == 0) {
logfreq = midinote_to_logfreq(midinote);
// could use more precision, closer enough for now. those numbers comes from my DX7
//FRAC_NUM detuneRatio = 0.0209 * exp(-0.396 * (((float)logfreq) / (1 << 24))) / 7;
FRAC_NUM detuneRatio = 0.0209 * EXP_FUNC(-0.396 * (((float)logfreq) / (1 << 24))) / 7;
logfreq += detuneRatio * logfreq * (detune - 7);
logfreq += coarsemul[coarse & 31];
if (fine) {
// (1 << 24) / log(2)
//logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5);
logfreq += (int32_t)floor(24204406.323123 * LOG_FUNC(1 + 0.01 * fine) + 0.5);
}
// // This was measured at 7.213Hz per count at 9600Hz, but the exact
// // value is somewhat dependent on midinote. Close enough for now.
// //logfreq += 12606 * (detune -7);
} else {
// ((1 << 24) * log(10) / log(2) * .01) << 3
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3;
logfreq += detune > 7 ? 13457 * (detune - 7) : 0;
}
return logfreq;
}
const uint8_t velocity_data[64] = {
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163,
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202,
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232,
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251,
252, 253, 254
};
// See "velocity" section of notes. Returns velocity delta in microsteps.
int ScaleVelocity(int velocity, int sensitivity) {
int clamped_vel = std::max(0, std::min(127, velocity));
int vel_value = velocity_data[clamped_vel >> 1] - 239;
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4;
return scaled_vel;
}
int ScaleRate(int midinote, int sensitivity) {
int x = std::min(31, std::max(0, midinote / 3 - 7));
int qratedelta = (sensitivity * x) >> 3;
#ifdef SUPER_PRECISE
int rem = x & 7;
if (sensitivity == 3 && rem == 3) {
qratedelta -= 1;
} else if (sensitivity == 7 && rem > 0 && rem < 4) {
qratedelta += 1;
}
#endif
return qratedelta;
}
const uint8_t exp_scale_data[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66,
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250
};
int ScaleCurve(int group, int depth, int curve) {
int scale;
if (curve == 0 || curve == 3) {
// linear
scale = (group * depth * 329) >> 12;
} else {
// exponential
int n_scale_data = sizeof(exp_scale_data);
int raw_exp = exp_scale_data[std::min(group, n_scale_data - 1)];
scale = (raw_exp * depth * 329) >> 15;
}
if (curve < 2) {
scale = -scale;
}
return scale;
}
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth,
int left_curve, int right_curve) {
int offset = midinote - break_pt - 17;
if (offset >= 0) {
return ScaleCurve((offset + 1) / 3, right_depth, right_curve);
} else {
return ScaleCurve(-(offset - 1) / 3, left_depth, left_curve);
}
}
static const uint8_t pitchmodsenstab[] = {
0, 10, 20, 33, 55, 92, 153, 255
};
// 0, 66, 109, 255
static const uint32_t ampmodsenstab[] = {
0, 4342338, 7171437, 16777216
};
Dx7Note::Dx7Note() {
for (int op = 0; op < 6; op++) {
params_[op].phase = 0;
params_[op].gain_out = 0;
}
}
//void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) {
void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity, int srcnote, int porta, const Controllers *ctrls) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = std::min(127, outlevel);
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = std::max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].init((const int32_t*)rates, (const int32_t*)levels, outlevel, rate_scaling);
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
opMode[op] = mode;
basepitch_[op] = freq;
porta_curpitch_[op] = freq;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
if (porta >= 0)
porta_curpitch_[op] = osc_freq(srcnote, mode, coarse, fine, detune);
}
for (int i = 0; i < 4; i++) {
rates[i] = patch[126 + i];
levels[i] = patch[130 + i];
}
pitchenv_.set((const int32_t *)rates, (const int32_t*)levels);
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
porta_rateindex_ = (porta < 128) ? porta : 127;
porta_gliss_ = ctrls->values_[kControllerPortamentoGlissando];
}
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) {
// ==== PITCH ====
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23));
int32_t pmod_1 = (((int64_t) pmd) * (int64_t) senslfo) >> 39;
pmod_1 = abs(pmod_1);
int32_t pmod_2 = (int32_t)(((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14);
pmod_2 = abs(pmod_2);
int32_t pitch_mod = std::max(pmod_1, pmod_2);
pitch_mod = pitchenv_.getsample() + (pitch_mod * (senslfo < 0 ? -1 : 1));
// ---- PITCH BEND ----
int pitchbend = ctrls->values_[kControllerPitch];
int32_t pb = (pitchbend - 0x2000);
if (pb != 0) {
if (ctrls->values_[kControllerPitchStep] == 0) {
pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRange]) / 12.0;
} else {
int stp = 12 / ctrls->values_[kControllerPitchStep];
pb = pb * stp / 8191;
pb = (pb * (8191 / stp)) << 11;
}
}
int32_t pitch_base = pb + ctrls->masterTune;
pitch_mod += pitch_base;
// ==== AMP MOD ====
lfo_val = (1 << 24) - lfo_val;
uint32_t amod_1 = (uint32_t)(((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8); // Q24 :D
amod_1 = (uint32_t)(((int64_t) amod_1 * (int64_t) lfo_val) >> 24);
uint32_t amod_2 = (uint32_t)(((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7); // Q?? :|
uint32_t amd_mod = std::max(amod_1, amod_2);
// ==== EG AMP MOD ====
uint32_t amod_3 = (ctrls->eg_mod + 1) << 17;
amd_mod = std::max((1 << 24) - amod_3, amd_mod);
// ==== OP RENDER ====
for (int op = 0; op < 6; op++) {
// if ( ctrls->opSwitch[op] == '0' ) {
if (!(ctrls->opSwitch & (1 << op))) {
env_[op].getsample(); // advance the envelop even if it is not playing
params_[op].level_in = 0;
} else {
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
int32_t basepitch = basepitch_[op];
if ( opMode[op] )
params_[op].freq = Freqlut::lookup(basepitch + pitch_base);
else {
if ( porta_rateindex_ >= 0 ) {
basepitch = porta_curpitch_[op];
if ( porta_gliss_ )
basepitch = logfreq_round2semi(basepitch);
}
params_[op].freq = Freqlut::lookup(basepitch + pitch_mod);
}
int32_t level = env_[op].getsample();
if (ampmodsens_[op] != 0) {
uint32_t sensamp = (uint32_t)(((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24);
// TODO: mehhh.. this needs some real tuning.
//uint32_t pt = exp(((float)sensamp) / 262144 * 0.07 + 12.2);
uint32_t pt = EXP_FUNC(((float)sensamp) / 262144 * 0.07 + 12.2);
uint32_t ldiff = (uint32_t)(((uint64_t)level) * (((uint64_t)pt << 4)) >> 28);
level -= ldiff;
}
params_[op].level_in = level;
}
}
// ==== PORTAMENTO ====
int porta = porta_rateindex_;
if ( porta >= 0 ) {
int32_t rate = Porta::rates[porta];
for (int op = 0; op < 6; op++) {
int32_t cur = porta_curpitch_[op];
int32_t dst = basepitch_[op];
bool going_up = cur < dst;
int32_t newpitch = cur + (going_up ? +rate : -rate);
if ( going_up ? (cur > dst) : (cur < dst) )
newpitch = dst;
porta_curpitch_[op] = newpitch;
}
}
ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_);
}
void Dx7Note::keyup() {
for (int op = 0; op < 6; op++) {
env_[op].keydown(false);
}
pitchenv_.keydown(false);
}
void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity, int porta, const Controllers *ctrls) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
basepitch_[op] = freq;
porta_curpitch_[op] = freq;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
opMode[op] = mode;
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = std::min(127, outlevel);
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = std::max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].update((const int32_t*)rates, (const int32_t*)levels, (int32_t)outlevel, rate_scaling);
}
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
porta_rateindex_ = (porta < 128) ? porta : 127;
porta_gliss_ = ctrls->values_[kControllerPortamentoGlissando];
}
void Dx7Note::peekVoiceStatus(VoiceStatus &status) {
for (int i = 0; i < 6; i++) {
status.amp[i] = Exp2::lookup(params_[i].level_in - (14 * (1 << 24)));
env_[i].getPosition(&status.ampStep[i]);
}
pitchenv_.getPosition(&status.pitchStep);
}
/**
Used in monophonic mode to transfer voice state from different notes
*/
void Dx7Note::transferState(Dx7Note &src) {
for (int i = 0; i < 6; i++) {
env_[i].transfer(src.env_[i]);
params_[i].gain_out = src.params_[i].gain_out;
params_[i].phase = src.params_[i].phase;
}
}
void Dx7Note::transferSignal(Dx7Note &src) {
for (int i = 0; i < 6; i++) {
params_[i].gain_out = src.params_[i].gain_out;
params_[i].phase = src.params_[i].phase;
}
}
void Dx7Note::transferPortamento(Dx7Note &src) {
for (int i = 0; i < 6; i++) {
porta_curpitch_[i] = src.porta_curpitch_[i];
}
}
void Dx7Note::oscSync() {
for (int i = 0; i < 6; i++) {
params_[i].gain_out = 0;
params_[i].phase = 0;
}
}

@ -0,0 +1,82 @@
/*
Copyright 2016-2017 Pascal Gauthier.
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SYNTH_DX7NOTE_H_
#define SYNTH_DX7NOTE_H_
// This is the logic to put together a note from the MIDI description
// and run the low-level modules.
// It will continue to evolve a bit, as note-stealing logic, scaling,
// and real-time control of parameters live here.
#include <stdint.h>
#include "env.h"
#include "pitchenv.h"
#include "fm_core.h"
struct VoiceStatus {
uint32_t amp[6];
char ampStep[6];
char pitchStep;
};
class Dx7Note {
public:
Dx7Note();
void init(const uint8_t patch[156], int midinote, int velocity, int srcnote, int porta, const Controllers *ctrls);
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls);
void keyup();
// TODO: some way of indicating end-of-note. Maybe should be a return
// value from the compute method? (Having a count return from keyup
// is also tempting, but if there's a dynamic parameter change after
// keyup, that won't work.
// PG:add the update
void update(const uint8_t patch[156], int midinote, int velocity, int porta, const Controllers *ctrls);
void peekVoiceStatus(VoiceStatus &status);
void transferState(Dx7Note& src);
void transferSignal(Dx7Note &src);
void transferPortamento(Dx7Note &src);
void oscSync();
private:
Env env_[6];
FmOpParams params_[6];
PitchEnv pitchenv_;
int32_t basepitch_[6];
int32_t fb_buf_[2]={0 ,0};
int32_t fb_shift_;
int32_t ampmodsens_[6];
int32_t opMode[6];
int ampmoddepth_;
int algorithm_;
int pitchmoddepth_;
int pitchmodsens_;
int porta_rateindex_;
int porta_gliss_;
int32_t porta_curpitch_[6];
};
#endif

@ -0,0 +1,190 @@
/*
Copyright 2017 Pascal Gauthier.
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//using namespace std;
#include <math.h>
#include "synth.h"
#include "env.h"
uint32_t Env::sr_multiplier = (1 << 24);
const int32_t levellut[] = {
0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46
};
#ifdef ACCURATE_ENVELOPE
const int32_t statics[] = {
1764000, 1764000, 1411200, 1411200, 1190700, 1014300, 992250,
882000, 705600, 705600, 584325, 507150, 502740, 441000, 418950,
352800, 308700, 286650, 253575, 220500, 220500, 176400, 145530,
145530, 125685, 110250, 110250, 88200, 88200, 74970, 61740,
61740, 55125, 48510, 44100, 37485, 31311, 30870, 27562, 27562,
22050, 18522, 17640, 15435, 14112, 13230, 11025, 9261, 9261, 7717,
6615, 6615, 5512, 5512, 4410, 3969, 3969, 3439, 2866, 2690, 2249,
1984, 1896, 1808, 1411, 1367, 1234, 1146, 926, 837, 837, 705,
573, 573, 529, 441, 441
// and so on, I stopped measuring after R=76 (needs to be double-checked anyway)
};
#endif
void Env::init_sr(double sampleRate) {
sr_multiplier = (44100.0 / sampleRate) * (1 << 24);
}
void Env::init(const int32_t r[4], const int32_t l[4], int32_t ol, int32_t rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
outlevel_ = ol;
rate_scaling_ = rate_scaling;
level_ = 0;
down_ = true;
advance(0);
}
int32_t Env::getsample() {
#ifdef ACCURATE_ENVELOPE
if (staticcount_) {
staticcount_ -= _N_;
if (staticcount_ <= 0) {
staticcount_ = 0;
advance(ix_ + 1);
}
}
#endif
if (ix_ < 3 || ((ix_ < 4) && !down_)) {
if (staticcount_) {
;
}
else if (rising_) {
const int32_t jumptarget = 1716;
if (level_ < (jumptarget << 16)) {
level_ = jumptarget << 16;
}
level_ += (((17 << 24) - level_) >> 24) * inc_;
// TODO: should probably be more accurate when inc is large
if (level_ >= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
}
else { // !rising
level_ -= inc_;
if (level_ <= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
}
}
// TODO: this would be a good place to set level to 0 when under threshold
return level_;
}
void Env::keydown(bool d) {
if (down_ != d) {
down_ = d;
advance(d ? 0 : 3);
}
}
int32_t Env::scaleoutlevel(int32_t outlevel) {
return outlevel >= 20 ? 28 + outlevel : levellut[outlevel];
}
void Env::advance(int newix) {
ix_ = newix;
if (ix_ < 4) {
int32_t newlevel = levels_[ix_];
int32_t actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) + outlevel_ - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
// level here is same as Java impl
targetlevel_ = actuallevel << 16;
rising_ = (targetlevel_ > level_);
// rate
int32_t qrate = (rates_[ix_] * 41) >> 6;
qrate += rate_scaling_;
qrate = std::min(int(qrate), 63);
#ifdef ACCURATE_ENVELOPE
if (targetlevel_ == level_ || (ix_ == 0 && newlevel == 0)) {
// approximate number of samples at 44.100 kHz to achieve the time
// empirically gathered using 2 TF1s, could probably use some double-checking
// and cleanup, but it's pretty close for now.
int32_t staticrate = rates_[ix_];
staticrate += rate_scaling_; // needs to be checked, as well, but seems correct
staticrate = std::min(int(staticrate), 99);
staticcount_ = staticrate < 77 ? statics[staticrate] : 20 * (99 - staticrate);
if (staticrate < 77 && (ix_ == 0 && newlevel == 0)) {
staticcount_ /= 20; // attack is scaled faster
}
staticcount_ = (int)(((int64_t)staticcount_ * (int64_t)sr_multiplier) >> 24);
}
else {
staticcount_ = 0;
}
#endif
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2));
// meh, this should be fixed elsewhere
inc_ = (int)(((int64_t)inc_ * (int64_t)sr_multiplier) >> 24);
}
}
void Env::update(const int32_t r[4], const int32_t l[4], int32_t ol, int32_t rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
outlevel_ = ol;
rate_scaling_ = rate_scaling;
if ( down_ ) {
// for now we simply reset ourselves at level 3
int32_t newlevel = levels_[2];
int32_t actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
targetlevel_ = actuallevel << 16;
advance(2);
}
}
void Env::getPosition(char *step) {
*step = ix_;
}
void Env::transfer(Env &src) {
for (int i = 0; i < 4; i++) {
rates_[i] = src.rates_[i];
levels_[i] = src.levels_[i];
}
outlevel_ = src.outlevel_;
rate_scaling_ = src.rate_scaling_;
level_ = src.level_;
targetlevel_ = src.targetlevel_;
rising_ = src.rising_;
ix_ = src.ix_;
down_ = src.down_;
#ifdef ACCURATE_ENVELOPE
staticcount_ = src.staticcount_;
#endif
inc_ = src.inc_;
}

@ -0,0 +1,81 @@
/*
Copyright 2017 Pascal Gauthier.
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __ENV_H
#define __ENV_H
#include "synth.h"
// DX7 envelope generation
#define ACCURATE_ENVELOPE
class Env {
public:
// The rates and levels arrays are calibrated to match the Dx7 parameters
// (ie, value 0..99). The outlevel parameter is calibrated in microsteps
// (ie units of approx .023 dB), with 99 * 32 = nominal full scale. The
// rate_scaling parameter is in qRate units (ie 0..63).
void init(const int32_t rates[4], const int32_t levels[4], int32_t outlevel,
int32_t rate_scaling);
void update(const int32_t rates[4], const int32_t levels[4], int32_t outlevel,
int32_t rate_scaling);
// Result is in Q24/doubling log format. Also, result is subsampled
// for every N samples.
// A couple more things need to happen for this to be used as a gain
// value. First, the # of outputs scaling needs to be applied. Also,
// modulation.
// Then, of course, log to linear.
int32_t getsample();
void keydown(bool down);
static int32_t scaleoutlevel(int32_t outlevel);
void getPosition(char *step);
static void init_sr(double sample_rate);
void transfer(Env &src);
private:
// PG: This code is normalized to 44100, need to put a multiplier
// if we are not using 44100.
static uint32_t sr_multiplier;
int32_t rates_[4];
int32_t levels_[4];
int32_t outlevel_;
int32_t rate_scaling_;
// Level is stored so that 2^24 is one doubling, ie 16 more bits than
// the DX7 itself (fraction is stored in level rather than separate
// counter)
int32_t level_;
int32_t targetlevel_;
bool rising_;
int32_t ix_;
int32_t inc_;
#ifdef ACCURATE_ENVELOPE
int32_t staticcount_;
#endif
bool down_;
void advance(int newix);
};
#endif

@ -0,0 +1,70 @@
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _USE_MATH_DEFINES
#include <math.h>
#include "synth.h"
#include "exp2.h"
//#include <stdio.h>
#ifdef _MSC_VER
#define exp2(arg) pow(2.0, arg)
#endif
int32_t exp2tab[EXP2_N_SAMPLES << 1];
void Exp2::init() {
FRAC_NUM inc = exp2(1.0 / EXP2_N_SAMPLES);
FRAC_NUM y = 1 << 30;
for (int i = 0; i < EXP2_N_SAMPLES; i++) {
exp2tab[(i << 1) + 1] = (int32_t)floor(y + 0.5);
y *= inc;
}
for (int i = 0; i < EXP2_N_SAMPLES - 1; i++) {
exp2tab[i << 1] = exp2tab[(i << 1) + 3] - exp2tab[(i << 1) + 1];
}
exp2tab[(EXP2_N_SAMPLES << 1) - 2] = (1U << 31) - exp2tab[(EXP2_N_SAMPLES << 1) - 1];
}
int32_t tanhtab[TANH_N_SAMPLES << 1];
static FRAC_NUM dtanh(FRAC_NUM y) {
return 1 - y * y;
}
void Tanh::init() {
FRAC_NUM step = 4.0 / TANH_N_SAMPLES;
FRAC_NUM y = 0;
for (int i = 0; i < TANH_N_SAMPLES; i++) {
tanhtab[(i << 1) + 1] = (1 << 24) * y + 0.5;
//printf("%d\n", tanhtab[(i << 1) + 1]);
// Use a basic 4th order Runge-Kutte to compute tanh from its
// differential equation.
FRAC_NUM k1 = dtanh(y);
FRAC_NUM k2 = dtanh(y + 0.5 * step * k1);
FRAC_NUM k3 = dtanh(y + 0.5 * step * k2);
FRAC_NUM k4 = dtanh(y + step * k3);
FRAC_NUM dy = (step / 6) * (k1 + k4 + 2 * (k2 + k3));
y += dy;
}
for (int i = 0; i < TANH_N_SAMPLES - 1; i++) {
tanhtab[i << 1] = tanhtab[(i << 1) + 3] - tanhtab[(i << 1) + 1];
}
int32_t lasty = (1 << 24) * y + 0.5;
tanhtab[(TANH_N_SAMPLES << 1) - 2] = lasty - tanhtab[(TANH_N_SAMPLES << 1) - 1];
}

@ -0,0 +1,80 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
class Exp2 {
public:
Exp2();
static void init();
// Q24 in, Q24 out
static int32_t lookup(int32_t x);
};
#define EXP2_LG_N_SAMPLES 10
#define EXP2_N_SAMPLES (1 << EXP2_LG_N_SAMPLES)
#define EXP2_INLINE
extern int32_t exp2tab[EXP2_N_SAMPLES << 1];
#ifdef EXP2_INLINE
inline
int32_t Exp2::lookup(int32_t x) {
const int32_t SHIFT = 24 - EXP2_LG_N_SAMPLES;
int32_t lowbits = x & ((1 << SHIFT) - 1);
int32_t x_int = (x >> (SHIFT - 1)) & ((EXP2_N_SAMPLES - 1) << 1);
int32_t dy = exp2tab[x_int];
int32_t y0 = exp2tab[x_int + 1];
int32_t y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
return y >> (6 - (x >> 24));
}
#endif
class Tanh {
public:
static void init();
// Q24 in, Q24 out
static int32_t lookup(int32_t x);
};
#define TANH_LG_N_SAMPLES 10
#define TANH_N_SAMPLES (1 << TANH_LG_N_SAMPLES)
extern int32_t tanhtab[TANH_N_SAMPLES << 1];
inline
int32_t Tanh::lookup(int32_t x) {
int32_t signum = x >> 31;
x ^= signum;
if (x >= (4 << 24)) {
if (x >= (17 << 23)) {
return signum ^ (1 << 24);
}
int32_t sx = ((int64_t) - 48408812 * (int64_t)x) >> 24;
return signum ^ ((1 << 24) - 2 * Exp2::lookup(sx));
} else {
const int32_t SHIFT = 26 - TANH_LG_N_SAMPLES;
int32_t lowbits = x & ((1 << SHIFT) - 1);
int32_t x_int = (x >> (SHIFT - 1)) & ((TANH_N_SAMPLES - 1) << 1);
int32_t dy = tanhtab[x_int];
int32_t y0 = tanhtab[x_int + 1];
int32_t y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
return y ^ signum;
}
}

@ -0,0 +1,100 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//using namespace std;
#include "synth.h"
#include "exp2.h"
#include "fm_op_kernel.h"
#include "fm_core.h"
const FmAlgorithm FmCore::algorithms[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
{ { 0xc1, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
{ { 0xc1, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
};
int n_out(const FmAlgorithm &alg) {
int32_t count = 0;
for (int i = 0; i < 6; i++) {
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++;
}
return count;
}
uint8_t FmCore::get_carrier_operators(uint8_t algorithm)
{
uint8_t op_out = 0;
FmAlgorithm alg = algorithms[algorithm];
for (uint8_t i = 0; i < 6; i++)
{
if ((alg.ops[i]&OUT_BUS_ADD) == OUT_BUS_ADD)
op_out |= 1 << i;
}
return op_out;
}
void FmCore::dump() {
#ifdef VERBOSE
for (int i = 0; i < 32; i++) {
cout << (i + 1) << ":";
const FmAlgorithm &alg = algorithms[i];
for (int j = 0; j < 6; j++) {
int32_t flags = alg.ops[j];
cout << " ";
if (flags & FB_IN) cout << "[";
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->";
cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0");
if (flags & OUT_BUS_ADD) cout << "+";
//cout << alg.ops[j].in << "->" << alg.ops[j].out;
if (flags & FB_OUT) cout << "]";
}
cout << " " << n_out(alg);
cout << endl;
}
#endif
}

@ -0,0 +1,59 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __FM_CORE_H
#define __FM_CORE_H
#include <stdint.h>
#include "aligned_buf.h"
#include "fm_op_kernel.h"
#include "synth.h"
#include "controllers.h"
class FmOperatorInfo {
public:
int32_t in;
int32_t out;
};
enum FmOperatorFlags {
OUT_BUS_ONE = 1 << 0,
OUT_BUS_TWO = 1 << 1,
OUT_BUS_ADD = 1 << 2,
IN_BUS_ONE = 1 << 4,
IN_BUS_TWO = 1 << 5,
FB_IN = 1 << 6,
FB_OUT = 1 << 7
};
class FmAlgorithm {
public:
int32_t ops[6];
};
class FmCore {
public:
FmCore() {};
virtual ~FmCore() {};
static void dump();
static uint8_t get_carrier_operators(uint8_t algorithm);
virtual void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_gain) = 0;
protected:
AlignedBuf<int32_t, _N_>buf_[2];
const static FmAlgorithm algorithms[32];
};
#endif

@ -0,0 +1,384 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <math.h>
#include <cstdlib>
#include <stdint.h>
#include "synth.h"
#include "sin.h"
#include "fm_op_kernel.h"
#ifdef __ARM_NEON
#include <arm_neon.h>
extern "C"
void neon_fm_kernel(const int32_t *in, const int32_t *busin, int32_t *out, int32_t count,
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain);
const int32_t __attribute__ ((aligned(16))) const_0_1_2_3_arg[4] = {0, 1, 2, 3};
const int32_t __attribute__ ((aligned(16))) mask23_arg = 0x7fffff;
const float __attribute__ ((aligned(16))) coeffs_arg[4] = {
-0.01880853017455781, 0.25215252666796095, -1.2333439964934032, 1.0
};
const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0};
void neon_fm_kernel(const int32_t *in, const int32_t *busin, int32_t *out, int32_t count,
int32_t phase0, int32_t freq_arg, int32_t gain1_arg, int32_t dgain_arg) {
int32x4_t phase = vld1q_dup_s32(&phase0);
int32x4_t freq = vld1q_dup_s32(&freq_arg);
int32x4_t const_0_1_2_3 = vld1q_s32(const_0_1_2_3_arg);
phase = vmlaq_s32(phase, freq, const_0_1_2_3);
int32x4_t gain1 = vld1q_dup_s32(&gain1_arg);
int32x4_t dgain = vld1q_dup_s32(&dgain_arg);
gain1 = vmlaq_s32(gain1, dgain, const_0_1_2_3);
int32x4_t mask23 = vld1q_dup_s32(&mask23_arg);
float32x4_t coeffs = vld1q_f32(coeffs_arg);
float32x4_t gainf = vcvtq_n_f32_s32(gain1, 24);
int32x4_t freq4 = vshlq_n_s32(freq, 2);
float32x4_t dgainf = vcvtq_n_f32_s32(dgain, 22);
count -= 4;
int32x4_t q15 = vmovq_n_s32(0x800000);
int32x4_t q7 = vmovq_n_s32(0x400000);
while (true) {
int32x4_t phase4 = vaddq_s32(phase, freq4);
int32x4_t phase8 = vaddq_s32(phase4, freq4);
int32x4_t data1a = vld1q_s32(in);
data1a = vaddq_s32(data1a, phase);
int32x4_t data1b = vld1q_s32(in + 4);
data1b = vaddq_s32(data1b, phase4);
int32x4_t data1c = vld1q_s32(in + 8);
data1c = vaddq_s32(data1c, phase8);
phase = vaddq_s32(phase8, freq4);
in += 12;
int32x4_t data4a = (int32x4_t)vtstq_s32(data1a, q15);
int32x4_t data4b = (int32x4_t)vtstq_s32(data1b, q15);
int32x4_t data4c = (int32x4_t)vtstq_s32(data1c, q15);
data1a = vandq_s32(data1a, mask23);
data1b = vandq_s32(data1b, mask23);
data1c = vandq_s32(data1c, mask23);
data1a = vsubq_s32(data1a, q7);
data1b = vsubq_s32(data1b, q7);
data1c = vsubq_s32(data1c, q7);
float32x4_t fdata1a = vcvtq_n_f32_s32(data1a, 22);
float32x4_t fdata1b = vcvtq_n_f32_s32(data1b, 22);
float32x4_t fdata1c = vcvtq_n_f32_s32(data1c, 22);
fdata1a = vmulq_f32(fdata1a, fdata1a);
fdata1b = vmulq_f32(fdata1b, fdata1b);
fdata1c = vmulq_f32(fdata1c, fdata1c);
float32x4_t fdata2a = vdupq_lane_f32(vget_low_f32(coeffs), 1);
float32x4_t fdata2b = vdupq_lane_f32(vget_low_f32(coeffs), 1);
float32x4_t fdata2c = vdupq_lane_f32(vget_low_f32(coeffs), 1);
fdata2a = vmlaq_lane_f32(fdata2a, fdata1a, vget_low_f32(coeffs), 0);
fdata2b = vmlaq_lane_f32(fdata2b, fdata1b, vget_low_f32(coeffs), 0);
fdata2c = vmlaq_lane_f32(fdata2c, fdata1c, vget_low_f32(coeffs), 0);
float32x4_t fdata3a = vdupq_lane_f32(vget_high_f32(coeffs), 0);
float32x4_t fdata3b = vdupq_lane_f32(vget_high_f32(coeffs), 0);
float32x4_t fdata3c = vdupq_lane_f32(vget_high_f32(coeffs), 0);
fdata3a = vmlaq_f32(fdata3a, fdata1a, fdata2a);
fdata3b = vmlaq_f32(fdata3b, fdata1b, fdata2b);
fdata3c = vmlaq_f32(fdata3c, fdata1c, fdata2c);
fdata2a = vdupq_lane_f32(vget_high_f32(coeffs), 1);
fdata2b = vdupq_lane_f32(vget_high_f32(coeffs), 1);
fdata2c = vdupq_lane_f32(vget_high_f32(coeffs), 1);
fdata2a = vmlaq_f32(fdata2a, fdata1a, fdata3a);
fdata2b = vmlaq_f32(fdata2b, fdata1b, fdata3b);
fdata2c = vmlaq_f32(fdata2c, fdata1c, fdata3c);
fdata3a = vaddq_f32(gainf, dgainf);
fdata3b = vaddq_f32(fdata3a, dgainf);
fdata2a = vmulq_f32(fdata2a, gainf);
fdata2b = vmulq_f32(fdata2b, fdata3a);
fdata2c = vmulq_f32(fdata2c, fdata3b);
gainf = vaddq_f32(fdata3b, dgainf);
int32x4_t data3a = vcvtq_n_s32_f32(fdata2a, 24);
int32x4_t data3b = vcvtq_n_s32_f32(fdata2b, 24);
int32x4_t data3c = vcvtq_n_s32_f32(fdata2c, 24);
data1a = vld1q_s32(busin);
data1b = vld1q_s32(busin + 4);
data1c = vld1q_s32(busin + 8);
busin += 12;
data3a = veorq_s32(data3a, data4a);
data3b = veorq_s32(data3b, data4b);
data3c = veorq_s32(data3c, data4c);
data3a = vaddq_s32(data3a, data1a);
data3b = vaddq_s32(data3b, data1b);
data3c = vaddq_s32(data3c, data1c);
vst1q_s32(out, data3a);
vst1q_s32(out + 4, data3b);
vst1q_s32(out + 8, data3c);
out += 12;
count -= 12;
if (count <= 0) {
if (count == 0) {
// finish last chunk of 4
data1a = vld1q_s32(in);
data1a = vaddq_s32(data1a, phase);
data4a = (int32x4_t)vtstq_s32(data1a, q15);
data1a = vandq_s32(data1a, mask23);
data1a = vsubq_s32(data1a, q7);
fdata1a = vcvtq_n_f32_s32(data1a, 22);
fdata1a = vmulq_f32(fdata1a, fdata1a);
fdata2a = vdupq_lane_f32(vget_low_f32(coeffs), 1);
fdata2a = vmlaq_lane_f32(fdata2a, fdata1a, vget_low_f32(coeffs), 0);
fdata3a = vdupq_lane_f32(vget_high_f32(coeffs), 0);
fdata3a = vmlaq_f32(fdata3a, fdata1a, fdata2a);
fdata2a = vdupq_lane_f32(vget_high_f32(coeffs), 1);
fdata2a = vmlaq_f32(fdata2a, fdata1a, fdata3a);
fdata2a = vmulq_f32(fdata2a, gainf);
data3a = vcvtq_n_s32_f32(fdata2a, 24);
data1a = vld1q_s32(busin);
data3a = veorq_s32(data3a, data4a);
data3a = vaddq_s32(data3a, data1a);
vst1q_s32(out, data3a);
}
break;
}
}
}
#endif
void FmOpKernel::compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
#ifdef __ARM_NEON
neon_fm_kernel(input, add ? output : zeros, output, _N_,
phase0, freq, gain, dgain);
#else
int32_t phase = phase0;
if (add) {
for (int i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
}
#endif
}
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
#ifdef __ARM_NEON
neon_fm_kernel(zeros, add ? output : zeros, output, _N_,
phase0, freq, gain, dgain);
#else
int32_t phase = phase0;
if (add) {
for (int i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
}
#endif
}
#define noDOUBLE_ACCURACY
#define HIGH_ACCURACY
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int32_t fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
if (add) {
for (int i = 0; i < _N_; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y;
phase += freq;
}
} else {
for (int i = 0; i < _N_; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase += freq;
}
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Experimental sine wave generators below
#if 0
// Results: accuracy 64.3 mean, 170 worst case
// high accuracy: 5.0 mean, 49 worst case
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
u = ((int64_t)u * gain) >> 30;
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
v = ((int64_t)v * gain) >> 30;
int32_t s = Sin::compute10(freq << 6);
int32_t c = Sin::compute10((freq << 6) + (1 << 28));
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
v = ((int64_t)v * gain) >> 24;
int32_t s = Sin::compute(freq) << 6;
int32_t c = Sin::compute(freq + (1 << 22)) << 6;
#endif
for (int i = 0; i < _N_; i++) {
output[i] = u;
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30;
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30;
v = t;
}
}
#endif
#if 0
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
// for freq < 0.25, 275.2 mean, 716 worst
// high accuracy: 57.4 mean, 7559 worst
// freq < 0.25: 17.9 mean, 78 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5);
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5);
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5);
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
v = ((int64_t)v * gain) >> 24;
int32_t a = Sin::compute(freq >> 1) << 1;
#endif
for (int i = 0; i < _N_; i++) {
output[i] = u;
v -= ((int64_t)a * (int64_t)u) >> 24;
u += ((int64_t)a * (int64_t)v) >> 24;
}
}
#endif
#if 0
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
// with FRAC_NUM accuracy initialization: mean 1.55, worst 58 (near freq = 0)
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef DOUBLE_ACCURACY
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5);
FRAC_NUM a_d = sin(freq * (M_PI / (1 << 24)));
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) *
(M_PI / (1 << 23))) + 0.5);
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5);
#else
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5));
int32_t a = Sin::compute10(freq << 5);
v = ((int64_t)v * (int64_t)a) >> 29;
int32_t aa = ((int64_t)a * (int64_t)a) >> 29;
#else
int32_t u = Sin::compute(phase) << 6;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
int32_t a = Sin::compute(freq >> 1);
v = ((int64_t)v * (int64_t)a) >> 17;
int32_t aa = ((int64_t)a * (int64_t)a) >> 17;
#endif
#endif
if (aa < 0) aa = (1 << 31) - 1;
for (int i = 0; i < _N_; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
v -= ((int64_t)aa * (int64_t)u) >> 29;
u += v;
}
}
#endif
#if 0
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
// high accuracy 2.9 mean, 143 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t lastu = Sin::compute10((phase - freq) << 6);
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1;
#else
int32_t u = Sin::compute(phase) << 6;
int32_t lastu = Sin::compute(phase - freq) << 6;
int32_t a = Sin::compute(freq + (1 << 22)) << 7;
#endif
if (a < 0 && freq < 256) a = (1 << 31) - 1;
if (a > 0 && freq > 0x7fff00) a = -(1 << 31);
for (int i = 0; i < _N_; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
//output[i] = u;
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu;
lastu = u;
u = newu;
}
}
#endif

@ -0,0 +1,47 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __FM_OP_KERNEL_H
#define __FM_OP_KERNEL_H
struct FmOpParams {
int32_t level_in; // value to be computed (from level to gain[0])
int32_t gain_out; // computed value (gain[1] to gain[0])
int32_t freq;
int32_t phase;
};
class FmOpKernel {
public:
// gain1 and gain2 represent linear step: gain for sample i is
// gain1 + (1 + i) / 64 * (gain2 - gain1)
// This is the basic FM operator. No feedback.
static void compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add);
// This is a sine generator, no feedback.
static void compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add);
// One op with feedback, no add.
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int32_t fb_gain, bool add);
};
#endif

@ -0,0 +1,56 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Resolve frequency signal (1.0 in Q24 format = 1 octave) to phase delta.
// The LUT is just a global, and we'll need the init function to be called before
// use.
#include <stdint.h>
#include <math.h>
#include "freqlut.h"
#include "synth.h"
#define LG_N_SAMPLES 10
#define N_SAMPLES (1 << LG_N_SAMPLES)
#define SAMPLE_SHIFT (24 - LG_N_SAMPLES)
#define MAX_LOGFREQ_INT 20
int32_t lut[N_SAMPLES + 1];
void Freqlut::init(FRAC_NUM sample_rate) {
FRAC_NUM y = (1LL << (24 + MAX_LOGFREQ_INT)) / sample_rate;
FRAC_NUM inc = pow(2, 1.0 / N_SAMPLES);
for (int i = 0; i < N_SAMPLES + 1; i++) {
lut[i] = (int32_t)floor(y + 0.5);
y *= inc;
}
}
// Note: if logfreq is more than 20.0, the results will be inaccurate. However,
// that will be many times the Nyquist rate.
int32_t Freqlut::lookup(int32_t logfreq) {
int32_t ix = (logfreq & 0xffffff) >> SAMPLE_SHIFT;
int32_t y0 = lut[ix];
int32_t y1 = lut[ix + 1];
int32_t lowbits = logfreq & ((1 << SAMPLE_SHIFT) - 1);
int32_t y = y0 + ((((int64_t)(y1 - y0) * (int64_t)lowbits)) >> SAMPLE_SHIFT);
int32_t hibits = logfreq >> 24;
return y >> (MAX_LOGFREQ_INT - hibits);
}

@ -0,0 +1,23 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "synth.h"
class Freqlut {
public:
static void init(FRAC_NUM sample_rate);
static int32_t lookup(int32_t logfreq);
};

@ -0,0 +1,99 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Low frequency oscillator, compatible with DX7
#include <algorithm>
#include "synth.h"
#include "sin.h"
#include "lfo.h"
uint32_t Lfo::unit_;
void Lfo::init(FRAC_NUM sample_rate) {
// constant is 1 << 32 / 15.5s / 11
Lfo::unit_ = (int32_t)(_N_ * 25190424 / sample_rate + 0.5);
}
void Lfo::reset(const uint8_t params[6]) {
int32_t rate = params[0]; // 0..99
int32_t sr = rate == 0 ? 1 : (165 * rate) >> 6;
sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4));
delta_ = unit_ * sr;
int32_t a = 99 - params[1]; // LFO delay
if (a == 99) {
delayinc_ = ~0u;
delayinc2_ = ~0u;
} else {
a = (16 + (a & 15)) << (1 + (a >> 4));
delayinc_ = unit_ * a;
a &= 0xff80;
a = std::max(0x80, int(a));
delayinc2_ = unit_ * a;
}
waveform_ = params[5];
sync_ = params[4] != 0;
}
int32_t Lfo::getsample() {
phase_ += delta_;
int32_t x;
switch (waveform_) {
case 0: // triangle
x = phase_ >> 7;
x ^= -(phase_ >> 31);
x &= (1 << 24) - 1;
return x;
case 1: // sawtooth down
return (~phase_ ^ (1U << 31)) >> 8;
case 2: // sawtooth up
return (phase_ ^ (1U << 31)) >> 8;
case 3: // square
return ((~phase_) >> 7) & (1 << 24);
case 4: // sine
return (1 << 23) + (Sin::lookup(phase_ >> 8) >> 1);
case 5: // s&h
if (phase_ < delta_) {
randstate_ = (randstate_ * 179 + 17) & 0xff;
}
x = randstate_ ^ 0x80;
return (x + 1) << 16;
}
return 1 << 23;
}
int32_t Lfo::getdelay() {
uint32_t delta = delaystate_ < (1U << 31) ? delayinc_ : delayinc2_;
uint64_t d = ((uint64_t)delaystate_) + delta;
if (d > ~0u) {
return 1 << 24;
}
delaystate_ = d;
if (d < (1U << 31)) {
return 0;
} else {
return (d >> 7) & ((1 << 24) - 1);
}
}
void Lfo::keydown() {
if (sync_) {
phase_ = (1U << 31) - 1;
}
delaystate_ = 0;
}

@ -0,0 +1,43 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Low frequency oscillator, compatible with DX7
class Lfo {
public:
static void init(FRAC_NUM sample_rate);
void reset(const uint8_t params[6]);
// result is 0..1 in Q24
int32_t getsample();
// result is 0..1 in Q24
int32_t getdelay();
void keydown();
private:
static uint32_t unit_;
uint32_t phase_; // Q32
uint32_t delta_;
uint8_t waveform_;
uint8_t randstate_;
bool sync_;
uint32_t delaystate_;
uint32_t delayinc_;
uint32_t delayinc2_;
};

@ -0,0 +1,93 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "synth.h"
#include "pitchenv.h"
int32_t PitchEnv::unit_;
void PitchEnv::init(FRAC_NUM sample_rate) {
unit_ = _N_ * (1 << 24) / (21.3 * sample_rate) + 0.5;
}
const uint8_t pitchenv_rate[] = {
1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12,
12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 30, 31, 33, 34, 36, 37, 38, 39, 41, 42, 44, 46, 47,
49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 79, 82,
85, 88, 91, 94, 98, 102, 106, 110, 115, 120, 125, 130, 135, 141, 147,
153, 159, 165, 171, 178, 185, 193, 202, 211, 232, 243, 254, 255
};
const int8_t pitchenv_tab[] = {
-128, -116, -104, -95, -85, -76, -68, -61, -56, -52, -49, -46, -43,
-41, -39, -37, -35, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24,
-23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10,
-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 38, 40, 43, 46, 49, 53, 58, 65, 73,
82, 92, 103, 115, 127
};
void PitchEnv::set(const int32_t r[4], const int32_t l[4]) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
level_ = pitchenv_tab[l[3]] << 19;
down_ = true;
advance(0);
}
int32_t PitchEnv::getsample() {
if (ix_ < 3 || ((ix_ < 4) && !down_)) {
if (rising_) {
level_ += inc_;
if (level_ >= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
} else { // !rising
level_ -= inc_;
if (level_ <= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
}
}
return level_;
}
void PitchEnv::keydown(bool d) {
if (down_ != d) {
down_ = d;
advance(d ? 0 : 3);
}
}
void PitchEnv::advance(int newix) {
ix_ = newix;
if (ix_ < 4) {
int32_t newlevel = levels_[ix_];
targetlevel_ = pitchenv_tab[newlevel] << 19;
rising_ = (targetlevel_ > level_);
inc_ = pitchenv_rate[rates_[ix_]] * unit_;
}
}
void PitchEnv::getPosition(char *step) {
*step = ix_;
}

@ -0,0 +1,52 @@
/*
Copyright 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __PITCHENV_H
#define __PITCHENV_H
// Computation of the DX7 pitch envelope
class PitchEnv {
public:
static void init(FRAC_NUM sample_rate);
// The rates and levels arrays are calibrated to match the Dx7 parameters
// (ie, value 0..99).
void set(const int32_t rates[4], const int32_t levels[4]);
// Result is in Q24/octave
int32_t getsample();
void keydown(bool down);
void getPosition(char *step);
private:
static int32_t unit_;
int32_t rates_[4];
int32_t levels_[4];
int32_t level_;
int32_t targetlevel_;
bool rising_;
int32_t ix_;
int32_t inc_;
bool down_;
void advance(int newix);
};
extern const uint8_t pitchenv_rate[];
extern const int8_t pitchenv_tab[];
#endif

@ -0,0 +1,35 @@
/*
Copyright 2019 Jean Pierre Cimalando.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <math.h>
#include "porta.h"
#include "synth.h"
void Porta::init_sr(double sampleRate)
{
// compute portamento for CC 7-bit range
for (uint8_t i = 0; i < 128; ++i) {
// number of semitones travelled
double sps = 350.0 * pow(2.0, -0.062 * i); // per second
double spf = sps / sampleRate; // per frame
double spp = spf * _N_; // per period
const int32_t step = (1 << 24) / 12;
rates[i] = (int32_t)(0.5f + step * spp); // to pitch units
}
}
int32_t Porta::rates[128];

@ -0,0 +1,28 @@
/*
Copyright 2019 Jean Pierre Cimalando.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SYNTH_PORTA_H_
#define SYNTH_PORTA_H_
#include <stdint.h>
struct Porta {
public:
static void init_sr(double sampleRate);
static int32_t rates[128];
};
#endif

@ -0,0 +1,144 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _USE_MATH_DEFINES
#include <math.h>
#include "synth.h"
#include "sin.h"
#define R (1 << 29)
#ifdef SIN_DELTA
int32_t sintab[SIN_N_SAMPLES << 1];
#else
int32_t sintab[SIN_N_SAMPLES + 1];
#endif
void Sin::init() {
FRAC_NUM dphase = 2 * M_PI / SIN_N_SAMPLES;
//int32_t c = (int32_t)floor(cos(dphase) * (1 << 30) + 0.5);
int32_t c = (int32_t)floor(COS_FUNC(dphase) * (1 << 30) + 0.5);
//int32_t s = (int32_t)floor(sin(dphase) * (1 << 30) + 0.5);
int32_t s = (int32_t)floor(SIN_FUNC(dphase) * (1 << 30) + 0.5);
int32_t u = 1 << 30;
int32_t v = 0;
for (int i = 0; i < SIN_N_SAMPLES / 2; i++) {
#ifdef SIN_DELTA
sintab[(i << 1) + 1] = (v + 32) >> 6;
sintab[((i + SIN_N_SAMPLES / 2) << 1) + 1] = -((v + 32) >> 6);
#else
sintab[i] = (v + 32) >> 6;
sintab[i + SIN_N_SAMPLES / 2] = -((v + 32) >> 6);
#endif
int32_t t = ((int64_t)u * (int64_t)s + (int64_t)v * (int64_t)c + R) >> 30;
u = ((int64_t)u * (int64_t)c - (int64_t)v * (int64_t)s + R) >> 30;
v = t;
}
#ifdef SIN_DELTA
for (int i = 0; i < SIN_N_SAMPLES - 1; i++) {
sintab[i << 1] = sintab[(i << 1) + 3] - sintab[(i << 1) + 1];
}
sintab[(SIN_N_SAMPLES << 1) - 2] = -sintab[(SIN_N_SAMPLES << 1) - 1];
#else
sintab[SIN_N_SAMPLES] = 0;
#endif
}
#ifndef SIN_INLINE
int32_t Sin::lookup(int32_t phase) {
const int32_t SHIFT = 24 - SIN_LG_N_SAMPLES;
int32_t lowbits = phase & ((1 << SHIFT) - 1);
#ifdef SIN_DELTA
int32_t phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int32_t dy = sintab[phase_int];
int32_t y0 = sintab[phase_int + 1];
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
#else
int32_t phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int32_t y0 = sintab[phase_int];
int32_t y1 = sintab[phase_int + 1];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif
}
#endif
#if 0
// The following is an implementation designed not to use any lookup tables,
// based on the following implementation by Basile Graf:
// http://www.rossbencina.com/static/code/sinusoids/even_polynomial_sin_approximation.txt
#define C0 (1 << 24)
#define C1 (331121857 >> 2)
#define C2 (1084885537 >> 4)
#define C3 (1310449902 >> 6)
int32_t Sin::compute(int32_t phase) {
int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22);
int32_t x2 = ((int64_t)x * (int64_t)x) >> 22;
int32_t x4 = ((int64_t)x2 * (int64_t)x2) >> 24;
int32_t x6 = ((int64_t)x2 * (int64_t)x4) >> 24;
int32_t y = C0 -
(((int64_t)C1 * (int64_t)x2) >> 24) +
(((int64_t)C2 * (int64_t)x4) >> 24) -
(((int64_t)C3 * (int64_t)x6) >> 24);
y ^= -((phase >> 23) & 1);
return y;
}
#endif
#if 1
// coefficients are Chebyshev polynomial, computed by compute_cos_poly.py
#define C8_0 16777216
#define C8_2 -331168742
#define C8_4 1089453524
#define C8_6 -1430910663
#define C8_8 950108533
int32_t Sin::compute(int32_t phase) {
int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22);
int32_t x2 = ((int64_t)x * (int64_t)x) >> 16;
int32_t y = (((((((((((((int64_t)C8_8
* (int64_t)x2) >> 32) + C8_6)
* (int64_t)x2) >> 32) + C8_4)
* (int64_t)x2) >> 32) + C8_2)
* (int64_t)x2) >> 32) + C8_0);
y ^= -((phase >> 23) & 1);
return y;
}
#endif
#define C10_0 (1 << 30)
#define C10_2 -1324675874 // scaled * 4
#define C10_4 1089501821
#define C10_6 -1433689867
#define C10_8 1009356886
#define C10_10 -421101352
int32_t Sin::compute10(int32_t phase) {
int32_t x = (phase & ((1 << 29) - 1)) - (1 << 28);
int32_t x2 = ((int64_t)x * (int64_t)x) >> 26;
int32_t y = ((((((((((((((((int64_t)C10_10
* (int64_t)x2) >> 34) + C10_8)
* (int64_t)x2) >> 34) + C10_6)
* (int64_t)x2) >> 34) + C10_4)
* (int64_t)x2) >> 32) + C10_2)
* (int64_t)x2) >> 30) + C10_0);
y ^= -((phase >> 29) & 1);
return y;
}

@ -0,0 +1,62 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
class Sin {
public:
Sin();
static void init();
static int32_t lookup(int32_t phase);
static int32_t compute(int32_t phase);
// A more accurate sine, both input and output Q30
static int32_t compute10(int32_t phase);
};
#define SIN_LG_N_SAMPLES 10
#define SIN_N_SAMPLES (1 << SIN_LG_N_SAMPLES)
#define SIN_INLINE
// Use twice as much RAM for the LUT but avoid a little computation
#define SIN_DELTA
#ifdef SIN_DELTA
extern int32_t sintab[SIN_N_SAMPLES << 1];
#else
extern int32_t sintab[SIN_N_SAMPLES + 1];
#endif
#ifdef SIN_INLINE
inline
int32_t Sin::lookup(int32_t phase) {
const int32_t SHIFT = 24 - SIN_LG_N_SAMPLES;
int32_t lowbits = phase & ((1 << SHIFT) - 1);
#ifdef SIN_DELTA
int32_t phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int32_t dy = sintab[phase_int];
int32_t y0 = sintab[phase_int + 1];
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
#else
int32_t phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int32_t y0 = sintab[phase_int];
int32_t y1 = sintab[phase_int + 1];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif
}
#endif

@ -69,23 +69,23 @@ static inline uint16_t sinLog(uint16_t phi) {
EngineMkI::EngineMkI() {
float bitReso = SINLOG_TABLESIZE;
for(int i=0;i<SINLOG_TABLESIZE;i++) {
for(int32_t i=0;i<SINLOG_TABLESIZE;i++) {
float x1 = sin(((0.5+i)/bitReso) * M_PI/2.0);
sinLogTable[i] = round(-1024 * log2(x1));
}
bitReso = SINEXP_TABLESIZE;
for(int i=0;i<SINEXP_TABLESIZE;i++) {
for(int32_t i=0;i<SINEXP_TABLESIZE;i++) {
float x1 = (pow(2, float(i)/bitReso)-1) * 4096;
sinExpTable[i] = round(x1);
}
#ifdef MKIDEBUG
char buffer[4096];
int pos = 0;
uint8_t buffer[4096];
int32_t pos = 0;
TRACE("****************************************");
for(int i=0;i<SINLOG_TABLESIZE;i++) {
for(int32_t i=0;i<SINLOG_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinLogTable[i]);
if ( pos > 90 ) {
TRACE("SINLOGTABLE: %s" ,buffer);
@ -97,7 +97,7 @@ EngineMkI::EngineMkI() {
buffer[0] = 0;
pos = 0;
TRACE("----------------------------------------");
for(int i=0;i<SINEXP_TABLESIZE;i++) {
for(int32_t i=0;i<SINEXP_TABLESIZE;i++) {
pos += sprintf(buffer+pos, "%d ", sinExpTable[i]);
if ( pos > 90 ) {
TRACE("SINEXTTABLE: %s" ,buffer);
@ -146,7 +146,7 @@ void EngineMkI::compute(int32_t *output, const int32_t *input,
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = mkiSin((phase+input[i]), gain);
output[i] = y + adder[i];
@ -154,14 +154,13 @@ void EngineMkI::compute(int32_t *output, const int32_t *input,
}
}
void EngineMkI::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
void EngineMkI::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = mkiSin(phase , gain);
output[i] = y + adder[i];
@ -169,9 +168,7 @@ void EngineMkI::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
}
}
void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add) {
void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int32_t fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
@ -179,7 +176,7 @@ void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
@ -193,7 +190,7 @@ void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
}
// exclusively used for ALGO 6 with feedback
void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) {
void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift) {
int32_t dgain[2];
int32_t gain[2];
int32_t phase[2];
@ -211,7 +208,7 @@ void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01,
dgain[0] = (gain02 - gain01 + (_N_ >> 1)) >> LG_N;
dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out));
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
// op 0
@ -232,7 +229,7 @@ void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01,
}
// exclusively used for ALGO 4 with feedback
void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) {
void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift) {
int32_t dgain[3];
int32_t gain[3];
int32_t phase[3];
@ -255,7 +252,7 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01,
dgain[2] = (parms[2].gain_out - (parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out));
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
// op 0
@ -281,7 +278,7 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01,
}
void EngineMkI::render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int kLevelThresh = ENV_MAX-100;
const int32_t kLevelThresh = ENV_MAX-100;
FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
bool fb_on = feedback_shift < 16;
@ -292,12 +289,12 @@ void EngineMkI::render(int32_t *output, FmOpParams *params, int32_t algorithm, i
alg.ops[0] = 0xc4;
}
for (int op = 0; op < 6; op++) {
int flags = alg.ops[op];
for (int32_t op = 0; op < 6; op++) {
int32_t flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int inbus = (flags >> 4) & 3;
int outbus = flags & 3;
int32_t inbus = (flags >> 4) & 3;
int32_t outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain_out == 0 ? (ENV_MAX-1) : param.gain_out;
int32_t gain2 = ENV_MAX-(param.level_in >> (28-ENV_BITDEPTH));
@ -315,20 +312,20 @@ void EngineMkI::render(int32_t *output, FmOpParams *params, int32_t algorithm, i
switch ( algorithm ) {
// three operator feedback, process exception for ALGO 4
case 3 :
compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), (int32_t)16));
compute_fb3(outptr, params, gain1, gain2, fb_buf, std::min((feedback_shift+2), (int32_t)16));
params[1].phase += params[1].freq << LG_N; // hack, we already processed op-5 - op-4
params[2].phase += params[2].freq << LG_N; // yuk yuk
op += 2; // ignore the 2 other operators
break;
// two operator feedback, process exception for ALGO 6
case 5 :
compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), (int32_t)16));
compute_fb2(outptr, params, gain1, gain2, fb_buf, std::min((feedback_shift+2), (int32_t)16));
params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5
op++; // ignore next operator;
break;
case 31 :
// one operator feedback, process exception for ALGO 32
compute_fb(outptr, param.phase, param.freq, gain1, gain2, fb_buf, min((feedback_shift+2), (int32_t)16), add);
compute_fb(outptr, param.phase, param.freq, gain1, gain2, fb_buf, std::min((feedback_shift+2), (int32_t)16), add);
break;
default:
// one operator feedback, normal process

@ -27,17 +27,17 @@ class EngineMkI : public FmCore {
public:
EngineMkI();
~EngineMkI() {};
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift);
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) override;
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int fb_gain, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int32_t fb_gain, bool add);
void compute_fb2(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift);
void compute_fb2(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift);
void compute_fb3(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift);
void compute_fb3(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int32_t fb_shift);
};

@ -22,15 +22,15 @@
#include "EngineMsfa.h"
void EngineMsfa::render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int kLevelThresh = 1120;
const int32_t kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (int op = 0; op < 6; op++) {
int flags = alg.ops[op];
for (uint8_t op = 0; op < 6; op++) {
int32_t flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int inbus = (flags >> 4) & 3;
int outbus = flags & 3;
int32_t inbus = (flags >> 4) & 3;
int32_t outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain_out;
int32_t gain2 = Exp2::lookup(param.level_in - (14 * (1 << 24)));

@ -25,5 +25,5 @@ class EngineMsfa : public FmCore {
public:
EngineMsfa() {};
~EngineMsfa() {};
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_gain);
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_gain) override;
};

@ -118,7 +118,7 @@ void EngineOpl::compute(int32_t *output, const int32_t *input, int32_t phase0, i
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = oplSin((phase+input[i]) >> 14, gain);
output[i] = (y << 14) + adder[i];
@ -132,7 +132,7 @@ void EngineOpl::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int3
int32_t phase = phase0;
const int32_t *adder = add ? output : zeros;
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t y = oplSin(phase >> 14, gain);
output[i] = (y << 14) + adder[i];
@ -142,7 +142,7 @@ void EngineOpl::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int3
void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add) {
int32_t *fb_buf, int32_t fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
@ -150,7 +150,7 @@ void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
for (int i = 0; i < _N_; i++) {
for (uint8_t i = 0; i < _N_; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
@ -165,15 +165,15 @@ void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
void EngineOpl::render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) {
const int kLevelThresh = 507; // really ????
const int32_t kLevelThresh = 507; // really ????
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (int op = 0; op < 6; op++) {
int flags = alg.ops[op];
for (uint8_t op = 0; op < 6; op++) {
int32_t flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int inbus = (flags >> 4) & 3;
int outbus = flags & 3;
int32_t inbus = (flags >> 4) & 3;
int32_t outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain_out == 0 ? 511 : param.gain_out;
int32_t gain2 = 512-(param.level_in >> 19);
@ -207,15 +207,3 @@ void EngineOpl::render(int32_t *output, FmOpParams *params, int32_t algorithm, i
param.phase += param.freq << LG_N;
}
}

@ -30,10 +30,10 @@ class EngineOpl : public FmCore {
public:
EngineOpl() {};
~EngineOpl() {};
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift);
void render(int32_t *output, FmOpParams *params, int32_t algorithm, int32_t *fb_buf, int32_t feedback_shift) override;
void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int fb_gain, bool add);
void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, int32_t *fb_buf, int32_t fb_gain, bool add);
};
#endif // ENGINEOPL_H_INCLUDED

@ -58,7 +58,7 @@ PluginFx::PluginFx() {
Gain = 1.0;
}
void PluginFx::init(int sr) {
void PluginFx::init(uint16_t sr) {
mm = 0;
s1 = s2 = s3 = s4 = c = d = 0;
R24 = 0;
@ -104,7 +104,7 @@ inline float PluginFx::NR(float sample, float g) {
return y;
}
void PluginFx::process(float *work, int sampleSize) {
void PluginFx::process(float *work, uint16_t sampleSize) {
// very basic DC filter
float t_fd = work[0];
work[0] = work[0] - dc_id + dc_r * dc_od;
@ -120,12 +120,12 @@ void PluginFx::process(float *work, int sampleSize) {
// Gain
if (Gain == 0.0)
{
for (int i = 0; i < sampleSize; i++ )
for (uint16_t i = 0; i < sampleSize; i++ )
work[i] = 0.0;
}
else if ( Gain != 1.0)
{
for (int i = 0; i < sampleSize; i++ )
for (uint16_t i = 0; i < sampleSize; i++ )
work[i] *= Gain;
}
@ -152,7 +152,7 @@ void PluginFx::process(float *work, int sampleSize) {
float g = rCutoff;
float lpc = g / (1 + g);
for (int i = 0; i < sampleSize; i++ ) {
for (uint16_t i = 0; i < sampleSize; i++ ) {
float s = work[i];
s = s - 0.45 * tptlpupw(c, s, 15, sampleRateInv);
s = tptpc(d, s, bright);

@ -18,8 +18,9 @@
*/
#ifndef PLUGINFX_H_INCLUDED
#define PLUGINFX_H_INCLUDED
#pragma once
#include "stdint.h"
class PluginFx {
float s1, s2, s3, s4;
@ -33,7 +34,7 @@ class PluginFx {
// 24 db multimode
float mm;
float mmt;
int mmch;
int32_t mmch;
inline float NR24(float sample, float g, float lpc);
// preprocess values taken the UI
@ -51,7 +52,7 @@ class PluginFx {
inline float NR(float sample, float g);
bool bandPassSw;
float rcor, rcorInv;
int R;
int32_t R;
float dc_id;
float dc_od;
@ -65,9 +66,7 @@ class PluginFx {
float Reso;
float Gain;
void init(int sampleRate);
void process(float *work, int sampleSize);
void init(uint16_t sampleRate);
void process(float *work, uint16_t sampleSize);
float getGain(void);
};
#endif

@ -21,7 +21,7 @@
#include "synth.h"
/*
static const float32_t zeroblock_f32[] = {
static const float zeroblock_f32[] = {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#if AUDIO_BLOCK_SAMPLES > 16
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
@ -55,13 +55,13 @@ class Compressor
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
public:
//constructor
Compressor(const float32_t sample_rate_Hz) {
Compressor(const float sample_rate_Hz) {
//setDefaultValues(AUDIO_SAMPLE_RATE); resetStates();
setDefaultValues(sample_rate_Hz);
resetStates();
};
void setDefaultValues(const float32_t sample_rate_Hz) {
void setDefaultValues(const float 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
@ -71,7 +71,7 @@ class Compressor
//here's the method that does all the work
void doCompression(float32_t *audio_block, uint16_t len) {
void doCompression(float *audio_block, uint16_t len) {
//Serial.println("AudioEffectGain_F32: updating."); //for debugging.
if (!audio_block) {
printf("No audio_block available for Compressor!\n");
@ -87,8 +87,7 @@ class Compressor
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);
float32_t* audio_level_dB_block = new float32_t[len];
float* audio_level_dB_block = new float[len];
if(!audio_level_dB_block)
{
printf("Cannot allocate memory for \"audio_level_dB_block\" - stopping\n");
@ -101,8 +100,7 @@ class Compressor
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);
float32_t* gain_block=new float32_t[len];
float* gain_block=new float[len];
if(!gain_block)
{
printf("Cannot allocate memory for \"gain_block\" - stopping\n");
@ -121,19 +119,18 @@ class Compressor
//release memory
if(audio_level_dB_block)
delete(audio_level_dB_block);
delete audio_level_dB_block;
if(gain_block)
delete(gain_block);
delete 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 calcAudioLevel_dB(float32_t *wav_block, float32_t *level_dB_block, uint16_t len) {
void calcAudioLevel_dB(float *wav_block, float *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);
float32_t* wav_pow_block=new float32_t[len];
float* wav_pow_block=new float[len];
if(!wav_pow_block)
{
printf("Cannot allocate memory for \"wav_pow_block\" - stopping\n");
@ -145,7 +142,7 @@ class Compressor
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
float 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];
@ -165,18 +162,17 @@ class Compressor
//release memory and return
if(wav_pow_block)
delete(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 calcGain(float32_t *audio_level_dB_block, float32_t *gain_block,uint16_t len) {
void calcGain(float *audio_level_dB_block, float *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);
float32_t* inst_targ_gain_dB_block=new float32_t[len];
float* inst_targ_gain_dB_block=new float[len];
if(!inst_targ_gain_dB_block)
{
printf("Cannot allocate memory for \"inst_targ_gain_dB_block\" - stopping\n");
@ -187,8 +183,7 @@ class Compressor
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);
float32_t *gain_dB_block = new float32_t[len];
float *gain_dB_block = new float[len];
if(!gain_dB_block)
{
printf("Cannot allocate memory for \"gain_dB_block\" - stopping\n");
@ -205,20 +200,19 @@ class Compressor
//release memory and return
if(inst_targ_gain_dB_block)
delete(inst_targ_gain_dB_block);
delete inst_targ_gain_dB_block;
if(gain_dB_block)
delete(gain_dB_block);
delete gain_dB_block;
return; //output is passed through gain_block
}
//Compute the instantaneous desired gain, including the compression ratio and
//threshold for where the comrpession kicks in
void calcInstantaneousTargetGain(float32_t *audio_level_dB_block, float32_t *inst_targ_gain_dB_block, uint16_t len) {
void calcInstantaneousTargetGain(float *audio_level_dB_block, float *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);
float32_t* above_thresh_dB_block=new float32_t[len];
float* above_thresh_dB_block=new float[len];
if(!above_thresh_dB_block)
{
printf("Cannot allocate memory for \"above_thresh_dB_block\" - stopping\n");
@ -251,17 +245,17 @@ class Compressor
// release memory before returning
if(above_thresh_dB_block)
delete(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 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;
void calcSmoothedGain_dB(float *inst_targ_gain_dB_block, float *gain_dB_block, uint16_t len) {
float gain_dB;
float one_minus_attack_const = 1.0f - attack_const;
float one_minus_release_const = 1.0f - release_const;
for (uint16_t i = 0; i < len; i++) {
gain_dB = inst_targ_gain_dB_block[i];
@ -288,48 +282,48 @@ class Compressor
//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 setPreGain(float32_t g) { pre_gain = g; }
void setPreGain_dB(float32_t gain_dB) { setPreGain(pow(10.0, gain_dB / 20.0)); }
void setCompressionRatio(float32_t cr) {
comp_ratio = max(0.001f, cr); //limit to positive values
void setPreGain(float g) { pre_gain = g; }
void setPreGain_dB(float gain_dB) { setPreGain(pow(10.0, gain_dB / 20.0)); }
void setCompressionRatio(float cr) {
comp_ratio = std::max(0.001f, cr); //limit to positive values
updateThresholdAndCompRatioConstants();
}
void setAttack_sec(float32_t a, float32_t fs_Hz) {
void setAttack_sec(float a, float 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
setLevelTimeConst_sec(std::min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants
}
void setRelease_sec(float32_t r, float32_t fs_Hz) {
void setRelease_sec(float r, float 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
setLevelTimeConst_sec(std::min(attack_sec,release_sec) / 5.0, fs_Hz); //make the level time-constant one-fifth the gain time constants
}
void 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);
void setLevelTimeConst_sec(float t_sec, float fs_Hz) {
const float min_t_sec = 0.002f; //this is the minimum allowed value
level_lp_sec = std::max(min_t_sec,t_sec);
level_lp_const = expf(-1.0f / (level_lp_sec * fs_Hz)); //expf() is much faster than exp()
}
void setThresh_dBFS(float32_t val) {
void setThresh_dBFS(float val) {
thresh_dBFS = val;
setThreshPow(pow(10.0, thresh_dBFS / 10.0));
}
void enableHPFilter(boolean flag) { use_HP_prefilter = flag; };
//methods to return information about this module
float32_t getPreGain_dB(void) { return 20.0 * log10f_approx(pre_gain); }
float32_t getAttack_sec(void) { return attack_sec; }
float32_t getRelease_sec(void) { return release_sec; }
float32_t getLevelTimeConst_sec(void) { return level_lp_sec; }
float32_t getThresh_dBFS(void) { return thresh_dBFS; }
float32_t getCompressionRatio(void) { return comp_ratio; }
float32_t getCurrentLevel_dBFS(void) { return 10.0* log10f_approx(prev_level_lp_pow); }
float32_t getCurrentGain_dB(void) { return prev_gain_dB; }
void setHPFilterCoeff_N2IIR_Matlab(float32_t b[], float32_t a[]){
float getPreGain_dB(void) { return 20.0 * log10f_approx(pre_gain); }
float getAttack_sec(void) { return attack_sec; }
float getRelease_sec(void) { return release_sec; }
float getLevelTimeConst_sec(void) { return level_lp_sec; }
float getThresh_dBFS(void) { return thresh_dBFS; }
float getCompressionRatio(void) { return comp_ratio; }
float getCurrentLevel_dBFS(void) { return 10.0* log10f_approx(prev_level_lp_pow); }
float getCurrentGain_dB(void) { return prev_gain_dB; }
void setHPFilterCoeff_N2IIR_Matlab(float b[], float 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
@ -338,20 +332,20 @@ class Compressor
private:
//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
float *inputQueueArray_f32[1]; //memory pointer for the input to this module
float prev_level_lp_pow = 1.0;
float 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];
float hp_coeff[5 * hp_nstages] = {1.0, 0.0, 0.0, 0.0, 0.0}; //no filtering. actual filter coeff set later
float hp_state[4 * hp_nstages];
void 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
float b[] = {9.979871156751189e-01, -1.995974231350238e+00, 9.979871156751189e-01}; //from Matlab
float 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
@ -359,34 +353,34 @@ class Compressor
//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()
float attack_const, release_const, level_lp_const; //used in calcGain(). set by setAttack_sec() and setRelease_sec();
float comp_ratio_const, thresh_pow_FS_wCR; //used in calcGain(); set in updateThresholdAndCompRatioConstants()
void updateThresholdAndCompRatioConstants(void) {
comp_ratio_const = 1.0f-(1.0f / comp_ratio);
thresh_pow_FS_wCR = powf(thresh_pow_FS, comp_ratio_const);
}
//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) {
float attack_sec, release_sec, level_lp_sec;
float thresh_dBFS = 0.0; //threshold for compression, relative to digital full scale
float thresh_pow_FS = 1.0f; //same as above, but not in dB
void setThreshPow(float t_pow) {
thresh_pow_FS = t_pow;
updateThresholdAndCompRatioConstants();
}
float32_t comp_ratio = 1.0; //compression ratio
float32_t pre_gain = -1.0; //gain to apply before the compression. negative value disables
float comp_ratio = 1.0; //compression ratio
float 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) {
static float pow10f(float 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) {
static float log10f_approx(float x) {
//return log10f(x); //standard, but slower
return log2f_approx(x)*0.3010299956639812f; //faster: log2(x)/log2(10)
}
@ -400,12 +394,12 @@ class Compressor
** 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;
//float log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f};
static float log2f_approx(float X) {
//float *C = &log2f_approx_coeff[0];
float Y;
float F;
int32_t E;
// This is the approximation to log2()
F = frexpf(fabsf(X), &E);

@ -25,10 +25,10 @@
#include "fm_core.h"
// State of MIDI controllers
const int kControllerPitch = 0;
const int kControllerPitchRange = 1;
const int kControllerPitchStep = 2;
const int kControllerPortamentoGlissando = 3;
const uint8_t kControllerPitch = 0;
const uint8_t kControllerPitchRange = 1;
const uint8_t kControllerPitchStep = 2;
const uint8_t kControllerPortamentoGlissando = 3;
class FmMod {
public:
@ -96,13 +96,13 @@ class Controllers {
}
if (mod.amp)
amp_mod = max(amp_mod, total);
amp_mod = std::max(amp_mod, total);
if (mod.pitch)
pitch_mod = max(pitch_mod, total);
pitch_mod = std::max(pitch_mod, total);
if (mod.eg)
eg_mod = max(eg_mod, total);
eg_mod = std::max(eg_mod, total);
}
public:
@ -117,9 +117,9 @@ class Controllers {
uint8_t foot_cc;
uint8_t modwheel_cc;
bool portamento_enable_cc;
int portamento_cc;
int32_t portamento_cc;
bool portamento_gliss_cc;
int masterTune;
int32_t masterTune;
uint8_t opSwitch;

@ -38,7 +38,7 @@
#include "porta.h"
#include "compressor.h"
Dexed::Dexed(uint8_t maxnotes, int rate)
Dexed::Dexed(uint8_t maxnotes, uint16_t rate)
{
samplerate = float32_t(rate);
@ -53,7 +53,6 @@ Dexed::Dexed(uint8_t maxnotes, int rate)
Porta::init_sr(rate);
fx.init(rate);
max_notes = maxnotes;
currentNote = 0;
resetControllers();
controllers.masterTune = 0;
@ -64,7 +63,23 @@ Dexed::Dexed(uint8_t maxnotes, int rate)
sustain = false;
voices = NULL;
setMaxNotes(max_notes);
max_notes=maxnotes;
if (max_notes > 0)
{
voices = new ProcessorVoice[max_notes]; // sizeof(ProcessorVoice) = 20
for (uint8_t i = 0; i < max_notes; i++)
{
voices[i].dx7_note = new Dx7Note; // sizeof(Dx7Note) = 692
voices[i].keydown = false;
voices[i].sustained = false;
voices[i].live = false;
voices[i].key_pressed_timer = 0;
}
}
else
voices = NULL;
used_notes=max_notes;
setMonoMode(false);
loadInitVoice();
@ -91,9 +106,7 @@ Dexed::~Dexed()
for (uint8_t note = 0; note < max_notes; note++)
delete voices[note].dx7_note;
for (uint8_t note = 0; note < max_notes; note++)
delete &voices[note];
delete[] voices;
}
void Dexed::setEngineType(uint8_t engine)
@ -103,19 +116,19 @@ void Dexed::setEngineType(uint8_t engine)
switch(engine)
{
case MSFA:
controllers.core = (EngineMkI*)engineMsfa;
controllers.core = (FmCore*)engineMsfa;
engineType=MSFA;
break;
case MKI:
controllers.core = (EngineMkI*)engineMkI;
controllers.core = (FmCore*)engineMkI;
engineType=MKI;
break;
case OPL:
controllers.core = (EngineMkI*)engineOpl;
controllers.core = (FmCore*)engineOpl;
engineType=OPL;
break;
default:
controllers.core = (EngineMkI*)engineMsfa;
controllers.core = (FmCore*)engineMsfa;
engineType=MSFA;
break;
}
@ -135,44 +148,8 @@ FmCore* Dexed::getEngineAddress(void)
void Dexed::setMaxNotes(uint8_t new_max_notes)
{
uint8_t i = 0;
max_notes = constrain(max_notes, 0, _MAX_NOTES);
#if defined(MICRODEXED_VERSION) && defined(DEBUG)
Serial.print("Allocating memory for ");
Serial.print(max_notes, DEC);
Serial.println(" notes.");
Serial.println();
#endif
if (voices)
{
panic();
for (i = 0; i < max_notes; i++)
{
if (voices[i].dx7_note)
delete voices[i].dx7_note;
}
delete(voices);
}
max_notes = constrain(new_max_notes, 0, _MAX_NOTES);
if (max_notes > 0)
{
voices = new ProcessorVoice[max_notes]; // sizeof(ProcessorVoice) = 20
for (i = 0; i < max_notes; i++)
{
voices[i].dx7_note = new Dx7Note; // sizeof(Dx7Note) = 692
voices[i].keydown = false;
voices[i].sustained = false;
voices[i].live = false;
voices[i].key_pressed_timer = 0;
}
}
else
voices = NULL;
used_notes = constrain(new_max_notes, 0, max_notes);
}
void Dexed::activate(void)
@ -186,11 +163,11 @@ void Dexed::deactivate(void)
panic();
}
void Dexed::getSamples(float32_t* buffer, uint16_t n_samples)
void Dexed::getSamples(float* buffer, uint16_t n_samples)
{
if (refreshVoice)
{
for (uint8_t i = 0; i < max_notes; i++)
for (uint8_t i = 0; i < used_notes; i++)
{
if ( voices[i].live )
voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity, voices[i].porta, &controllers);
@ -213,7 +190,7 @@ void Dexed::getSamples(float32_t* buffer, uint16_t n_samples)
int32_t lfovalue = lfo.getsample();
int32_t lfodelay = lfo.getdelay();
for (uint8_t note = 0; note < max_notes; note++)
for (uint8_t note = 0; note < used_notes; note++)
{
if (voices[note].live)
{
@ -238,13 +215,13 @@ void Dexed::getSamples(float32_t* buffer, uint16_t n_samples)
void Dexed::getSamples(int16_t* buffer, uint16_t n_samples)
{
float32_t tmp[n_samples];
float tmp[n_samples];
getSamples(tmp, n_samples);
arm_float_to_q15(tmp, (q15_t*)buffer, n_samples);
}
void Dexed::keydown(int16_t pitch, uint8_t velo) {
void Dexed::keydown(uint8_t pitch, uint8_t velo) {
if ( velo == 0 ) {
keyup(pitch);
return;
@ -254,10 +231,10 @@ void Dexed::keydown(int16_t pitch, uint8_t velo) {
pitch += data[144] - TRANSPOSE_FIX;
int previousKeyDown = lastKeyDown;
int32_t previousKeyDown = lastKeyDown;
lastKeyDown = pitch;
int porta = -1;
int32_t porta = -1;
if ( controllers.portamento_enable_cc && previousKeyDown >= 0 )
porta = controllers.portamento_cc;
@ -266,7 +243,7 @@ void Dexed::keydown(int16_t pitch, uint8_t velo) {
if (!monoMode && noteRefreshMode)
{
for (uint8_t i = 0; i < max_notes; i++)
for (uint8_t i = 0; i < used_notes; i++)
{
if (voices[i].midi_note == pitch && voices[i].keydown == false && voices[i].live && voices[i].sustained == true)
{
@ -284,17 +261,17 @@ void Dexed::keydown(int16_t pitch, uint8_t velo) {
}
}
for (uint8_t i = 0; i <= max_notes; i++)
for (uint8_t i = 0; i <= used_notes; i++)
{
if (i == max_notes)
if (i == used_notes)
{
uint32_t min_timer = 0xffff;
uint32_t min_timer = 0xffffffff;
if (monoMode)
break;
// no free sound slot found, so use the oldest note slot
for (uint8_t n = 0; n < max_notes; n++)
for (uint8_t n = 0; n < used_notes; n++)
{
if (voices[n].key_pressed_timer < min_timer)
{
@ -311,14 +288,14 @@ void Dexed::keydown(int16_t pitch, uint8_t velo) {
if (!voices[note].keydown)
{
currentNote = (note + 1) % max_notes;
currentNote = (note + 1) % used_notes;
//if (keydown_counter == 0) // Original comment: TODO: should only do this if # keys down was 0
lfo.keydown();
voices[note].midi_note = pitch;
voices[note].velocity = velo;
voices[note].sustained = sustain;
voices[note].keydown = true;
int srcnote = (previousKeyDown >= 0) ? previousKeyDown : pitch;
int32_t srcnote = (previousKeyDown >= 0) ? previousKeyDown : pitch;
voices[note].dx7_note->init(data, pitch, velo, srcnote, porta, &controllers);
if ( data[136] )
voices[note].dx7_note->oscSync();
@ -330,11 +307,11 @@ void Dexed::keydown(int16_t pitch, uint8_t velo) {
{
keydown_counter++;
}
note = (note + 1) % max_notes;
note = (note + 1) % used_notes;
}
if ( monoMode ) {
for (uint8_t i = 0; i < max_notes; i++) {
for (uint8_t i = 0; i < used_notes; i++) {
if ( voices[i].live ) {
// all keys are up, only transfer signal
if ( ! voices[i].keydown ) {
@ -355,14 +332,14 @@ void Dexed::keydown(int16_t pitch, uint8_t velo) {
voices[note].live = true;
}
void Dexed::keyup(int16_t pitch) {
void Dexed::keyup(uint8_t pitch) {
uint8_t note;
pitch = constrain(pitch, 0, 127);
pitch += data[144] - TRANSPOSE_FIX;
for (note = 0; note < max_notes; note++) {
for (note = 0; note < used_notes; note++) {
if ( voices[note].midi_note == pitch && voices[note].keydown ) {
voices[note].keydown = false;
voices[note].key_pressed_timer = 0;
@ -372,14 +349,14 @@ void Dexed::keyup(int16_t pitch) {
}
// note not found ?
if ( note >= max_notes ) {
if ( note >= used_notes ) {
return;
}
if ( monoMode ) {
int16_t highNote = -1;
int8_t highNote = -1;
uint8_t target = 0;
for (int8_t i = 0; i < max_notes; i++) {
for (int8_t i = 0; i < used_notes; i++) {
if ( voices[i].keydown && voices[i].midi_note > highNote ) {
target = i;
highNote = voices[i].midi_note;
@ -496,7 +473,7 @@ void Dexed::notesOff(void) {
uint8_t Dexed::getMaxNotes(void)
{
return max_notes;
return used_notes;
}
uint8_t Dexed::getNumNotesPlaying(void)
@ -505,7 +482,7 @@ uint8_t Dexed::getNumNotesPlaying(void)
uint8_t i;
uint8_t count_playing_voices = 0;
for (i = 0; i < max_notes; i++)
for (i = 0; i < used_notes; i++)
{
if (voices[i].live == true)
{
@ -613,7 +590,7 @@ bool Dexed::decodeVoice(uint8_t* new_data, uint8_t* encoded_data)
panic();
doRefreshVoice();
strncpy(dexed_voice_name, (char *)&encoded_data[118], sizeof(dexed_voice_name) - 1);
strlcpy(dexed_voice_name, (char *)&encoded_data[118], sizeof(dexed_voice_name) - 1);
dexed_voice_name[10] = '\0';
#if defined(MICRODEXED_VERSION) && defined(DEBUG)
Serial.print(F("Voice ["));
@ -703,7 +680,7 @@ void Dexed::loadVoiceParameters(uint8_t* new_data)
memcpy(&data, new_data, 155);
doRefreshVoice();
#if defined(MICRODEXED_VERSION) && defined(DEBUG)
strncpy(dexed_voice_name, (char *)&new_data[145], sizeof(dexed_voice_name) - 1);
strlcpy(dexed_voice_name, (char *)&new_data[145], sizeof(dexed_voice_name) - 1);
dexed_voice_name[10] = '\0';
Serial.print(F("Voice ["));
@ -1707,12 +1684,12 @@ uint8_t Dexed::getTranspose(void)
void Dexed::setName(char* name)
{
strncpy(name, (char*)&data[DEXED_VOICE_OFFSET + DEXED_NAME], 10);
strlcpy(name, (char*)&data[DEXED_VOICE_OFFSET + DEXED_NAME], 10);
}
void Dexed::getName(char* buffer)
{
strncpy((char*)&data[DEXED_VOICE_OFFSET + DEXED_NAME], buffer, 10);
strlcpy((char*)&data[DEXED_VOICE_OFFSET + DEXED_NAME], buffer, 10);
buffer[10] = '\0';
}
@ -1764,52 +1741,52 @@ bool Dexed::getCompressor(void)
return (use_compressor);
}
void Dexed::setCompressorPreGain_dB(float32_t pre_gain)
void Dexed::setCompressorPreGain_dB(float pre_gain)
{
compressor->setPreGain_dB(pre_gain);
}
void Dexed::setCompressorAttack_sec(float32_t attack_sec)
void Dexed::setCompressorAttack_sec(float attack_sec)
{
compressor->setAttack_sec(attack_sec, samplerate);
}
void Dexed::setCompressorRelease_sec(float32_t release_sec)
void Dexed::setCompressorRelease_sec(float release_sec)
{
compressor->setRelease_sec(release_sec, samplerate);
}
void Dexed::setCompressorThresh_dBFS(float32_t thresh_dBFS)
void Dexed::setCompressorThresh_dBFS(float thresh_dBFS)
{
compressor->setThresh_dBFS(thresh_dBFS);
}
void Dexed::setCompressionRatio(float32_t comp_ratio)
void Dexed::setCompressionRatio(float comp_ratio)
{
compressor->setCompressionRatio(comp_ratio);
}
float32_t Dexed::getCompressorPreGain_dB(void)
float Dexed::getCompressorPreGain_dB(void)
{
return (compressor->getPreGain_dB());
}
float32_t Dexed::getCompressorAttack_sec(void)
float Dexed::getCompressorAttack_sec(void)
{
return (compressor->getAttack_sec());
}
float32_t Dexed::getCompressorRelease_sec(void)
float Dexed::getCompressorRelease_sec(void)
{
return (compressor->getRelease_sec());
}
float32_t Dexed::getCompressorThresh_dBFS(void)
float Dexed::getCompressorThresh_dBFS(void)
{
return (compressor->getThresh_dBFS());
}
float32_t Dexed::getCompressionRatio(void)
float Dexed::getCompressionRatio(void)
{
return (compressor->getCompressionRatio());
}

@ -51,7 +51,7 @@
#define NUM_VOICE_PARAMETERS 156
struct ProcessorVoice {
int16_t midi_note;
uint8_t midi_note;
uint8_t velocity;
int16_t porta;
bool keydown;
@ -165,7 +165,7 @@ enum ENGINES {
class Dexed
{
public:
Dexed(uint8_t maxnotes, int rate);
Dexed(uint8_t maxnotes, uint16_t rate);
~Dexed();
// Global methods
@ -174,7 +174,6 @@ class Dexed
bool getMonoMode(void);
void setMonoMode(bool mode);
void setNoteRefreshMode(bool mode);
void setMaxNotes(uint8_t n);
uint8_t getMaxNotes(void);
void doRefreshVoice(void);
void setOPAll(uint8_t ops);
@ -193,28 +192,29 @@ class Dexed
void setVelocityScale(uint8_t offset, uint8_t max);
void getVelocityScale(uint8_t* offset, uint8_t* max);
void setVelocityScale(uint8_t setup);
void setMaxNotes(uint8_t n);
void setEngineType(uint8_t engine);
uint8_t getEngineType(void);
FmCore* getEngineAddress(void);
#ifndef TEENSYDUINO
void setCompressor(bool comp);
bool getCompressor(void);
void setCompressorPreGain_dB(float32_t pre_gain);
void setCompressorAttack_sec(float32_t attack_sec);
void setCompressorRelease_sec(float32_t release_sec);
void setCompressorThresh_dBFS(float32_t thresh_dBFS);
void setCompressionRatio(float32_t comp_ratio);
float32_t getCompressorPreGain_dB(void);
float32_t getCompressorAttack_sec(void);
float32_t getCompressorRelease_sec(void);
float32_t getCompressorThresh_dBFS(void);
float32_t getCompressionRatio(void);
void setCompressorPreGain_dB(float pre_gain);
void setCompressorAttack_sec(float attack_sec);
void setCompressorRelease_sec(float release_sec);
void setCompressorThresh_dBFS(float thresh_dBFS);
void setCompressionRatio(float comp_ratio);
float getCompressorPreGain_dB(void);
float getCompressorAttack_sec(void);
float getCompressorRelease_sec(void);
float getCompressorThresh_dBFS(void);
float getCompressionRatio(void);
#endif
int16_t checkSystemExclusive(const uint8_t* sysex, const uint16_t len);
// Sound methods
void keyup(int16_t pitch);
void keydown(int16_t pitch, uint8_t velo);
void keyup(uint8_t pitch);
void keydown(uint8_t pitch, uint8_t velo);
void setSustain(bool sustain);
bool getSustain(void);
void panic(void);
@ -355,12 +355,13 @@ class Dexed
03, 48, // pitch_mod_sensitivity, transpose
73, 78, 73, 84, 32, 86, 79, 73, 67, 69 // 10 * char for name ("INIT VOICE")
};
float32_t samplerate;
float samplerate;
uint8_t data[NUM_VOICE_PARAMETERS];
uint8_t max_notes;
uint8_t used_notes;
PluginFx fx;
Controllers controllers;
int lastKeyDown;
int32_t lastKeyDown;
uint32_t xrun;
uint16_t render_time_max;
int16_t currentNote;
@ -375,9 +376,9 @@ class Dexed
EngineMsfa* engineMsfa;
EngineMkI* engineMkI;
EngineOpl* engineOpl;
void getSamples(float32_t* buffer, uint16_t n_samples);
void getSamples(float* buffer, uint16_t n_samples);
void getSamples(int16_t* buffer, uint16_t n_samples);
void compress(float32_t* wav_in, float32_t* wav_out, uint16_t n, float32_t threshold, float32_t slope, uint16_t sr, float32_t tla, float32_t twnd, float32_t tatt, float32_t trel);
void compress(float* wav_in, float* wav_out, uint16_t n, float threshold, float slope, uint16_t sr, float tla, float twnd, float tatt, float trel);
bool use_compressor;
uint8_t velocity_offset;
uint8_t velocity_max;

@ -87,14 +87,14 @@ const uint8_t velocity_data[64] = {
// See "velocity" section of notes. Returns velocity delta in microsteps.
int ScaleVelocity(int velocity, int sensitivity) {
int clamped_vel = max(0, min(127, velocity));
int clamped_vel = std::max(0, std::min(127, velocity));
int vel_value = velocity_data[clamped_vel >> 1] - 239;
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4;
return scaled_vel;
}
int ScaleRate(int midinote, int sensitivity) {
int x = min(31, max(0, midinote / 3 - 7));
int x = std::min(31, std::max(0, midinote / 3 - 7));
int qratedelta = (sensitivity * x) >> 3;
#ifdef SUPER_PRECISE
int rem = x & 7;
@ -120,7 +120,7 @@ int ScaleCurve(int group, int depth, int curve) {
} else {
// exponential
int n_scale_data = sizeof(exp_scale_data);
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)];
int raw_exp = exp_scale_data[std::min(group, n_scale_data - 1)];
scale = (raw_exp * depth * 329) >> 15;
}
if (curve < 2) {
@ -170,12 +170,12 @@ void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity, int src
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
outlevel = std::min(127, outlevel);
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
outlevel = std::max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].init(rates, levels, outlevel, rate_scaling);
env_[op].init((const int32_t*)rates, (const int32_t*)levels, outlevel, rate_scaling);
int mode = patch[off + 17];
int coarse = patch[off + 18];
@ -194,7 +194,7 @@ void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity, int src
rates[i] = patch[126 + i];
levels[i] = patch[130 + i];
}
pitchenv_.set(rates, levels);
pitchenv_.set((const int32_t *)rates, (const int32_t*)levels);
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16;
@ -213,7 +213,7 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co
pmod_1 = abs(pmod_1);
int32_t pmod_2 = (int32_t)(((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14);
pmod_2 = abs(pmod_2);
int32_t pitch_mod = max(pmod_1, pmod_2);
int32_t pitch_mod = std::max(pmod_1, pmod_2);
pitch_mod = pitchenv_.getsample() + (pitch_mod * (senslfo < 0 ? -1 : 1));
// ---- PITCH BEND ----
@ -236,11 +236,11 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co
uint32_t amod_1 = (uint32_t)(((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8); // Q24 :D
amod_1 = (uint32_t)(((int64_t) amod_1 * (int64_t) lfo_val) >> 24);
uint32_t amod_2 = (uint32_t)(((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7); // Q?? :|
uint32_t amd_mod = max(amod_1, amod_2);
uint32_t amd_mod = std::max(amod_1, amod_2);
// ==== EG AMP MOD ====
uint32_t amod_3 = (ctrls->eg_mod + 1) << 17;
amd_mod = max((1 << 24) - amod_3, amd_mod);
amd_mod = std::max((1 << 24) - amod_3, amd_mod);
// ==== OP RENDER ====
for (int op = 0; op < 6; op++) {
@ -330,12 +330,12 @@ void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity, int p
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
outlevel = std::min(127, outlevel);
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
outlevel = std::max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].update(rates, levels, outlevel, rate_scaling);
env_[op].update((const int32_t*)rates, (const int32_t*)levels, (int32_t)outlevel, rate_scaling);
}
algorithm_ = patch[134];
int feedback = patch[135];

@ -64,7 +64,7 @@ class Dx7Note {
FmOpParams params_[6];
PitchEnv pitchenv_;
int32_t basepitch_[6];
int32_t fb_buf_[2];
int32_t fb_buf_[2]={0 ,0};
int32_t fb_shift_;
int32_t ampmodsens_[6];
int32_t opMode[6];

@ -24,12 +24,12 @@
uint32_t Env::sr_multiplier = (1 << 24);
const int levellut[] = {
const int32_t levellut[] = {
0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46
};
#ifdef ACCURATE_ENVELOPE
const int statics[] = {
const int32_t statics[] = {
1764000, 1764000, 1411200, 1411200, 1190700, 1014300, 992250,
882000, 705600, 705600, 584325, 507150, 502740, 441000, 418950,
352800, 308700, 286650, 253575, 220500, 220500, 176400, 145530,
@ -47,7 +47,7 @@ void Env::init_sr(double sampleRate) {
sr_multiplier = (44100.0 / sampleRate) * (1 << 24);
}
void Env::init(const int r[4], const int l[4], int ol, int rate_scaling) {
void Env::init(const int32_t r[4], const int32_t l[4], int32_t ol, int32_t rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
@ -75,7 +75,7 @@ int32_t Env::getsample() {
;
}
else if (rising_) {
const int jumptarget = 1716;
const int32_t jumptarget = 1716;
if (level_ < (jumptarget << 16)) {
level_ = jumptarget << 16;
}
@ -105,15 +105,15 @@ void Env::keydown(bool d) {
}
}
int Env::scaleoutlevel(int outlevel) {
int32_t Env::scaleoutlevel(int32_t outlevel) {
return outlevel >= 20 ? 28 + outlevel : levellut[outlevel];
}
void Env::advance(int newix) {
ix_ = newix;
if (ix_ < 4) {
int newlevel = levels_[ix_];
int actuallevel = scaleoutlevel(newlevel) >> 1;
int32_t newlevel = levels_[ix_];
int32_t actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) + outlevel_ - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
// level here is same as Java impl
@ -121,18 +121,18 @@ void Env::advance(int newix) {
rising_ = (targetlevel_ > level_);
// rate
int qrate = (rates_[ix_] * 41) >> 6;
int32_t qrate = (rates_[ix_] * 41) >> 6;
qrate += rate_scaling_;
qrate = min(qrate, 63);
qrate = std::min(int(qrate), 63);
#ifdef ACCURATE_ENVELOPE
if (targetlevel_ == level_ || (ix_ == 0 && newlevel == 0)) {
// approximate number of samples at 44.100 kHz to achieve the time
// empirically gathered using 2 TF1s, could probably use some double-checking
// and cleanup, but it's pretty close for now.
int staticrate = rates_[ix_];
int32_t staticrate = rates_[ix_];
staticrate += rate_scaling_; // needs to be checked, as well, but seems correct
staticrate = min(staticrate, 99);
staticrate = std::min(int(staticrate), 99);
staticcount_ = staticrate < 77 ? statics[staticrate] : 20 * (99 - staticrate);
if (staticrate < 77 && (ix_ == 0 && newlevel == 0)) {
staticcount_ /= 20; // attack is scaled faster
@ -149,7 +149,7 @@ void Env::advance(int newix) {
}
}
void Env::update(const int r[4], const int l[4], int ol, int rate_scaling) {
void Env::update(const int32_t r[4], const int32_t l[4], int32_t ol, int32_t rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
@ -158,8 +158,8 @@ void Env::update(const int r[4], const int l[4], int ol, int rate_scaling) {
rate_scaling_ = rate_scaling;
if ( down_ ) {
// for now we simply reset ourselves at level 3
int newlevel = levels_[2];
int actuallevel = scaleoutlevel(newlevel) >> 1;
int32_t newlevel = levels_[2];
int32_t actuallevel = scaleoutlevel(newlevel) >> 1;
actuallevel = (actuallevel << 6) - 4256;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
targetlevel_ = actuallevel << 16;

@ -31,11 +31,11 @@ class Env {
// (ie, value 0..99). The outlevel parameter is calibrated in microsteps
// (ie units of approx .023 dB), with 99 * 32 = nominal full scale. The
// rate_scaling parameter is in qRate units (ie 0..63).
void init(const int rates[4], const int levels[4], int outlevel,
int rate_scaling);
void init(const int32_t rates[4], const int32_t levels[4], int32_t outlevel,
int32_t rate_scaling);
void update(const int rates[4], const int levels[4], int outlevel,
int rate_scaling);
void update(const int32_t rates[4], const int32_t levels[4], int32_t outlevel,
int32_t rate_scaling);
// Result is in Q24/doubling log format. Also, result is subsampled
// for every N samples.
// A couple more things need to happen for this to be used as a gain
@ -45,7 +45,7 @@ class Env {
int32_t getsample();
void keydown(bool down);
static int scaleoutlevel(int outlevel);
static int32_t scaleoutlevel(int32_t outlevel);
void getPosition(char *step);
static void init_sr(double sample_rate);
@ -57,20 +57,20 @@ class Env {
// if we are not using 44100.
static uint32_t sr_multiplier;
int rates_[4];
int levels_[4];
int outlevel_;
int rate_scaling_;
int32_t rates_[4];
int32_t levels_[4];
int32_t outlevel_;
int32_t rate_scaling_;
// Level is stored so that 2^24 is one doubling, ie 16 more bits than
// the DX7 itself (fraction is stored in level rather than separate
// counter)
int32_t level_;
int targetlevel_;
int32_t targetlevel_;
bool rising_;
int ix_;
int inc_;
int32_t ix_;
int32_t inc_;
#ifdef ACCURATE_ENVELOPE
int staticcount_;
int32_t staticcount_;
#endif
bool down_;

@ -34,13 +34,13 @@ extern int32_t exp2tab[EXP2_N_SAMPLES << 1];
#ifdef EXP2_INLINE
inline
int32_t Exp2::lookup(int32_t x) {
const int SHIFT = 24 - EXP2_LG_N_SAMPLES;
int lowbits = x & ((1 << SHIFT) - 1);
int x_int = (x >> (SHIFT - 1)) & ((EXP2_N_SAMPLES - 1) << 1);
int dy = exp2tab[x_int];
int y0 = exp2tab[x_int + 1];
const int32_t SHIFT = 24 - EXP2_LG_N_SAMPLES;
int32_t lowbits = x & ((1 << SHIFT) - 1);
int32_t x_int = (x >> (SHIFT - 1)) & ((EXP2_N_SAMPLES - 1) << 1);
int32_t dy = exp2tab[x_int];
int32_t y0 = exp2tab[x_int + 1];
int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
int32_t y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
return y >> (6 - (x >> 24));
}
#endif
@ -69,12 +69,12 @@ int32_t Tanh::lookup(int32_t x) {
int32_t sx = ((int64_t) - 48408812 * (int64_t)x) >> 24;
return signum ^ ((1 << 24) - 2 * Exp2::lookup(sx));
} else {
const int SHIFT = 26 - TANH_LG_N_SAMPLES;
int lowbits = x & ((1 << SHIFT) - 1);
int x_int = (x >> (SHIFT - 1)) & ((TANH_N_SAMPLES - 1) << 1);
int dy = tanhtab[x_int];
int y0 = tanhtab[x_int + 1];
int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
const int32_t SHIFT = 26 - TANH_LG_N_SAMPLES;
int32_t lowbits = x & ((1 << SHIFT) - 1);
int32_t x_int = (x >> (SHIFT - 1)) & ((TANH_N_SAMPLES - 1) << 1);
int32_t dy = tanhtab[x_int];
int32_t y0 = tanhtab[x_int + 1];
int32_t y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
return y ^ signum;
}
}

@ -57,7 +57,7 @@ const FmAlgorithm FmCore::algorithms[32] = {
};
int n_out(const FmAlgorithm &alg) {
int count = 0;
int32_t count = 0;
for (int i = 0; i < 6; i++) {
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++;
}
@ -84,7 +84,7 @@ void FmCore::dump() {
cout << (i + 1) << ":";
const FmAlgorithm &alg = algorithms[i];
for (int j = 0; j < 6; j++) {
int flags = alg.ops[j];
int32_t flags = alg.ops[j];
cout << " ";
if (flags & FB_IN) cout << "[";
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->";

@ -25,8 +25,8 @@
class FmOperatorInfo {
public:
int in;
int out;
int32_t in;
int32_t out;
};
enum FmOperatorFlags {
@ -41,7 +41,7 @@ enum FmOperatorFlags {
class FmAlgorithm {
public:
int ops[6];
int32_t ops[6];
};
class FmCore {

@ -29,7 +29,7 @@ void neon_fm_kernel(const int32_t *in, const int32_t *busin, int32_t *out, int32
const int32_t __attribute__ ((aligned(16))) const_0_1_2_3_arg[4] = {0, 1, 2, 3};
const int32_t __attribute__ ((aligned(16))) mask23_arg = 0x7fffff;
const float32_t __attribute__ ((aligned(16))) coeffs_arg[4] = {
const float __attribute__ ((aligned(16))) coeffs_arg[4] = {
-0.01880853017455781, 0.25215252666796095, -1.2333439964934032, 1.0
};
const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0};
@ -214,7 +214,7 @@ void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add) {
int32_t *fb_buf, int32_t fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;

@ -41,7 +41,7 @@ class FmOpKernel {
// One op with feedback, no add.
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_gain, bool add);
int32_t *fb_buf, int32_t fb_gain, bool add);
};
#endif

@ -45,12 +45,12 @@ void Freqlut::init(FRAC_NUM sample_rate) {
// Note: if logfreq is more than 20.0, the results will be inaccurate. However,
// that will be many times the Nyquist rate.
int32_t Freqlut::lookup(int32_t logfreq) {
int ix = (logfreq & 0xffffff) >> SAMPLE_SHIFT;
int32_t ix = (logfreq & 0xffffff) >> SAMPLE_SHIFT;
int32_t y0 = lut[ix];
int32_t y1 = lut[ix + 1];
int lowbits = logfreq & ((1 << SAMPLE_SHIFT) - 1);
int32_t lowbits = logfreq & ((1 << SAMPLE_SHIFT) - 1);
int32_t y = y0 + ((((int64_t)(y1 - y0) * (int64_t)lowbits)) >> SAMPLE_SHIFT);
int hibits = logfreq >> 24;
int32_t hibits = logfreq >> 24;
return y >> (MAX_LOGFREQ_INT - hibits);
}

@ -16,6 +16,8 @@
// Low frequency oscillator, compatible with DX7
#include <algorithm>
#include "synth.h"
#include "sin.h"
@ -29,11 +31,11 @@ void Lfo::init(FRAC_NUM sample_rate) {
}
void Lfo::reset(const uint8_t params[6]) {
int rate = params[0]; // 0..99
int sr = rate == 0 ? 1 : (165 * rate) >> 6;
int32_t rate = params[0]; // 0..99
int32_t sr = rate == 0 ? 1 : (165 * rate) >> 6;
sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4));
delta_ = unit_ * sr;
int a = 99 - params[1]; // LFO delay
int32_t a = 99 - params[1]; // LFO delay
if (a == 99) {
delayinc_ = ~0u;
delayinc2_ = ~0u;
@ -41,7 +43,7 @@ void Lfo::reset(const uint8_t params[6]) {
a = (16 + (a & 15)) << (1 + (a >> 4));
delayinc_ = unit_ * a;
a &= 0xff80;
a = max(0x80, a);
a = std::max(0x80, int(a));
delayinc2_ = unit_ * a;
}
waveform_ = params[5];

@ -17,7 +17,7 @@
#include "synth.h"
#include "pitchenv.h"
int PitchEnv::unit_;
int32_t PitchEnv::unit_;
void PitchEnv::init(FRAC_NUM sample_rate) {
unit_ = _N_ * (1 << 24) / (21.3 * sample_rate) + 0.5;
@ -42,7 +42,7 @@ const int8_t pitchenv_tab[] = {
82, 92, 103, 115, 127
};
void PitchEnv::set(const int r[4], const int l[4]) {
void PitchEnv::set(const int32_t r[4], const int32_t l[4]) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
@ -81,7 +81,7 @@ void PitchEnv::keydown(bool d) {
void PitchEnv::advance(int newix) {
ix_ = newix;
if (ix_ < 4) {
int newlevel = levels_[ix_];
int32_t newlevel = levels_[ix_];
targetlevel_ = pitchenv_tab[newlevel] << 19;
rising_ = (targetlevel_ > level_);
inc_ = pitchenv_rate[rates_[ix_]] * unit_;

@ -25,21 +25,21 @@ class PitchEnv {
// The rates and levels arrays are calibrated to match the Dx7 parameters
// (ie, value 0..99).
void set(const int rates[4], const int levels[4]);
void set(const int32_t rates[4], const int32_t levels[4]);
// Result is in Q24/octave
int32_t getsample();
void keydown(bool down);
void getPosition(char *step);
private:
static int unit_;
int rates_[4];
int levels_[4];
static int32_t unit_;
int32_t rates_[4];
int32_t levels_[4];
int32_t level_;
int targetlevel_;
int32_t targetlevel_;
bool rising_;
int ix_;
int inc_;
int32_t ix_;
int32_t inc_;
bool down_;

@ -22,12 +22,12 @@ void Porta::init_sr(double sampleRate)
{
// compute portamento for CC 7-bit range
for (unsigned int i = 0; i < 128; ++i) {
for (uint8_t i = 0; i < 128; ++i) {
// number of semitones travelled
double sps = 350.0 * pow(2.0, -0.062 * i); // per second
double spf = sps / sampleRate; // per frame
double spp = spf * _N_; // per period
const int step = (1 << 24) / 12;
const int32_t step = (1 << 24) / 12;
rates[i] = (int32_t)(0.5f + step * spp); // to pitch units
}
}

@ -60,18 +60,18 @@ void Sin::init() {
#ifndef SIN_INLINE
int32_t Sin::lookup(int32_t phase) {
const int SHIFT = 24 - SIN_LG_N_SAMPLES;
int lowbits = phase & ((1 << SHIFT) - 1);
const int32_t SHIFT = 24 - SIN_LG_N_SAMPLES;
int32_t lowbits = phase & ((1 << SHIFT) - 1);
#ifdef SIN_DELTA
int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int dy = sintab[phase_int];
int y0 = sintab[phase_int + 1];
int32_t phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int32_t dy = sintab[phase_int];
int32_t y0 = sintab[phase_int + 1];
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
#else
int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int y0 = sintab[phase_int];
int y1 = sintab[phase_int + 1];
int32_t phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int32_t y0 = sintab[phase_int];
int32_t y1 = sintab[phase_int + 1];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif

@ -43,18 +43,18 @@ extern int32_t sintab[SIN_N_SAMPLES + 1];
#ifdef SIN_INLINE
inline
int32_t Sin::lookup(int32_t phase) {
const int SHIFT = 24 - SIN_LG_N_SAMPLES;
int lowbits = phase & ((1 << SHIFT) - 1);
const int32_t SHIFT = 24 - SIN_LG_N_SAMPLES;
int32_t lowbits = phase & ((1 << SHIFT) - 1);
#ifdef SIN_DELTA
int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int dy = sintab[phase_int];
int y0 = sintab[phase_int + 1];
int32_t phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int32_t dy = sintab[phase_int];
int32_t y0 = sintab[phase_int + 1];
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
#else
int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int y0 = sintab[phase_int];
int y1 = sintab[phase_int + 1];
int32_t phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int32_t y0 = sintab[phase_int];
int32_t y1 = sintab[phase_int + 1];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif

@ -25,12 +25,10 @@
#define TRANSPOSE_FIX 24
#define VOICE_SILENCE_LEVEL 1100
#define _MAX_NOTES 32
#define LG_N 6
#define _N_ (1 << LG_N)
template<typename T>
/*template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
}
@ -38,7 +36,7 @@ inline static T min(const T& a, const T& b) {
template<typename T>
inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}
}*/
#define QER(n,b) ( ((float)n)/(1<<b) )
@ -67,7 +65,7 @@ inline static T max(const T& a, const T& b) {
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift)
static inline int32_t signed_saturate_rshift(int32_t val, int32_t bits, int32_t rshift)
{
int32_t out, max;

@ -37,8 +37,6 @@
#define TRANSPOSE_FIX 24
#define VOICE_SILENCE_LEVEL 1100
#define _MAX_NOTES 32
#define PB_RANGE_DEFAULT 1
#define PB_STEP_DEFAULT 0
#define MW_RANGE_DEFAULT 50

@ -0,0 +1,91 @@
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SYNTH_H
#define SYNTH_H
//#define SUPER_PRECISE
#include <stdint.h>
#define MIDI_CONTROLLER_MODE_MAX 2
#define TRANSPOSE_FIX 24
#define VOICE_SILENCE_LEVEL 1100
#define LG_N 6
#define _N_ (1 << LG_N)
/*template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
}
template<typename T>
inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}*/
#define QER(n,b) ( ((float)n)/(1<<b) )
#define FRAC_NUM float
#define SIN_FUNC sinf
// #define SIN_FUNC arm_sin_f32 // very fast but not as accurate
#define COS_FUNC cosf
// #define COS_FUNC arm_cos_f32 // very fast but not as accurate
#define LOG_FUNC logf
#define EXP_FUNC expf
#define SQRT_FUNC sqrtf
// #define ARM_SQRT_FUNC arm_sqrt_f32 // fast but not as accurate
#if defined(__circle__)
#include <circle/timer.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define constrain(amt, low, high) ({ \
__typeof__(amt) _amt = (amt); \
__typeof__(low) _low = (low); \
__typeof__(high) _high = (high); \
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
static inline int32_t signed_saturate_rshift(int32_t val, int32_t bits, int32_t rshift)
{
int32_t out, max;
out = val >> rshift;
max = 1 << (bits - 1);
if (out >= 0)
{
if (out > max - 1) out = max - 1;
}
else
{
if (out < -max) out = -max;
}
return out;
}
static inline uint32_t millis (void)
{
return uint32_t(CTimer::Get ()->GetClockTicks () / (CLOCKHZ / 1000));
}
#endif
#endif

@ -0,0 +1,63 @@
/*
synth_dexed
synth_dexed is a port of the Dexed sound engine (https://github.com/asb2m10/dexed)
as library for the Teensy-3.5/3.6/4.x with an audio shield.
Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android
(c)2018-2021 H. Wirtz <wirtz@parasitstudio.de>
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, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "synth_dexed.h"
#if defined(TEENSYDUINO)
void AudioSynthDexed::update(void)
{
if (in_update == true)
{
xrun++;
return;
}
else
in_update = true;
elapsedMicros render_time;
audio_block_t *block;
block = allocate();
if (!block)
{
in_update = false;
return;
}
getSamples(block->data, AUDIO_BLOCK_SAMPLES);
if (render_time > audio_block_time_us) // everything greater audio_block_time_us (2.9ms for buffer size of 128) is a buffer underrun!
xrun++;
if (render_time > render_time_max)
render_time_max = render_time;
transmit(block, 0);
release(block);
in_update = false;
};
#endif

@ -0,0 +1,72 @@
/*
synth_dexed
synth_dexed is a port of the Dexed sound engine (https://github.com/asb2m10/dexed)
as library for the Teensy-3.5/3.6/4.x with an audio shield.
Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android
(c)2018-2021 H. Wirtz <wirtz@parasitstudio.de>
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, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "dexed.h"
#if defined(TEENSYDUINO)
#include <AudioStream.h>
#endif
#include <stdint.h>
#define SYNTH_DEXED_VERSION "1.0.1"
//#define DEBUG 1
#define SAMPLE_RATE 44100
#define TRANSPOSE_FIX 24
#define VOICE_SILENCE_LEVEL 1100
#define PB_RANGE_DEFAULT 1
#define PB_STEP_DEFAULT 0
#define MW_RANGE_DEFAULT 50
#define MW_ASSIGN_DEFAULT 0 // Bitmapped: 0: Pitch, 1: Amp, 2: Bias
#define MW_MODE_DEFAULT 0
#define FC_RANGE_DEFAULT 50
#define FC_ASSIGN_DEFAULT 0 // Bitmapped: 0: Pitch, 1: Amp, 2: Bias
#define FC_MODE_DEFAULT 0
#define BC_RANGE_DEFAULT 50
#define BC_ASSIGN_DEFAULT 0 // Bitmapped: 0: Pitch, 1: Amp, 2: Bias
#define BC_MODE_DEFAULT 0
#define AT_RANGE_DEFAULT 50
#define AT_ASSIGN_DEFAULT 0 // Bitmapped: 0: Pitch, 1: Amp, 2: Bias
#define AT_MODE_DEFAULT 0
#define PORTAMENTO_MODE_DEFAULT 0 // 0: Retain, 1: Follow
#define PORTAMENTO_GLISSANDO_DEFAULT 0
#define PORTAMENTO_TIME_DEFAULT 0
//#define USE_SIMPLE_COMPRESSOR 1
#if defined(TEENSYDUINO)
class AudioSynthDexed : public AudioStream, public Dexed
{
public:
AudioSynthDexed(uint8_t max_notes, uint16_t sample_rate) : AudioStream(0, NULL), Dexed(max_notes,sample_rate) { };
protected:
const uint16_t audio_block_time_us = 1000000 / (SAMPLE_RATE / AUDIO_BLOCK_SAMPLES);
volatile bool in_update = false;
void update(void);
};
#endif

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save