diff --git a/MicroDexed.ino b/MicroDexed.ino index 4c58252..f8e676a 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -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; diff --git a/UI.hpp b/UI.hpp index f33cef7..59f9cb4 100644 --- a/UI.hpp +++ b/UI.hpp @@ -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); diff --git a/config.h b/config.h index 338ef2f..d820b08 100644 --- a/config.h +++ b/config.h @@ -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" diff --git a/dexed_sd.h b/dexed_sd.h index 20ed64e..bcd0bcd 100644 --- a/dexed_sd.h +++ b/dexed_sd.h @@ -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); diff --git a/third-party/Synth_Dexed/EngineMkI.cpp b/third-party/Synth_Dexed/EngineMkI.cpp new file mode 100644 index 0000000..be6cc2b --- /dev/null +++ b/third-party/Synth_Dexed/EngineMkI.cpp @@ -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 +#include + +#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< 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 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 ¶m = 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; + } +} + diff --git a/third-party/Synth_Dexed/EngineMkI.h b/third-party/Synth_Dexed/EngineMkI.h new file mode 100644 index 0000000..dcafa59 --- /dev/null +++ b/third-party/Synth_Dexed/EngineMkI.h @@ -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 diff --git a/third-party/Synth_Dexed/EngineMsfa.cpp b/third-party/Synth_Dexed/EngineMsfa.cpp new file mode 100644 index 0000000..e947b48 --- /dev/null +++ b/third-party/Synth_Dexed/EngineMsfa.cpp @@ -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 ¶m = 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; + } +} diff --git a/third-party/Synth_Dexed/EngineMsfa.h b/third-party/Synth_Dexed/EngineMsfa.h new file mode 100644 index 0000000..c3cd9e2 --- /dev/null +++ b/third-party/Synth_Dexed/EngineMsfa.h @@ -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; +}; diff --git a/third-party/Synth_Dexed/EngineOpl.cpp b/third-party/Synth_Dexed/EngineOpl.cpp new file mode 100644 index 0000000..3f52f6a --- /dev/null +++ b/third-party/Synth_Dexed/EngineOpl.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2014 Pascal Gauthier. + * Copyright (C) 2012 Steffen Ohrendorf + * + * 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 + * + * 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 ¶m = 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; + } +} diff --git a/third-party/Synth_Dexed/EngineOpl.h b/third-party/Synth_Dexed/EngineOpl.h new file mode 100644 index 0000000..2d2b03d --- /dev/null +++ b/third-party/Synth_Dexed/EngineOpl.h @@ -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 diff --git a/third-party/Synth_Dexed/PluginFx.cpp b/third-party/Synth_Dexed/PluginFx.cpp new file mode 100644 index 0000000..5d9df3b --- /dev/null +++ b/third-party/Synth_Dexed/PluginFx.cpp @@ -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 +#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); +} diff --git a/third-party/Synth_Dexed/PluginFx.h b/third-party/Synth_Dexed/PluginFx.h new file mode 100644 index 0000000..f31e208 --- /dev/null +++ b/third-party/Synth_Dexed/PluginFx.h @@ -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); +}; diff --git a/third-party/Synth_Dexed/aligned_buf.h b/third-party/Synth_Dexed/aligned_buf.h new file mode 100644 index 0000000..19371b8 --- /dev/null +++ b/third-party/Synth_Dexed/aligned_buf.h @@ -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 +#include + +template +class AlignedBuf { + public: + T *get() { + return (T *)((((intptr_t)storage_) + alignment - 1) & -alignment); + } + private: + unsigned char storage_[size * sizeof(T) + alignment]; +}; + +#endif diff --git a/third-party/Synth_Dexed/compressor.h b/third-party/Synth_Dexed/compressor.h new file mode 100644 index 0000000..d279abd --- /dev/null +++ b/third-party/Synth_Dexed/compressor.h @@ -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 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 diff --git a/third-party/Synth_Dexed/controllers.h b/third-party/Synth_Dexed/controllers.h new file mode 100644 index 0000000..5cd72d6 --- /dev/null +++ b/third-party/Synth_Dexed/controllers.h @@ -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 +#include "synth.h" +#include +#include +#include +#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 diff --git a/third-party/Synth_Dexed/dexed.cpp b/third-party/Synth_Dexed/dexed.cpp new file mode 100644 index 0000000..ba607c8 --- /dev/null +++ b/third-party/Synth_Dexed/dexed.cpp @@ -0,0 +1,1792 @@ +/* + 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 + + 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 +#include +#include +#include +#include +#include "dexed.h" +#include "synth.h" +#include "fm_core.h" +#include "exp2.h" +#include "sin.h" +#include "freqlut.h" +#include "controllers.h" +#include "PluginFx.h" +#include "porta.h" +#include "compressor.h" + +Dexed::Dexed(uint8_t maxnotes, uint16_t rate) +{ + samplerate = float32_t(rate); + + Exp2::init(); + Tanh::init(); + Sin::init(); + + Freqlut::init(rate); + Lfo::init(rate); + PitchEnv::init(rate); + Env::init_sr(rate); + Porta::init_sr(rate); + fx.init(rate); + + currentNote = 0; + resetControllers(); + controllers.masterTune = 0; + controllers.opSwitch = 0x3f; // enable all operators + lastKeyDown = -1; + vuSignal = 0.0; + lfo.reset(data + 137); + sustain = false; + voices = NULL; + + max_notes=maxnotes; + used_notes=max_notes; + setMonoMode(false); + loadInitVoice(); + + xrun = 0; + render_time_max = 0; + + setVelocityScale(MIDI_VELOCITY_SCALING_OFF); + setNoteRefreshMode(false); + + engineMsfa = new EngineMsfa; + engineMkI = new EngineMkI; + engineOpl = new EngineOpl; + setEngineType(MKI); + +#ifndef TEENSYDUINO + compressor = new Compressor(samplerate); +#endif + use_compressor = false; + + /* Init notes */ + 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; +} + +Dexed::~Dexed() +{ + currentNote = -1; + + delete[] voices; +} + +void Dexed::setEngineType(uint8_t engine) +{ + panic(); + + switch(engine) + { + case MSFA: + controllers.core = (FmCore*)engineMsfa; + engineType=MSFA; + break; + case MKI: + controllers.core = (FmCore*)engineMkI; + engineType=MKI; + break; + case OPL: + controllers.core = (FmCore*)engineOpl; + engineType=OPL; + break; + default: + controllers.core = (FmCore*)engineMsfa; + engineType=MSFA; + break; + } + + controllers.refresh(); +} + +uint8_t Dexed::getEngineType(void) +{ + return(engineType); +} + +FmCore* Dexed::getEngineAddress(void) +{ + return(controllers.core); +} + +void Dexed::setMaxNotes(uint8_t new_max_notes) +{ + panic(); + used_notes = constrain(new_max_notes, 0, max_notes); +} + +void Dexed::activate(void) +{ + panic(); + controllers.refresh(); +} + +void Dexed::deactivate(void) +{ + panic(); +} + +void Dexed::getSamples(float* buffer, uint16_t n_samples) +{ + if (refreshVoice) + { + 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); + } + lfo.reset(data + 137); + refreshVoice = false; + } + + arm_fill_f32(0.0, buffer, n_samples); + + for (uint16_t i = 0; i < n_samples; i += _N_) + { + AlignedBuf audiobuf; + + for (uint8_t j = 0; j < _N_; ++j) + { + audiobuf.get()[j] = 0; + } + + int32_t lfovalue = lfo.getsample(); + int32_t lfodelay = lfo.getdelay(); + + for (uint8_t note = 0; note < used_notes; note++) + { + if (voices[note].live) + { + voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); + + for (uint8_t j = 0; j < _N_; ++j) + { + buffer[i + j] += signed_saturate_rshift(audiobuf.get()[j] >> 4, 24, 9) / 32768.0; + audiobuf.get()[j] = 0; + } + } + } + } + + fx.process(buffer, n_samples); // Needed for fx.Gain()!!! + +#ifndef TEENSYDUINO + if (use_compressor == true) + compressor->doCompression(buffer, n_samples); +#endif +} + +void Dexed::getSamples(int16_t* buffer, uint16_t n_samples) +{ + float tmp[n_samples]; + + getSamples(tmp, n_samples); + arm_float_to_q15(tmp, (q15_t*)buffer, n_samples); +} + +void Dexed::keydown(uint8_t pitch, uint8_t velo) { + if ( velo == 0 ) { + keyup(pitch); + return; + } + + velo=uint8_t((float(velo)/127.0)*velocity_diff+0.5)+velocity_offset; + + pitch += data[144] - TRANSPOSE_FIX; + + int32_t previousKeyDown = lastKeyDown; + lastKeyDown = pitch; + + int32_t porta = -1; + if ( controllers.portamento_enable_cc && previousKeyDown >= 0 ) + porta = controllers.portamento_cc; + + uint8_t note = currentNote; + uint8_t keydown_counter = 0; + + if (!monoMode && noteRefreshMode) + { + 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) + { + // retrigger or refresh note? + voices[i].dx7_note->keyup(); + voices[i].midi_note = pitch; + voices[i].velocity = velo; + voices[i].keydown = true; + voices[i].sustained = sustain; + voices[i].live = true; + voices[i].dx7_note->init(data, pitch, velo, pitch, porta, &controllers); + voices[i].key_pressed_timer = millis(); + return; + } + } + } + + for (uint8_t i = 0; i <= used_notes; i++) + { + if (i == used_notes) + { + 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 < used_notes; n++) + { + if (voices[n].key_pressed_timer < min_timer) + { + min_timer = voices[n].key_pressed_timer; + note = n; + } + } + voices[note].keydown = false; + voices[note].sustained = false; + voices[note].live = false; + voices[note].key_pressed_timer = 0; + keydown_counter--; + } + + if (!voices[note].keydown) + { + 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; + 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(); + voices[i].key_pressed_timer = millis(); + keydown_counter++; + break; + } + else + { + keydown_counter++; + } + note = (note + 1) % used_notes; + } + + if ( monoMode ) { + for (uint8_t i = 0; i < used_notes; i++) { + if ( voices[i].live ) { + // all keys are up, only transfer signal + if ( ! voices[i].keydown ) { + voices[i].live = false; + voices[note].dx7_note->transferSignal(*voices[i].dx7_note); + break; + } + if ( voices[i].midi_note < pitch ) { + voices[i].live = false; + voices[note].dx7_note->transferState(*voices[i].dx7_note); + break; + } + return; + } + } + } + + voices[note].live = true; +} + +void Dexed::keyup(uint8_t pitch) { + uint8_t note; + + pitch = constrain(pitch, 0, 127); + + pitch += data[144] - TRANSPOSE_FIX; + + 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; + + break; + } + } + + // note not found ? + if ( note >= used_notes ) { + return; + } + + if ( monoMode ) { + int8_t highNote = -1; + uint8_t target = 0; + 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; + } + } + + if ( highNote != -1 && voices[note].live ) { + voices[note].live = false; + voices[note].key_pressed_timer = 0; + voices[target].live = true; + voices[target].dx7_note->transferState(*voices[note].dx7_note); + } + } + + if ( sustain ) { + voices[note].sustained = true; + } else { + voices[note].dx7_note->keyup(); + } +} + +void Dexed::doRefreshVoice(void) +{ + refreshVoice = true; +} + +void Dexed::setOPAll(uint8_t ops) +{ + controllers.opSwitch = ops; +} + +bool Dexed::getMonoMode(void) { + return monoMode; +} + +void Dexed::setMonoMode(bool mode) { + if (monoMode == mode) + return; + + notesOff(); + monoMode = mode; +} + +void Dexed::setNoteRefreshMode(bool mode) { + noteRefreshMode = mode; +} + +void Dexed::setSustain(bool s) +{ + if (sustain == s) + return; + + sustain = s; + + if (!getSustain()) + { + for (uint8_t note = 0; note < getMaxNotes(); note++) + { + if (voices[note].sustained && !voices[note].keydown) + { + voices[note].dx7_note->keyup(); + voices[note].sustained = false; + } + } + } +} + +bool Dexed::getSustain(void) +{ + return sustain; +} + +void Dexed::panic(void) +{ + for (uint8_t i = 0; i < max_notes; i++) + { + if (voices[i].live == true) { + voices[i].keydown = false; + voices[i].live = false; + voices[i].sustained = false; + voices[i].key_pressed_timer = 0; + if ( voices[i].dx7_note != NULL ) { + voices[i].dx7_note->oscSync(); + } + } + } + setSustain(0); +} + +void Dexed::resetControllers(void) +{ + controllers.values_[kControllerPitch] = 0x2000; + controllers.values_[kControllerPitchRange] = 0; + controllers.values_[kControllerPitchStep] = 0; + controllers.values_[kControllerPortamentoGlissando] = 0; + + controllers.modwheel_cc = 0; + controllers.foot_cc = 0; + controllers.breath_cc = 0; + controllers.aftertouch_cc = 0; + controllers.portamento_enable_cc = false; + controllers.portamento_cc = 0; + controllers.refresh(); +} + +void Dexed::notesOff(void) { + for (uint8_t i = 0; i < max_notes; i++) { + if (voices[i].live == true) { + voices[i].keydown = false; + voices[i].live = false; + } + } +} + +uint8_t Dexed::getMaxNotes(void) +{ + return used_notes; +} + +uint8_t Dexed::getNumNotesPlaying(void) +{ + uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers + uint8_t i; + uint8_t count_playing_voices = 0; + + for (i = 0; i < used_notes; i++) + { + if (voices[i].live == true) + { + uint8_t op_amp = 0; + uint8_t op_carrier_num = 0; + + memset(&voiceStatus, 0, sizeof(VoiceStatus)); + voices[i].dx7_note->peekVoiceStatus(voiceStatus); + + for (uint8_t op = 0; op < 6; op++) + { + if ((op_carrier & (1 << op))) + { + // this voice is a carrier! + op_carrier_num++; + if (voiceStatus.amp[op] <= VOICE_SILENCE_LEVEL && voiceStatus.ampStep[op] == 4) + { + // this voice produces no audio output + op_amp++; + } + } + } + + if (op_amp == op_carrier_num) + { + // all carrier-operators are silent -> disable the voice + voices[i].live = false; + voices[i].sustained = false; + voices[i].keydown = false; +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + Serial.print(F("Shutdown voice: ")); + Serial.println(i, DEC); +#endif + } + else + count_playing_voices++; + } + } + return (count_playing_voices); +} + +bool Dexed::decodeVoice(uint8_t* new_data, uint8_t* encoded_data) +{ + uint8_t* p_data = new_data; + uint8_t op; + uint8_t tmp; + char dexed_voice_name[11]; + + panic(); + + for (op = 0; op < 6; op++) + { + // 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 + memcpy(&new_data[op * 21], &encoded_data[op * 17], 11); + tmp = encoded_data[(op * 17) + 11]; + *(p_data + DEXED_OP_SCL_LEFT_CURVE + (op * 21)) = (tmp & 0x03); + *(p_data + DEXED_OP_SCL_RGHT_CURVE + (op * 21)) = (tmp & 0x0c) >> 2; + tmp = encoded_data[(op * 17) + 12]; + *(p_data + DEXED_OP_OSC_DETUNE + (op * 21)) = (tmp & 0x78) >> 3; + *(p_data + DEXED_OP_OSC_RATE_SCALE + (op * 21)) = (tmp & 0x07); + tmp = encoded_data[(op * 17) + 13]; + *(p_data + DEXED_OP_KEY_VEL_SENS + (op * 21)) = (tmp & 0x1c) >> 2; + *(p_data + DEXED_OP_AMP_MOD_SENS + (op * 21)) = (tmp & 0x03); + *(p_data + DEXED_OP_OUTPUT_LEV + (op * 21)) = encoded_data[(op * 17) + 14]; + tmp = encoded_data[(op * 17) + 15]; + *(p_data + DEXED_OP_FREQ_COARSE + (op * 21)) = (tmp & 0x3e) >> 1; + *(p_data + DEXED_OP_OSC_MODE + (op * 21)) = (tmp & 0x01); + *(p_data + DEXED_OP_FREQ_FINE + (op * 21)) = encoded_data[(op * 17) + 16]; + } + // 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 + memcpy(&new_data[DEXED_VOICE_OFFSET], &encoded_data[102], 8); + tmp = encoded_data[110]; + *(p_data + DEXED_VOICE_OFFSET + DEXED_ALGORITHM) = (tmp & 0x1f); + tmp = encoded_data[111]; + *(p_data + DEXED_VOICE_OFFSET + DEXED_OSC_KEY_SYNC) = (tmp & 0x08) >> 3; + *(p_data + DEXED_VOICE_OFFSET + DEXED_FEEDBACK) = (tmp & 0x07); + // DEXED_LFO_SPEED, // 11 + // DEXED_LFO_DELAY, // 12 + // DEXED_LFO_PITCH_MOD_DEP, // 13 + // DEXED_LFO_AMP_MOD_DEP, // 14 + memcpy(&new_data[DEXED_VOICE_OFFSET + DEXED_LFO_SPEED], &encoded_data[112], 4); + tmp = encoded_data[116]; + *(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_SENS) = (tmp & 0x30) >> 4; + *(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_WAVE) = (tmp & 0x0e) >> 1; + *(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_SYNC) = (tmp & 0x01); + *(p_data + DEXED_VOICE_OFFSET + DEXED_TRANSPOSE) = encoded_data[117]; + memcpy(&new_data[DEXED_VOICE_OFFSET + DEXED_NAME], &encoded_data[118], 10); + panic(); + doRefreshVoice(); + + strncpy(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 [")); + Serial.print(dexed_voice_name); + Serial.println(F("] decoded.")); +#endif + + return (true); +} + +bool Dexed::encodeVoice(uint8_t* encoded_data) +{ + uint8_t* p_data = data; + uint8_t op; + + for (op = 0; op < 6; op++) + { + // 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 + memcpy(&encoded_data[op * 17], &data[op * 21], 11); + encoded_data[(op * 17) + 11] = ((*(p_data + DEXED_OP_SCL_RGHT_CURVE + (op * 21)) & 0x0c) << 2) | (*(p_data + DEXED_OP_SCL_LEFT_CURVE + (op * 21)) & 0x03); + encoded_data[(op * 17) + 12] = ((*(p_data + DEXED_OP_OSC_DETUNE + (op * 21)) & 0x0f) << 3) | (*(p_data + DEXED_OP_OSC_RATE_SCALE + (op * 21)) & 0x07); + encoded_data[(op * 17) + 13] = ((*(p_data + DEXED_OP_KEY_VEL_SENS + (op * 21)) & 0x07) << 2) | (*(p_data + DEXED_OP_AMP_MOD_SENS + (op * 21)) & 0x03); + encoded_data[(op * 17) + 14] = *(p_data + DEXED_OP_OUTPUT_LEV + (op * 21)); + encoded_data[(op * 17) + 15] = ((*(p_data + DEXED_OP_FREQ_COARSE + (op * 21)) & 0x1f) << 1) | (*(p_data + DEXED_OP_OSC_MODE + (op * 21)) & 0x01); + encoded_data[(op * 17) + 16] = *(p_data + DEXED_OP_FREQ_FINE + (op * 21)); + } + // 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 + memcpy(&encoded_data[102], &data[DEXED_VOICE_OFFSET], 8); + encoded_data[110] = (*(p_data + DEXED_VOICE_OFFSET + DEXED_ALGORITHM) & 0x1f); + encoded_data[111] = (((*(p_data + DEXED_VOICE_OFFSET + DEXED_OSC_KEY_SYNC) & 0x01) << 3) | ((*(p_data + DEXED_VOICE_OFFSET + DEXED_FEEDBACK)) & 0x07)); + // DEXED_LFO_SPEED, // 11 + // DEXED_LFO_DELAY, // 12 + // DEXED_LFO_PITCH_MOD_DEP, // 13 + // DEXED_LFO_AMP_MOD_DEP, // 14 + memcpy(&encoded_data[112], &data[DEXED_VOICE_OFFSET + DEXED_LFO_SPEED], 4); + encoded_data[116] = (((*(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_SENS) & 0x07) << 4) | (((*(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_WAVE)) & 0x07) << 1) | ((*(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_SYNC)) & 0x01)); + encoded_data[117] = *(p_data + DEXED_VOICE_OFFSET + DEXED_TRANSPOSE); + memset(&encoded_data[118], 0, 10); + memcpy(&encoded_data[118], &data[DEXED_VOICE_OFFSET + DEXED_NAME], 10); + + return (true); +} + +bool Dexed::getVoiceData(uint8_t* data_copy) +{ + memcpy(data_copy, data, sizeof(data)); + return (true); +} + +void Dexed::setVoiceDataElement(uint8_t address, uint8_t value) +{ + address = constrain(address, 0, NUM_VOICE_PARAMETERS); + data[address] = value; + doRefreshVoice(); +} + +uint8_t Dexed::getVoiceDataElement(uint8_t address) +{ + address = constrain(address, 0, NUM_VOICE_PARAMETERS); + return (data[address]); +} + +void Dexed::loadVoiceParameters(uint8_t* new_data) +{ +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + char dexed_voice_name[11]; +#endif + + panic(); + 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); + dexed_voice_name[10] = '\0'; + + Serial.print(F("Voice [")); + Serial.print(dexed_voice_name); + Serial.println(F("] loaded.")); +#endif +} + +void Dexed::loadInitVoice(void) +{ + loadVoiceParameters(init_voice); +} + +void Dexed::setPBController(uint8_t pb_range, uint8_t pb_step) +{ +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + Serial.println(F("Dexed::setPBController")); +#endif + + pb_range = constrain(pb_range, 0, 12); + pb_step = constrain(pb_step, 0, 12); + + controllers.values_[kControllerPitchRange] = pb_range; + controllers.values_[kControllerPitchStep] = pb_step; + + controllers.refresh(); +} + +void Dexed::setMWController(uint8_t mw_range, uint8_t mw_assign, uint8_t mw_mode) +{ +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + Serial.println(F("Dexed::setMWController")); +#endif + + mw_range = constrain(mw_range, 0, 99); + mw_assign = constrain(mw_assign, 0, 7); + mw_mode = constrain(mw_mode, 0, MIDI_CONTROLLER_MODE_MAX); + + controllers.wheel.setRange(mw_range); + controllers.wheel.setTarget(mw_assign); + controllers.wheel.setMode(mw_mode); + + controllers.refresh(); +} + +void Dexed::setFCController(uint8_t fc_range, uint8_t fc_assign, uint8_t fc_mode) +{ +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + Serial.println(F("Dexed::setFCController")); +#endif + + fc_range = constrain(fc_range, 0, 99); + fc_assign = constrain(fc_assign, 0, 7); + fc_mode = constrain(fc_mode, 0, MIDI_CONTROLLER_MODE_MAX); + + controllers.foot.setRange(fc_range); + controllers.foot.setTarget(fc_assign); + controllers.foot.setMode(fc_mode); + + controllers.refresh(); +} + +void Dexed::setBCController(uint8_t bc_range, uint8_t bc_assign, uint8_t bc_mode) +{ +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + Serial.println(F("Dexed::setBCController")); +#endif + + bc_range = constrain(bc_range, 0, 99); + bc_assign = constrain(bc_assign, 0, 7); + bc_mode = constrain(bc_mode, 0, MIDI_CONTROLLER_MODE_MAX); + + controllers.breath.setRange(bc_range); + controllers.breath.setTarget(bc_assign); + controllers.breath.setMode(bc_mode); + + controllers.refresh(); +} + +void Dexed::setATController(uint8_t at_range, uint8_t at_assign, uint8_t at_mode) +{ +#if defined(MICRODEXED_VERSION) && defined(DEBUG) + Serial.println(F("Dexed::setATController")); +#endif + + at_range = constrain(at_range, 0, 99); + at_assign = constrain(at_assign, 0, 7); + at_mode = constrain(at_mode, 0, MIDI_CONTROLLER_MODE_MAX); + + controllers.at.setRange(at_range); + controllers.at.setTarget(at_assign); + controllers.at.setMode(at_mode); + + controllers.refresh(); +} + +void Dexed::setPortamento(uint8_t portamento_mode, uint8_t portamento_glissando, uint8_t portamento_time) +{ + portamento_mode = constrain(portamento_mode, 0, 1); + portamento_glissando = constrain(portamento_glissando, 0, 1); + portamento_time = constrain(portamento_time, 0, 99); + + controllers.portamento_cc = portamento_time; + controllers.portamento_enable_cc = portamento_mode > 63; + + if (portamento_time > 0) + controllers.portamento_enable_cc = true; + else + controllers.portamento_enable_cc = false; + + controllers.values_[kControllerPortamentoGlissando] = portamento_glissando; + + controllers.refresh(); +} + +void Dexed::setPortamentoMode(uint8_t portamento_mode) +{ + portamento_mode = constrain(portamento_mode, 0, 1); + controllers.portamento_enable_cc = portamento_mode > 63; + + controllers.refresh(); +} + +uint8_t Dexed::getPortamentoMode(void) +{ + return(controllers.portamento_enable_cc); +} + +void Dexed::setPortamentoGlissando(uint8_t portamento_glissando) +{ + portamento_glissando = constrain(portamento_glissando, 0, 1); + controllers.values_[kControllerPortamentoGlissando] = portamento_glissando; + + controllers.refresh(); +} + +uint8_t Dexed::getPortamentoGlissando(void) +{ + return(controllers.values_[kControllerPortamentoGlissando]); +} + +void Dexed::setPortamentoTime(uint8_t portamento_time) +{ + portamento_time = constrain(portamento_time, 0, 99); + controllers.portamento_cc = portamento_time; + + if (portamento_time > 0) + controllers.portamento_enable_cc = true; + else + controllers.portamento_enable_cc = false; + + controllers.refresh(); +} + +uint8_t Dexed::getPortamentoTime(void) +{ + return(controllers.portamento_cc); +} + +int16_t Dexed::checkSystemExclusive(const uint8_t* sysex, const uint16_t len) +/* + -1: SysEx end status byte not detected. + -2: SysEx vendor not Yamaha. + -3: Unknown SysEx parameter change. + -4: Unknown SysEx voice or function. + -5: Not a SysEx voice bulk upload. + -6: Wrong length for SysEx voice bulk upload (not 155). + -7: Checksum error for one voice. + -8: Not a SysEx bank bulk upload. + -9: Wrong length for SysEx bank bulk upload (not 4096). + -10: Checksum error for bank. + -11: Unknown SysEx message. + 64-77: Function parameter changed. + 100: Voice loaded. + 200: Bank loaded. + 300-455: Voice parameter changed. +*/ +{ + int32_t bulk_checksum_calc = 0; + const int8_t bulk_checksum = sysex[161]; + + // Check for SYSEX end byte + if (sysex[len - 1] != 0xf7) + return(-1); + + // check for Yamaha sysex + if (sysex[1] != 0x43) + return(-2); + + // Decode SYSEX by means of length + switch (len) + { + case 7: // parse parameter change + if (((sysex[3] & 0x7c) >> 2) != 0 && ((sysex[3] & 0x7c) >> 2) != 2) + return(-3); + + if ((sysex[3] & 0x7c) >> 2 == 0) // Voice parameter + { + setVoiceDataElement((sysex[4] & 0x7f) + ((sysex[3] & 0x03) * 128), sysex[5]); + doRefreshVoice(); + return((sysex[4] & 0x7f) + ((sysex[3] & 0x03) * 128)+300); + } + else if ((sysex[3] & 0x7c) >> 2 == 2) // Function parameter + return(sysex[4]); + else + return(-4); + break; + case 163: // 1 Voice bulk upload + if ((sysex[3] & 0x7f) != 0) + return(-5); + + if (((sysex[4] << 7) | sysex[5]) != 0x9b) + return(-6); + + // checksum calculation + for (uint8_t i = 0; i < 155 ; i++) + bulk_checksum_calc -= sysex[i + 6]; + bulk_checksum_calc &= 0x7f; + + if (bulk_checksum_calc != bulk_checksum) + return(-7); + + return(100); + break; + case 4104: // 1 Bank bulk upload + if ((sysex[3] & 0x7f) != 9) + return(-8); + + if (((sysex[4] << 7) | sysex[5]) != 0x1000) + return(-9); + + // checksum calculation + for (uint16_t i = 0; i < 4096 ; i++) + bulk_checksum_calc -= sysex[i + 6]; + bulk_checksum_calc &= 0x7f; + + if (bulk_checksum_calc != bulk_checksum) + return(-10); + + return(200); + break; + default: + return(-11); + } +} + +uint32_t Dexed::getXRun(void) +{ + return (xrun); +} + +uint16_t Dexed::getRenderTimeMax(void) +{ + return (render_time_max); +} + +void Dexed::resetRenderTimeMax(void) +{ + render_time_max = 0; +} + +void Dexed::ControllersRefresh(void) +{ + controllers.refresh(); +} + +void Dexed::setMasterTune(int8_t mastertune) +{ + mastertune = constrain(mastertune, -99, 99); + + controllers.masterTune = (int(mastertune / 100.0 * 0x4000) << 11) * (1.0 / 12.0); +} + +int8_t Dexed::getMasterTune(void) +{ + return (controllers.masterTune); +} + +void Dexed::setModWheel(uint8_t value) +{ + value = constrain(value, 0, 127); + + controllers.modwheel_cc = value; +} + +uint8_t Dexed::getModWheel(void) +{ + return (controllers.modwheel_cc); +} + +void Dexed::setBreathController(uint8_t value) +{ + value = constrain(value, 0, 127); + + controllers.breath_cc = value; +} + +uint8_t Dexed::getBreathController(void) +{ + return (controllers.breath_cc); +} + +void Dexed::setFootController(uint8_t value) +{ + value = constrain(value, 0, 127); + + controllers.foot_cc = value; +} + +uint8_t Dexed::getFootController(void) +{ + return (controllers.foot_cc); +} + +void Dexed::setAftertouch(uint8_t value) +{ + value = constrain(value, 0, 127); + + controllers.aftertouch_cc = value; +} + +uint8_t Dexed::getAftertouch(void) +{ + return (controllers.aftertouch_cc); +} + +void Dexed::setPitchbend(uint8_t value1, uint8_t value2) +{ + setPitchbend(uint16_t(((value2 & 0x7f) << 7) | (value1 & 0x7f))); +} + +void Dexed::setPitchbend(int16_t value) +{ + value = constrain(value, -8192, 8191); + + controllers.values_[kControllerPitch] = value + 0x2000; // -8192 to +8191 --> 0 to 16383 + setPitchbend(uint16_t(value + 0x2000)); // -8192 to +8191 --> 0 to 16383 +} + +void Dexed::setPitchbend(uint16_t value) +{ + controllers.values_[kControllerPitch] = (value & 0x3fff); +} + +int16_t Dexed::getPitchbend(void) +{ + return (controllers.values_[kControllerPitch] - 0x2000); +} + +void Dexed::setPitchbendRange(uint8_t range) +{ + range = constrain(range, 0, 12); + + controllers.values_[kControllerPitchRange] = range; +} + +uint8_t Dexed::getPitchbendRange(void) +{ + return (controllers.values_[kControllerPitchRange]); +} + +void Dexed::setPitchbendStep(uint8_t step) +{ + step = constrain(step, 0, 12); + + controllers.values_[kControllerPitchStep] = step; +} + +uint8_t Dexed::getPitchbendStep(void) +{ + return (controllers.values_[kControllerPitchStep]); +} + +void Dexed::setModWheelRange(uint8_t range) +{ + range = constrain(range, 0, 12); + + controllers.wheel.setRange(range); +} + +uint8_t Dexed::getModWheelRange(void) +{ + return (controllers.wheel.getRange()); +} + +void Dexed::setModWheelTarget(uint8_t target) +{ + target = constrain(target, 0, 7); + + controllers.wheel.setTarget(target); +} + +uint8_t Dexed::getModWheelTarget(void) +{ + return (controllers.wheel.getTarget()); +} + +void Dexed::setFootControllerRange(uint8_t range) +{ + range = constrain(range, 0, 12); + + controllers.foot.setRange(range); +} + +uint8_t Dexed::getFootControllerRange(void) +{ + return (controllers.foot.getRange()); +} + +void Dexed::setFootControllerTarget(uint8_t target) +{ + target = constrain(target, 0, 7); + + controllers.foot.setTarget(target); +} + +uint8_t Dexed::getFootControllerTarget(void) +{ + return (controllers.foot.getTarget()); +} + +void Dexed::setBreathControllerRange(uint8_t range) +{ + range = constrain(range, 0, 12); + + controllers.breath.setRange(range); +} + +uint8_t Dexed::getBreathControllerRange(void) +{ + return (controllers.breath.getRange()); +} + +void Dexed::setBreathControllerTarget(uint8_t target) +{ + target = constrain(target, 0, 7); + + controllers.breath.setTarget(target); +} + +uint8_t Dexed::getBreathControllerTarget(void) +{ + return (controllers.breath.getTarget()); +} + +void Dexed::setAftertouchRange(uint8_t range) +{ + range = constrain(range, 0, 12); + + controllers.at.setRange(range); +} + +uint8_t Dexed::getAftertouchRange(void) +{ + return (controllers.at.getRange()); +} + +void Dexed::setAftertouchTarget(uint8_t target) +{ + target = constrain(target, 0, 7); + + controllers.at.setTarget(target); +} + +uint8_t Dexed::getAftertouchTarget(void) +{ + return (controllers.at.getTarget()); +} + +void Dexed::setFilterCutoff(float cutoff) +{ + fx.Cutoff = cutoff; +} + +float Dexed::getFilterCutoff(void) +{ + return (fx.Cutoff); +} + +void Dexed::setFilterResonance(float resonance) +{ + fx.Reso = resonance; +} + +float Dexed::getFilterResonance(void) +{ + return (fx.Reso); +} + +void Dexed::setGain(float gain) +{ + fx.Gain = gain; +} + +float Dexed::getGain(void) +{ + return (fx.Gain); +} + +void Dexed::setOPRateAll(uint8_t rate) +{ + rate = constrain(rate, 0, 99); + + for (uint8_t op = 0; op < 6; op++) + { + for (uint8_t step = 0; step < 4; step++) + { + data[(op * 21) + DEXED_OP_EG_R1 + step] = rate; + } + } + doRefreshVoice(); +} + +void Dexed::setOPLevelAll(uint8_t level) +{ + level = constrain(level, 0, 99); + + for (uint8_t op = 0; op < 6; op++) + { + for (uint8_t step = 0; step < 4; step++) + { + data[(op * 21) + DEXED_OP_EG_L1 + step] = level; + } + } + doRefreshVoice(); +} + +void Dexed::setOPRateAllModulator(uint8_t step, uint8_t rate) +{ + uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers + + rate = constrain(rate, 0, 99); + step = constrain(step, 0, 3); + + for (uint8_t op = 0; op < 6; op++) + { + if ((op_carrier & (1 << op)) == 0) + data[(op * 21) + DEXED_OP_EG_R1 + step] = rate; + } + doRefreshVoice(); +} + +void Dexed::setOPLevelAllModulator(uint8_t step, uint8_t level) +{ + uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers + + step = constrain(step, 0, 3); + level = constrain(level, 0, 99); + + for (uint8_t op = 0; op < 6; op++) + { + if ((op_carrier & (1 << op)) == 0) + data[(op * 21) + DEXED_OP_EG_L1 + step] = level; + } + doRefreshVoice(); +} + +void Dexed::setOPRateAllCarrier(uint8_t step, uint8_t rate) +{ + uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers + + rate = constrain(rate, 0, 99); + step = constrain(step, 0, 3); + + for (uint8_t op = 0; op < 6; op++) + { + if ((op_carrier & (1 << op)) == 1) + data[(op * 21) + DEXED_OP_EG_R1 + step] = rate; + } + doRefreshVoice(); +} + +void Dexed::setOPLevelAllCarrier(uint8_t step, uint8_t level) +{ + uint8_t op_carrier = controllers.core->get_carrier_operators(data[134]); // look for carriers + + level = constrain(level, 0, 99); + step = constrain(step, 0, 3); + + for (uint8_t op = 0; op < 6; op++) + { + if ((op_carrier & (1 << op)) == 1) + data[(op * 21) + DEXED_OP_EG_L1 + step] = level; + } + doRefreshVoice(); +} + +void Dexed::setOPRate(uint8_t op, uint8_t step, uint8_t rate) +{ + op = constrain(op, 0, 5); + step = constrain(step, 0, 3); + rate = constrain(rate, 0, 99); + + data[(op * 21) + DEXED_OP_EG_R1 + step] = rate; + doRefreshVoice(); +} + +uint8_t Dexed::getOPRate(uint8_t op, uint8_t step) +{ + op = constrain(op, 0, 5); + step = constrain(step, 0, 3); + + return (data[(op * 21) + DEXED_OP_EG_R1 + step]); +} + +void Dexed::setOPLevel(uint8_t op, uint8_t step, uint8_t level) +{ + op = constrain(op, 0, 5); + step = constrain(step, 0, 3); + level = constrain(level, 0, 99); + + data[(op * 21) + DEXED_OP_EG_L1 + step] = level; + doRefreshVoice(); +} + +uint8_t Dexed::getOPLevel(uint8_t op, uint8_t step) +{ + op = constrain(op, 0, 5); + step = constrain(step, 0, 3); + + return (data[(op * 21) + DEXED_OP_EG_L1 + step]); +} + +void Dexed::setOPKeyboardLevelScalingBreakPoint(uint8_t op, uint8_t level) +{ + op = constrain(op, 0, 5); + level = constrain(level, 0, 99); + + data[(op * 21) + DEXED_OP_LEV_SCL_BRK_PT] = level; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardLevelScalingBreakPoint(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_LEV_SCL_BRK_PT]); +} + +void Dexed::setOPKeyboardLevelScalingDepthLeft(uint8_t op, uint8_t depth) +{ + op = constrain(op, 0, 5); + depth = constrain(depth, 0, 99); + + data[(op * 21) + DEXED_OP_SCL_LEFT_DEPTH] = depth; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardLevelScalingDepthLeft(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_SCL_LEFT_DEPTH]); +} + +void Dexed::setOPKeyboardLevelScalingDepthRight(uint8_t op, uint8_t depth) +{ + op = constrain(op, 0, 5); + depth = constrain(depth, 0, 99); + + data[(op * 21) + DEXED_OP_SCL_RGHT_DEPTH] = depth; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardLevelScalingDepthRight(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_SCL_RGHT_DEPTH]); +} + +void Dexed::setOPKeyboardLevelScalingCurveLeft(uint8_t op, uint8_t curve) +{ + op = constrain(op, 0, 5); + curve = constrain(curve, 0, 3); + + data[(op * 21) + DEXED_OP_SCL_LEFT_CURVE] = curve; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardLevelScalingCurveLeft(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_SCL_LEFT_CURVE]); +} + +void Dexed::setOPKeyboardLevelScalingCurveRight(uint8_t op, uint8_t curve) +{ + op = constrain(op, 0, 5); + curve = constrain(curve, 0, 3); + + data[(op * 21) + DEXED_OP_SCL_RGHT_CURVE] = curve; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardLevelScalingCurveRight(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_SCL_RGHT_CURVE]); +} + +void Dexed::setOPKeyboardRateScale(uint8_t op, uint8_t scale) +{ + op = constrain(op, 0, 5); + scale = constrain(scale, 0, 7); + + data[(op * 21) + DEXED_OP_OSC_RATE_SCALE] = scale; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardRateScale(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_OSC_RATE_SCALE]); +} + +void Dexed::setOPAmpModulationSensity(uint8_t op, uint8_t sensitivity) +{ + op = constrain(op, 0, 5); + sensitivity = constrain(sensitivity, 0, 3); + + data[(op * 21) + DEXED_OP_AMP_MOD_SENS] = sensitivity; + doRefreshVoice(); +} + +uint8_t Dexed::getOPAmpModulationSensity(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_AMP_MOD_SENS]); +} + +void Dexed::setOPKeyboardVelocitySensity(uint8_t op, uint8_t sensitivity) +{ + op = constrain(op, 0, 5); + sensitivity = constrain(sensitivity, 0, 7); + + data[(op * 21) + DEXED_OP_KEY_VEL_SENS] = sensitivity; + doRefreshVoice(); +} + +uint8_t Dexed::getOPKeyboardVelocitySensity(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_KEY_VEL_SENS]); +} + +void Dexed::setOPOutputLevel(uint8_t op, uint8_t level) +{ + op = constrain(op, 0, 5); + level = constrain(level, 0, 99); + + data[(op * 21) + DEXED_OP_OUTPUT_LEV] = level; + doRefreshVoice(); +} + +uint8_t Dexed::getOPOutputLevel(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_OUTPUT_LEV]); +} + +void Dexed::setOPMode(uint8_t op, uint8_t mode) +{ + op = constrain(op, 0, 5); + mode = constrain(mode, 0, 1); + + data[(op * 21) + DEXED_OP_OSC_MODE] = mode; + doRefreshVoice(); +} + +uint8_t Dexed::getOPMode(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_OSC_MODE]); +} + +void Dexed::setOPFrequencyCoarse(uint8_t op, uint8_t frq_coarse) +{ + op = constrain(op, 0, 5); + frq_coarse = constrain(frq_coarse, 0, 31); + + data[(op * 21) + DEXED_OP_FREQ_COARSE] = frq_coarse; + doRefreshVoice(); +} + +uint8_t Dexed::getOPFrequencyCoarse(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_FREQ_COARSE ]); +} + +void Dexed::setOPFrequencyFine(uint8_t op, uint8_t frq_fine) +{ + op = constrain(op, 0, 5); + frq_fine = constrain(frq_fine, 0, 99); + + data[(op * 21) + DEXED_OP_FREQ_FINE] = frq_fine; + doRefreshVoice(); +} + +uint8_t Dexed::getOPFrequencyFine(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_FREQ_FINE]); +} + +void Dexed::setOPDetune(uint8_t op, uint8_t detune) +{ + op = constrain(op, 0, 5); + detune = constrain(detune, 0, 14); + + data[(op * 21) + DEXED_OP_OSC_DETUNE] = detune; + doRefreshVoice(); +} + +uint8_t Dexed::getOPDetune(uint8_t op) +{ + op = constrain(op, 0, 5); + + return (data[(op * 21) + DEXED_OP_OSC_DETUNE]); +} + +void Dexed::setPitchRate(uint8_t step, uint8_t rate) +{ + step = constrain(step, 0, 3); + rate = constrain(rate, 0, 99); + + data[DEXED_VOICE_OFFSET + DEXED_PITCH_EG_R1 + step] = rate; + doRefreshVoice(); +} + +uint8_t Dexed::getPitchRate(uint8_t step) +{ + step = constrain(step, 0, 3); + + return (data[DEXED_VOICE_OFFSET + DEXED_PITCH_EG_R1 + step]); +} + +void Dexed::setPitchLevel(uint8_t step, uint8_t level) +{ + step = constrain(step, 0, 3); + level = constrain(level, 0, 99); + + data[DEXED_VOICE_OFFSET + DEXED_PITCH_EG_L1 + step] = level; + doRefreshVoice(); +} + +uint8_t Dexed::getPitchLevel(uint8_t step) +{ + step = constrain(step, 0, 3); + + return (data[DEXED_VOICE_OFFSET + DEXED_PITCH_EG_L1 + step]); +} + +void Dexed::setAlgorithm(uint8_t algorithm) +{ + algorithm = constrain(algorithm, 0, 31); + + data[DEXED_VOICE_OFFSET + DEXED_ALGORITHM] = algorithm; + doRefreshVoice(); +} + +uint8_t Dexed::getAlgorithm(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_ALGORITHM]); +} + +void Dexed::setFeedback(uint8_t feedback) +{ + feedback = constrain(feedback, 0, 31); + + data[DEXED_VOICE_OFFSET + DEXED_FEEDBACK] = feedback; + doRefreshVoice(); +} + +uint8_t Dexed::getFeedback(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_FEEDBACK]); +} + +void Dexed::setOscillatorSync(bool sync) +{ + data[DEXED_VOICE_OFFSET + DEXED_OSC_KEY_SYNC] = sync; + doRefreshVoice(); +} + +bool Dexed::getOscillatorSync(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_OSC_KEY_SYNC]); +} + +void Dexed::setLFOSpeed(uint8_t speed) +{ + speed = constrain(speed, 0, 99); + + data[DEXED_VOICE_OFFSET + DEXED_LFO_SPEED] = speed; + lfo.reset(data + 137); +} + +uint8_t Dexed::getLFOSpeed(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_SPEED]); +} + +void Dexed::setLFODelay(uint8_t delay) +{ + delay = constrain(delay, 0, 99); + + data[DEXED_VOICE_OFFSET + DEXED_LFO_DELAY] = delay; + lfo.reset(data + 137); +} + +uint8_t Dexed::getLFODelay(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_DELAY]); +} + +void Dexed::setLFOPitchModulationDepth(uint8_t depth) +{ + depth = constrain(depth, 0, 99); + + data[DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_DEP] = depth; + lfo.reset(data + 137); +} + +uint8_t Dexed::getLFOPitchModulationDepth(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_DEP]); +} + +void Dexed::setLFOAmpModulationDepth(uint8_t depth) +{ + depth = constrain(depth, 0, 99); + + data[DEXED_VOICE_OFFSET + DEXED_LFO_AMP_MOD_DEP] = depth; + lfo.reset(data + 137); +} + +uint8_t Dexed::getLFOAmpModulationDepth(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_AMP_MOD_DEP]); +} + +void Dexed::setLFOSync(bool sync) +{ + data[DEXED_VOICE_OFFSET + DEXED_LFO_SYNC] = sync; +} + +bool Dexed::getLFOSync(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_SYNC]); + lfo.reset(data + 137); +} + +void Dexed::setLFOWaveform(uint8_t waveform) +{ + waveform = constrain(waveform, 0, 5); + + data[DEXED_VOICE_OFFSET + DEXED_LFO_WAVE] = waveform; + lfo.reset(data + 137); +} + +uint8_t Dexed::getLFOWaveform(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_WAVE]); +} + +void Dexed::setLFOPitchModulationSensitivity(uint8_t sensitivity) +{ + sensitivity = constrain(sensitivity, 0, 5); + + data[DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_SENS] = sensitivity; + lfo.reset(data + 137); +} + +uint8_t Dexed::getLFOPitchModulationSensitivity(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_SENS]); +} + +void Dexed::setTranspose(uint8_t transpose) +{ + transpose = constrain(transpose, 0, 48); + + data[DEXED_VOICE_OFFSET + DEXED_TRANSPOSE] = transpose; +} + +uint8_t Dexed::getTranspose(void) +{ + return (data[DEXED_VOICE_OFFSET + DEXED_TRANSPOSE]); +} + +void Dexed::setName(char* name) +{ + strncpy(name, (char*)&data[DEXED_VOICE_OFFSET + DEXED_NAME], 10); +} + +void Dexed::getName(char* buffer) +{ + strncpy((char*)&data[DEXED_VOICE_OFFSET + DEXED_NAME], buffer, 10); + buffer[10] = '\0'; +} + +void Dexed::setVelocityScale(uint8_t offset, uint8_t max) +{ + velocity_offset = offset & 0x7f; + velocity_max = max & 0x7f; + velocity_diff = velocity_max - velocity_offset; +} + +void Dexed::getVelocityScale(uint8_t* offset, uint8_t* max) +{ + *offset = velocity_offset; + *max = velocity_max; +} + +void Dexed::setVelocityScale(uint8_t setup = MIDI_VELOCITY_SCALING_OFF) +{ + switch(setup) + { + case MIDI_VELOCITY_SCALING_OFF: + velocity_offset=0; + velocity_max=127; + break; + case MIDI_VELOCITY_SCALING_DX7: + velocity_offset=16; + velocity_max=109; + break; + case MIDI_VELOCITY_SCALING_DX7II: + velocity_offset=6; + velocity_max=119; + break; + default: + velocity_offset=0; + velocity_max=127; + break; + } + setVelocityScale(velocity_offset, velocity_max); +} + +#ifndef TEENSYDUINO +void Dexed::setCompressor(bool enable_compressor) +{ + use_compressor = enable_compressor; +} + +bool Dexed::getCompressor(void) +{ + return (use_compressor); +} + +void Dexed::setCompressorPreGain_dB(float pre_gain) +{ + compressor->setPreGain_dB(pre_gain); +} + +void Dexed::setCompressorAttack_sec(float attack_sec) +{ + compressor->setAttack_sec(attack_sec, samplerate); +} + +void Dexed::setCompressorRelease_sec(float release_sec) +{ + compressor->setRelease_sec(release_sec, samplerate); +} + +void Dexed::setCompressorThresh_dBFS(float thresh_dBFS) +{ + compressor->setThresh_dBFS(thresh_dBFS); +} + +void Dexed::setCompressionRatio(float comp_ratio) +{ + compressor->setCompressionRatio(comp_ratio); +} + +float Dexed::getCompressorPreGain_dB(void) +{ + return (compressor->getPreGain_dB()); +} + +float Dexed::getCompressorAttack_sec(void) +{ + return (compressor->getAttack_sec()); +} + +float Dexed::getCompressorRelease_sec(void) +{ + return (compressor->getRelease_sec()); +} + +float Dexed::getCompressorThresh_dBFS(void) +{ + return (compressor->getThresh_dBFS()); +} + +float Dexed::getCompressionRatio(void) +{ + return (compressor->getCompressionRatio()); +} +#endif diff --git a/third-party/Synth_Dexed/dexed.h b/third-party/Synth_Dexed/dexed.h new file mode 100644 index 0000000..bc34590 --- /dev/null +++ b/third-party/Synth_Dexed/dexed.h @@ -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 + + 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 +#include +#include +#if defined(TEENSYDUINO) +#include +#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 diff --git a/third-party/Synth_Dexed/dx7note.cpp b/third-party/Synth_Dexed/dx7note.cpp new file mode 100644 index 0000000..b5ed6db --- /dev/null +++ b/third-party/Synth_Dexed/dx7note.cpp @@ -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 +#include +#include "synth.h" +#include "freqlut.h" +#include "exp2.h" +#include "controllers.h" +#include "dx7note.h" +#include "porta.h" +//#include + +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; + } +} diff --git a/third-party/Synth_Dexed/dx7note.h b/third-party/Synth_Dexed/dx7note.h new file mode 100644 index 0000000..e66f4b6 --- /dev/null +++ b/third-party/Synth_Dexed/dx7note.h @@ -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 +#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 diff --git a/third-party/Synth_Dexed/env.cpp b/third-party/Synth_Dexed/env.cpp new file mode 100644 index 0000000..7a09def --- /dev/null +++ b/third-party/Synth_Dexed/env.cpp @@ -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 + +#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_; +} diff --git a/third-party/Synth_Dexed/env.h b/third-party/Synth_Dexed/env.h new file mode 100644 index 0000000..0637ac9 --- /dev/null +++ b/third-party/Synth_Dexed/env.h @@ -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 diff --git a/third-party/Synth_Dexed/exp2.cpp b/third-party/Synth_Dexed/exp2.cpp new file mode 100644 index 0000000..12c9223 --- /dev/null +++ b/third-party/Synth_Dexed/exp2.cpp @@ -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 + +#include "synth.h" +#include "exp2.h" + +//#include + +#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]; +} diff --git a/third-party/Synth_Dexed/exp2.h b/third-party/Synth_Dexed/exp2.h new file mode 100644 index 0000000..957642a --- /dev/null +++ b/third-party/Synth_Dexed/exp2.h @@ -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; + } +} diff --git a/third-party/Synth_Dexed/fm_core.cpp b/third-party/Synth_Dexed/fm_core.cpp new file mode 100644 index 0000000..af46f6e --- /dev/null +++ b/third-party/Synth_Dexed/fm_core.cpp @@ -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 +} diff --git a/third-party/Synth_Dexed/fm_core.h b/third-party/Synth_Dexed/fm_core.h new file mode 100644 index 0000000..c91f02c --- /dev/null +++ b/third-party/Synth_Dexed/fm_core.h @@ -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 +#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: + AlignedBufbuf_[2]; + const static FmAlgorithm algorithms[32]; +}; + +#endif diff --git a/third-party/Synth_Dexed/fm_op_kernel.cpp b/third-party/Synth_Dexed/fm_op_kernel.cpp new file mode 100644 index 0000000..bfaa702 --- /dev/null +++ b/third-party/Synth_Dexed/fm_op_kernel.cpp @@ -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 +#include +#include +#include "synth.h" +#include "sin.h" +#include "fm_op_kernel.h" + +#ifdef __ARM_NEON +#include +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 diff --git a/third-party/Synth_Dexed/fm_op_kernel.h b/third-party/Synth_Dexed/fm_op_kernel.h new file mode 100644 index 0000000..e624272 --- /dev/null +++ b/third-party/Synth_Dexed/fm_op_kernel.h @@ -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 diff --git a/third-party/Synth_Dexed/freqlut.cpp b/third-party/Synth_Dexed/freqlut.cpp new file mode 100644 index 0000000..49981a5 --- /dev/null +++ b/third-party/Synth_Dexed/freqlut.cpp @@ -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 +#include + +#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); +} diff --git a/third-party/Synth_Dexed/freqlut.h b/third-party/Synth_Dexed/freqlut.h new file mode 100644 index 0000000..83ad3c7 --- /dev/null +++ b/third-party/Synth_Dexed/freqlut.h @@ -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); +}; diff --git a/third-party/Synth_Dexed/lfo.cpp b/third-party/Synth_Dexed/lfo.cpp new file mode 100644 index 0000000..2795049 --- /dev/null +++ b/third-party/Synth_Dexed/lfo.cpp @@ -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 + +#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; +} diff --git a/third-party/Synth_Dexed/lfo.h b/third-party/Synth_Dexed/lfo.h new file mode 100644 index 0000000..3a063c1 --- /dev/null +++ b/third-party/Synth_Dexed/lfo.h @@ -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_; +}; diff --git a/third-party/Synth_Dexed/pitchenv.cpp b/third-party/Synth_Dexed/pitchenv.cpp new file mode 100644 index 0000000..2e5a363 --- /dev/null +++ b/third-party/Synth_Dexed/pitchenv.cpp @@ -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_; +} diff --git a/third-party/Synth_Dexed/pitchenv.h b/third-party/Synth_Dexed/pitchenv.h new file mode 100644 index 0000000..6877a77 --- /dev/null +++ b/third-party/Synth_Dexed/pitchenv.h @@ -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 diff --git a/third-party/Synth_Dexed/porta.cpp b/third-party/Synth_Dexed/porta.cpp new file mode 100644 index 0000000..53ab2f9 --- /dev/null +++ b/third-party/Synth_Dexed/porta.cpp @@ -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 +#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]; diff --git a/third-party/Synth_Dexed/porta.h b/third-party/Synth_Dexed/porta.h new file mode 100644 index 0000000..1245ea8 --- /dev/null +++ b/third-party/Synth_Dexed/porta.h @@ -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 + +struct Porta { + public: + static void init_sr(double sampleRate); + static int32_t rates[128]; +}; + +#endif diff --git a/third-party/Synth_Dexed/sin.cpp b/third-party/Synth_Dexed/sin.cpp new file mode 100644 index 0000000..d965011 --- /dev/null +++ b/third-party/Synth_Dexed/sin.cpp @@ -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 + +#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; +} diff --git a/third-party/Synth_Dexed/sin.h b/third-party/Synth_Dexed/sin.h new file mode 100644 index 0000000..0ac8a81 --- /dev/null +++ b/third-party/Synth_Dexed/sin.h @@ -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 diff --git a/third-party/Synth_Dexed/src/EngineMkI.cpp b/third-party/Synth_Dexed/src/EngineMkI.cpp index 5fe725c..be6cc2b 100644 --- a/third-party/Synth_Dexed/src/EngineMkI.cpp +++ b/third-party/Synth_Dexed/src/EngineMkI.cpp @@ -69,23 +69,23 @@ static inline uint16_t sinLog(uint16_t phi) { EngineMkI::EngineMkI() { float bitReso = SINLOG_TABLESIZE; - for(int i=0;i 90 ) { TRACE("SINLOGTABLE: %s" ,buffer); @@ -97,7 +97,7 @@ EngineMkI::EngineMkI() { buffer[0] = 0; pos = 0; TRACE("----------------------------------------"); - for(int i=0;i 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 ¶m = 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 diff --git a/third-party/Synth_Dexed/src/EngineMkI.h b/third-party/Synth_Dexed/src/EngineMkI.h index b0762f5..dcafa59 100644 --- a/third-party/Synth_Dexed/src/EngineMkI.h +++ b/third-party/Synth_Dexed/src/EngineMkI.h @@ -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); }; diff --git a/third-party/Synth_Dexed/src/EngineMsfa.cpp b/third-party/Synth_Dexed/src/EngineMsfa.cpp index 6a7dde3..e947b48 100644 --- a/third-party/Synth_Dexed/src/EngineMsfa.cpp +++ b/third-party/Synth_Dexed/src/EngineMsfa.cpp @@ -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 ¶m = 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))); diff --git a/third-party/Synth_Dexed/src/EngineMsfa.h b/third-party/Synth_Dexed/src/EngineMsfa.h index d12fa94..c3cd9e2 100644 --- a/third-party/Synth_Dexed/src/EngineMsfa.h +++ b/third-party/Synth_Dexed/src/EngineMsfa.h @@ -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; }; diff --git a/third-party/Synth_Dexed/src/EngineOpl.cpp b/third-party/Synth_Dexed/src/EngineOpl.cpp index c6d4c3b..3f52f6a 100644 --- a/third-party/Synth_Dexed/src/EngineOpl.cpp +++ b/third-party/Synth_Dexed/src/EngineOpl.cpp @@ -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 ¶m = 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; } } - - - - - - - - - - - - diff --git a/third-party/Synth_Dexed/src/EngineOpl.h b/third-party/Synth_Dexed/src/EngineOpl.h index fe7f20b..2d2b03d 100644 --- a/third-party/Synth_Dexed/src/EngineOpl.h +++ b/third-party/Synth_Dexed/src/EngineOpl.h @@ -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 diff --git a/third-party/Synth_Dexed/src/PluginFx.cpp b/third-party/Synth_Dexed/src/PluginFx.cpp index ec1eb63..5d9df3b 100644 --- a/third-party/Synth_Dexed/src/PluginFx.cpp +++ b/third-party/Synth_Dexed/src/PluginFx.cpp @@ -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); diff --git a/third-party/Synth_Dexed/src/PluginFx.h b/third-party/Synth_Dexed/src/PluginFx.h index 1dc4a9d..f31e208 100644 --- a/third-party/Synth_Dexed/src/PluginFx.h +++ b/third-party/Synth_Dexed/src/PluginFx.h @@ -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 diff --git a/third-party/Synth_Dexed/src/compressor.h b/third-party/Synth_Dexed/src/compressor.h index 7f0337f..30047fb 100644 --- a/third-party/Synth_Dexed/src/compressor.h +++ b/third-party/Synth_Dexed/src/compressor.h @@ -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); diff --git a/third-party/Synth_Dexed/src/controllers.h b/third-party/Synth_Dexed/src/controllers.h index 59576be..5cd72d6 100644 --- a/third-party/Synth_Dexed/src/controllers.h +++ b/third-party/Synth_Dexed/src/controllers.h @@ -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; diff --git a/third-party/Synth_Dexed/src/dexed.cpp b/third-party/Synth_Dexed/src/dexed.cpp index cf501f8..39ae543 100644 --- a/third-party/Synth_Dexed/src/dexed.cpp +++ b/third-party/Synth_Dexed/src/dexed.cpp @@ -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; + panic(); + 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()); } diff --git a/third-party/Synth_Dexed/src/dexed.h b/third-party/Synth_Dexed/src/dexed.h index 8d91150..bc34590 100644 --- a/third-party/Synth_Dexed/src/dexed.h +++ b/third-party/Synth_Dexed/src/dexed.h @@ -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; diff --git a/third-party/Synth_Dexed/src/dx7note.cpp b/third-party/Synth_Dexed/src/dx7note.cpp index d4e653b..b5ed6db 100644 --- a/third-party/Synth_Dexed/src/dx7note.cpp +++ b/third-party/Synth_Dexed/src/dx7note.cpp @@ -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]; diff --git a/third-party/Synth_Dexed/src/dx7note.h b/third-party/Synth_Dexed/src/dx7note.h index a21f541..e66f4b6 100644 --- a/third-party/Synth_Dexed/src/dx7note.h +++ b/third-party/Synth_Dexed/src/dx7note.h @@ -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]; diff --git a/third-party/Synth_Dexed/src/env.cpp b/third-party/Synth_Dexed/src/env.cpp index 31662ed..7a09def 100644 --- a/third-party/Synth_Dexed/src/env.cpp +++ b/third-party/Synth_Dexed/src/env.cpp @@ -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; diff --git a/third-party/Synth_Dexed/src/env.h b/third-party/Synth_Dexed/src/env.h index 3af98fa..0637ac9 100644 --- a/third-party/Synth_Dexed/src/env.h +++ b/third-party/Synth_Dexed/src/env.h @@ -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_; diff --git a/third-party/Synth_Dexed/src/exp2.h b/third-party/Synth_Dexed/src/exp2.h index 29dc298..957642a 100644 --- a/third-party/Synth_Dexed/src/exp2.h +++ b/third-party/Synth_Dexed/src/exp2.h @@ -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; } } diff --git a/third-party/Synth_Dexed/src/fm_core.cpp b/third-party/Synth_Dexed/src/fm_core.cpp index 780dd03..af46f6e 100644 --- a/third-party/Synth_Dexed/src/fm_core.cpp +++ b/third-party/Synth_Dexed/src/fm_core.cpp @@ -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") << "->"; diff --git a/third-party/Synth_Dexed/src/fm_core.h b/third-party/Synth_Dexed/src/fm_core.h index 8d23de1..c91f02c 100644 --- a/third-party/Synth_Dexed/src/fm_core.h +++ b/third-party/Synth_Dexed/src/fm_core.h @@ -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 { diff --git a/third-party/Synth_Dexed/src/fm_op_kernel.cpp b/third-party/Synth_Dexed/src/fm_op_kernel.cpp index b1834be..bfaa702 100644 --- a/third-party/Synth_Dexed/src/fm_op_kernel.cpp +++ b/third-party/Synth_Dexed/src/fm_op_kernel.cpp @@ -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; diff --git a/third-party/Synth_Dexed/src/fm_op_kernel.h b/third-party/Synth_Dexed/src/fm_op_kernel.h index d164bc2..e624272 100644 --- a/third-party/Synth_Dexed/src/fm_op_kernel.h +++ b/third-party/Synth_Dexed/src/fm_op_kernel.h @@ -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 diff --git a/third-party/Synth_Dexed/src/freqlut.cpp b/third-party/Synth_Dexed/src/freqlut.cpp index 0673ae7..49981a5 100644 --- a/third-party/Synth_Dexed/src/freqlut.cpp +++ b/third-party/Synth_Dexed/src/freqlut.cpp @@ -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); } diff --git a/third-party/Synth_Dexed/src/lfo.cpp b/third-party/Synth_Dexed/src/lfo.cpp index 33f1aa3..2795049 100644 --- a/third-party/Synth_Dexed/src/lfo.cpp +++ b/third-party/Synth_Dexed/src/lfo.cpp @@ -16,6 +16,8 @@ // Low frequency oscillator, compatible with DX7 +#include + #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]; diff --git a/third-party/Synth_Dexed/src/pitchenv.cpp b/third-party/Synth_Dexed/src/pitchenv.cpp index d7f53bd..2e5a363 100644 --- a/third-party/Synth_Dexed/src/pitchenv.cpp +++ b/third-party/Synth_Dexed/src/pitchenv.cpp @@ -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_; diff --git a/third-party/Synth_Dexed/src/pitchenv.h b/third-party/Synth_Dexed/src/pitchenv.h index f74a322..6877a77 100644 --- a/third-party/Synth_Dexed/src/pitchenv.h +++ b/third-party/Synth_Dexed/src/pitchenv.h @@ -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_; diff --git a/third-party/Synth_Dexed/src/porta.cpp b/third-party/Synth_Dexed/src/porta.cpp index 059c06b..53ab2f9 100644 --- a/third-party/Synth_Dexed/src/porta.cpp +++ b/third-party/Synth_Dexed/src/porta.cpp @@ -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 } } diff --git a/third-party/Synth_Dexed/src/sin.cpp b/third-party/Synth_Dexed/src/sin.cpp index 1949cd5..d965011 100644 --- a/third-party/Synth_Dexed/src/sin.cpp +++ b/third-party/Synth_Dexed/src/sin.cpp @@ -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 diff --git a/third-party/Synth_Dexed/src/sin.h b/third-party/Synth_Dexed/src/sin.h index 85eccd1..0ac8a81 100644 --- a/third-party/Synth_Dexed/src/sin.h +++ b/third-party/Synth_Dexed/src/sin.h @@ -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 diff --git a/third-party/Synth_Dexed/src/synth.h b/third-party/Synth_Dexed/src/synth.h index 07804e1..e72e577 100644 --- a/third-party/Synth_Dexed/src/synth.h +++ b/third-party/Synth_Dexed/src/synth.h @@ -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 +/*template 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 inline static T max(const T& a, const T& b) { return a > b ? a : b; -} +}*/ #define QER(n,b) ( ((float)n)/(1< _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; diff --git a/third-party/Synth_Dexed/src/synth_dexed.h b/third-party/Synth_Dexed/src/synth_dexed.h index c48854d..40669d1 100644 --- a/third-party/Synth_Dexed/src/synth_dexed.h +++ b/third-party/Synth_Dexed/src/synth_dexed.h @@ -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 diff --git a/third-party/Synth_Dexed/synth.h b/third-party/Synth_Dexed/synth.h new file mode 100644 index 0000000..e72e577 --- /dev/null +++ b/third-party/Synth_Dexed/synth.h @@ -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 + +#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 +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +template +inline static T max(const T& a, const T& b) { + return a > b ? a : b; +}*/ + +#define QER(n,b) ( ((float)n)/(1< + +#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 diff --git a/third-party/Synth_Dexed/synth_dexed.cpp b/third-party/Synth_Dexed/synth_dexed.cpp new file mode 100644 index 0000000..31a3710 --- /dev/null +++ b/third-party/Synth_Dexed/synth_dexed.cpp @@ -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 + + 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 diff --git a/third-party/Synth_Dexed/synth_dexed.h b/third-party/Synth_Dexed/synth_dexed.h new file mode 100644 index 0000000..40669d1 --- /dev/null +++ b/third-party/Synth_Dexed/synth_dexed.h @@ -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 + + 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 +#endif +#include + +#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 diff --git a/third-party/TeensyVariablePlayback/.github/FUNDING.yml b/third-party/teensy-variable-playback/.github/FUNDING.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/FUNDING.yml rename to third-party/teensy-variable-playback/.github/FUNDING.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/soundio.yml b/third-party/teensy-variable-playback/.github/workflows/soundio.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/soundio.yml rename to third-party/teensy-variable-playback/.github/workflows/soundio.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_array.yml b/third-party/teensy-variable-playback/.github/workflows/teensy41_ex_array.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_array.yml rename to third-party/teensy-variable-playback/.github/workflows/teensy41_ex_array.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_littlefs.yml b/third-party/teensy-variable-playback/.github/workflows/teensy41_ex_littlefs.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_littlefs.yml rename to third-party/teensy-variable-playback/.github/workflows/teensy41_ex_littlefs.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_sdraw.yml b/third-party/teensy-variable-playback/.github/workflows/teensy41_ex_sdraw.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_sdraw.yml rename to third-party/teensy-variable-playback/.github/workflows/teensy41_ex_sdraw.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_sdwav.yml b/third-party/teensy-variable-playback/.github/workflows/teensy41_ex_sdwav.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_sdwav.yml rename to third-party/teensy-variable-playback/.github/workflows/teensy41_ex_sdwav.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_serialflash.yml b/third-party/teensy-variable-playback/.github/workflows/teensy41_ex_serialflash.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/teensy41_ex_serialflash.yml rename to third-party/teensy-variable-playback/.github/workflows/teensy41_ex_serialflash.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/teensy41_lib.yml b/third-party/teensy-variable-playback/.github/workflows/teensy41_lib.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/teensy41_lib.yml rename to third-party/teensy-variable-playback/.github/workflows/teensy41_lib.yml diff --git a/third-party/TeensyVariablePlayback/.github/workflows/ubuntu_x64_cmake.yml b/third-party/teensy-variable-playback/.github/workflows/ubuntu_x64_cmake.yml similarity index 100% rename from third-party/TeensyVariablePlayback/.github/workflows/ubuntu_x64_cmake.yml rename to third-party/teensy-variable-playback/.github/workflows/ubuntu_x64_cmake.yml diff --git a/third-party/TeensyVariablePlayback/.gitignore b/third-party/teensy-variable-playback/.gitignore similarity index 100% rename from third-party/TeensyVariablePlayback/.gitignore rename to third-party/teensy-variable-playback/.gitignore diff --git a/third-party/TeensyVariablePlayback/CMakeLists.txt b/third-party/teensy-variable-playback/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/CMakeLists.txt rename to third-party/teensy-variable-playback/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/LICENSE b/third-party/teensy-variable-playback/LICENSE similarity index 100% rename from third-party/TeensyVariablePlayback/LICENSE rename to third-party/teensy-variable-playback/LICENSE diff --git a/third-party/TeensyVariablePlayback/README.md b/third-party/teensy-variable-playback/README.md similarity index 100% rename from third-party/TeensyVariablePlayback/README.md rename to third-party/teensy-variable-playback/README.md diff --git a/third-party/TeensyVariablePlayback/build-linux.sh b/third-party/teensy-variable-playback/build-linux.sh similarity index 100% rename from third-party/TeensyVariablePlayback/build-linux.sh rename to third-party/teensy-variable-playback/build-linux.sh diff --git a/third-party/TeensyVariablePlayback/build-t41.sh b/third-party/teensy-variable-playback/build-t41.sh similarity index 100% rename from third-party/TeensyVariablePlayback/build-t41.sh rename to third-party/teensy-variable-playback/build-t41.sh diff --git a/third-party/TeensyVariablePlayback/cmake/install_arduino_library.cmake b/third-party/teensy-variable-playback/cmake/install_arduino_library.cmake similarity index 100% rename from third-party/TeensyVariablePlayback/cmake/install_arduino_library.cmake rename to third-party/teensy-variable-playback/cmake/install_arduino_library.cmake diff --git a/third-party/TeensyVariablePlayback/cmake/teensy_variable_playback.cmake.in b/third-party/teensy-variable-playback/cmake/teensy_variable_playback.cmake.in similarity index 100% rename from third-party/TeensyVariablePlayback/cmake/teensy_variable_playback.cmake.in rename to third-party/teensy-variable-playback/cmake/teensy_variable_playback.cmake.in diff --git a/third-party/TeensyVariablePlayback/cmake/toolchains/teensy41.cmake b/third-party/teensy-variable-playback/cmake/toolchains/teensy41.cmake similarity index 100% rename from third-party/TeensyVariablePlayback/cmake/toolchains/teensy41.cmake rename to third-party/teensy-variable-playback/cmake/toolchains/teensy41.cmake diff --git a/third-party/TeensyVariablePlayback/cmake/uninstall.cmake b/third-party/teensy-variable-playback/cmake/uninstall.cmake similarity index 100% rename from third-party/TeensyVariablePlayback/cmake/uninstall.cmake rename to third-party/teensy-variable-playback/cmake/uninstall.cmake diff --git a/third-party/TeensyVariablePlayback/docs/InstallArduino.gif b/third-party/teensy-variable-playback/docs/InstallArduino.gif similarity index 100% rename from third-party/TeensyVariablePlayback/docs/InstallArduino.gif rename to third-party/teensy-variable-playback/docs/InstallArduino.gif diff --git a/third-party/TeensyVariablePlayback/docs/dependencies.png b/third-party/teensy-variable-playback/docs/dependencies.png similarity index 100% rename from third-party/TeensyVariablePlayback/docs/dependencies.png rename to third-party/teensy-variable-playback/docs/dependencies.png diff --git a/third-party/TeensyVariablePlayback/examples/CMakeLists.txt b/third-party/teensy-variable-playback/examples/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/LittleFS/CMakeLists.txt b/third-party/teensy-variable-playback/examples/LittleFS/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/LittleFS/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/LittleFS/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/LittleFS/littlefs_raw.ino b/third-party/teensy-variable-playback/examples/LittleFS/littlefs_raw.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/LittleFS/littlefs_raw.ino rename to third-party/teensy-variable-playback/examples/LittleFS/littlefs_raw.ino diff --git a/third-party/TeensyVariablePlayback/examples/SerialFlash/CMakeLists.txt b/third-party/teensy-variable-playback/examples/SerialFlash/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/SerialFlash/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/SerialFlash/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/SerialFlash/serialflash.ino b/third-party/teensy-variable-playback/examples/SerialFlash/serialflash.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/SerialFlash/serialflash.ino rename to third-party/teensy-variable-playback/examples/SerialFlash/serialflash.ino diff --git a/third-party/TeensyVariablePlayback/examples/array/CMakeLists.txt b/third-party/teensy-variable-playback/examples/array/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/array/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/array/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/array/array.ino b/third-party/teensy-variable-playback/examples/array/array.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/array/array.ino rename to third-party/teensy-variable-playback/examples/array/array.ino diff --git a/third-party/TeensyVariablePlayback/examples/midilooper/CMakeLists.txt b/third-party/teensy-variable-playback/examples/midilooper/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/midilooper/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/midilooper/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/midilooper/midilooper.ino b/third-party/teensy-variable-playback/examples/midilooper/midilooper.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/midilooper/midilooper.ino rename to third-party/teensy-variable-playback/examples/midilooper/midilooper.ino diff --git a/third-party/TeensyVariablePlayback/examples/sampleloader/CMakeLists.txt b/third-party/teensy-variable-playback/examples/sampleloader/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sampleloader/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/sampleloader/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/sampleloader/README.md b/third-party/teensy-variable-playback/examples/sampleloader/README.md similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sampleloader/README.md rename to third-party/teensy-variable-playback/examples/sampleloader/README.md diff --git a/third-party/TeensyVariablePlayback/examples/sampleloader/sampleloader.ino b/third-party/teensy-variable-playback/examples/sampleloader/sampleloader.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sampleloader/sampleloader.ino rename to third-party/teensy-variable-playback/examples/sampleloader/sampleloader.ino diff --git a/third-party/TeensyVariablePlayback/examples/sd_play_all/CMakeLists.txt b/third-party/teensy-variable-playback/examples/sd_play_all/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sd_play_all/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/sd_play_all/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/sd_play_all/sd_play_all.ino b/third-party/teensy-variable-playback/examples/sd_play_all/sd_play_all.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sd_play_all/sd_play_all.ino rename to third-party/teensy-variable-playback/examples/sd_play_all/sd_play_all.ino diff --git a/third-party/TeensyVariablePlayback/examples/sd_raw/CMakeLists.txt b/third-party/teensy-variable-playback/examples/sd_raw/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sd_raw/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/sd_raw/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/sd_raw/sd_raw.ino b/third-party/teensy-variable-playback/examples/sd_raw/sd_raw.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sd_raw/sd_raw.ino rename to third-party/teensy-variable-playback/examples/sd_raw/sd_raw.ino diff --git a/third-party/TeensyVariablePlayback/examples/sd_wav/CMakeLists.txt b/third-party/teensy-variable-playback/examples/sd_wav/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sd_wav/CMakeLists.txt rename to third-party/teensy-variable-playback/examples/sd_wav/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/examples/sd_wav/sd_wav.ino b/third-party/teensy-variable-playback/examples/sd_wav/sd_wav.ino similarity index 100% rename from third-party/TeensyVariablePlayback/examples/sd_wav/sd_wav.ino rename to third-party/teensy-variable-playback/examples/sd_wav/sd_wav.ino diff --git a/third-party/TeensyVariablePlayback/extras/linux/array/CMakeLists.txt b/third-party/teensy-variable-playback/extras/linux/array/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/extras/linux/array/CMakeLists.txt rename to third-party/teensy-variable-playback/extras/linux/array/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/extras/linux/array/array.cpp b/third-party/teensy-variable-playback/extras/linux/array/array.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/extras/linux/array/array.cpp rename to third-party/teensy-variable-playback/extras/linux/array/array.cpp diff --git a/third-party/TeensyVariablePlayback/extras/linux/sd_raw/CMakeLists.txt b/third-party/teensy-variable-playback/extras/linux/sd_raw/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/extras/linux/sd_raw/CMakeLists.txt rename to third-party/teensy-variable-playback/extras/linux/sd_raw/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/extras/linux/sd_raw/sd_raw.cpp b/third-party/teensy-variable-playback/extras/linux/sd_raw/sd_raw.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/extras/linux/sd_raw/sd_raw.cpp rename to third-party/teensy-variable-playback/extras/linux/sd_raw/sd_raw.cpp diff --git a/third-party/TeensyVariablePlayback/extras/soundio/save_raw/CMakeLists.txt b/third-party/teensy-variable-playback/extras/soundio/save_raw/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/extras/soundio/save_raw/CMakeLists.txt rename to third-party/teensy-variable-playback/extras/soundio/save_raw/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/extras/soundio/save_raw/save_raw.cpp b/third-party/teensy-variable-playback/extras/soundio/save_raw/save_raw.cpp similarity index 99% rename from third-party/TeensyVariablePlayback/extras/soundio/save_raw/save_raw.cpp rename to third-party/teensy-variable-playback/extras/soundio/save_raw/save_raw.cpp index 1cfbdd6..2594680 100644 --- a/third-party/TeensyVariablePlayback/extras/soundio/save_raw/save_raw.cpp +++ b/third-party/teensy-variable-playback/extras/soundio/save_raw/save_raw.cpp @@ -124,7 +124,7 @@ void my_handler(sig_atomic_t i){ printf("Caught signal %d\n",i); } else { - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -133,15 +133,15 @@ void my_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } } void crash_handler(sig_atomic_t i){ - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -150,9 +150,9 @@ void crash_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } diff --git a/third-party/TeensyVariablePlayback/extras/soundio/save_raw_sd/CMakeLists.txt b/third-party/teensy-variable-playback/extras/soundio/save_raw_sd/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/extras/soundio/save_raw_sd/CMakeLists.txt rename to third-party/teensy-variable-playback/extras/soundio/save_raw_sd/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/extras/soundio/save_raw_sd/save_raw_sd.cpp b/third-party/teensy-variable-playback/extras/soundio/save_raw_sd/save_raw_sd.cpp similarity index 99% rename from third-party/TeensyVariablePlayback/extras/soundio/save_raw_sd/save_raw_sd.cpp rename to third-party/teensy-variable-playback/extras/soundio/save_raw_sd/save_raw_sd.cpp index 5741e71..b50701b 100644 --- a/third-party/TeensyVariablePlayback/extras/soundio/save_raw_sd/save_raw_sd.cpp +++ b/third-party/teensy-variable-playback/extras/soundio/save_raw_sd/save_raw_sd.cpp @@ -133,7 +133,7 @@ void my_handler(sig_atomic_t i){ printf("Caught signal %d\n",i); } else { - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -142,15 +142,15 @@ void my_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } } void crash_handler(sig_atomic_t i){ - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -159,9 +159,9 @@ void crash_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } diff --git a/third-party/TeensyVariablePlayback/extras/soundio/save_wav/CMakeLists.txt b/third-party/teensy-variable-playback/extras/soundio/save_wav/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/extras/soundio/save_wav/CMakeLists.txt rename to third-party/teensy-variable-playback/extras/soundio/save_wav/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/extras/soundio/save_wav/save_wav.cpp b/third-party/teensy-variable-playback/extras/soundio/save_wav/save_wav.cpp similarity index 92% rename from third-party/TeensyVariablePlayback/extras/soundio/save_wav/save_wav.cpp rename to third-party/teensy-variable-playback/extras/soundio/save_wav/save_wav.cpp index fb70fdc..8a20eea 100644 --- a/third-party/TeensyVariablePlayback/extras/soundio/save_wav/save_wav.cpp +++ b/third-party/teensy-variable-playback/extras/soundio/save_wav/save_wav.cpp @@ -118,7 +118,7 @@ void my_handler(sig_atomic_t i){ printf("Caught signal %d\n",i); } else { - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -127,15 +127,15 @@ void my_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } } void crash_handler(sig_atomic_t i){ - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -144,9 +144,9 @@ void crash_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } diff --git a/third-party/TeensyVariablePlayback/extras/soundio/sd_play_all/CMakeLists.txt b/third-party/teensy-variable-playback/extras/soundio/sd_play_all/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/extras/soundio/sd_play_all/CMakeLists.txt rename to third-party/teensy-variable-playback/extras/soundio/sd_play_all/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/extras/soundio/sd_play_all/sd_play_all.cpp b/third-party/teensy-variable-playback/extras/soundio/sd_play_all/sd_play_all.cpp similarity index 99% rename from third-party/TeensyVariablePlayback/extras/soundio/sd_play_all/sd_play_all.cpp rename to third-party/teensy-variable-playback/extras/soundio/sd_play_all/sd_play_all.cpp index f8fba59..a3e66de 100644 --- a/third-party/TeensyVariablePlayback/extras/soundio/sd_play_all/sd_play_all.cpp +++ b/third-party/teensy-variable-playback/extras/soundio/sd_play_all/sd_play_all.cpp @@ -216,7 +216,7 @@ void my_handler(sig_atomic_t i){ printf("Caught signal %d\n",i); } else { - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -225,15 +225,15 @@ void my_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } } void crash_handler(sig_atomic_t i){ - cerr << "sig seg fault handler" << endl; + std::cerr << "sig seg fault handler" << std::endl; const int asize = 10; void *array[asize]; size_t size; @@ -242,9 +242,9 @@ void crash_handler(sig_atomic_t i){ size = backtrace(array, asize); // print out all the frames to stderr - cerr << "stack trace: " << endl; + std::cerr << "stack trace: " << std::endl; backtrace_symbols_fd(array, size, STDERR_FILENO); - cerr << "resend SIGSEGV to get core dump" << endl; + std::cerr << "resend SIGSEGV to get core dump" << std::endl; signal(i, SIG_DFL); kill(getpid(), i); } diff --git a/third-party/TeensyVariablePlayback/keywords.txt b/third-party/teensy-variable-playback/keywords.txt similarity index 100% rename from third-party/TeensyVariablePlayback/keywords.txt rename to third-party/teensy-variable-playback/keywords.txt diff --git a/third-party/TeensyVariablePlayback/library.json b/third-party/teensy-variable-playback/library.json similarity index 100% rename from third-party/TeensyVariablePlayback/library.json rename to third-party/teensy-variable-playback/library.json diff --git a/third-party/TeensyVariablePlayback/library.properties b/third-party/teensy-variable-playback/library.properties similarity index 100% rename from third-party/TeensyVariablePlayback/library.properties rename to third-party/teensy-variable-playback/library.properties diff --git a/third-party/TeensyVariablePlayback/src/CMakeLists.linux.cmake.in b/third-party/teensy-variable-playback/src/CMakeLists.linux.cmake.in similarity index 100% rename from third-party/TeensyVariablePlayback/src/CMakeLists.linux.cmake.in rename to third-party/teensy-variable-playback/src/CMakeLists.linux.cmake.in diff --git a/third-party/TeensyVariablePlayback/src/CMakeLists.txt b/third-party/teensy-variable-playback/src/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/src/CMakeLists.txt rename to third-party/teensy-variable-playback/src/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/src/IndexableFile.h b/third-party/teensy-variable-playback/src/IndexableFile.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/IndexableFile.h rename to third-party/teensy-variable-playback/src/IndexableFile.h index a4d4ea6..cc8c9fd 100644 --- a/third-party/TeensyVariablePlayback/src/IndexableFile.h +++ b/third-party/teensy-variable-playback/src/IndexableFile.h @@ -20,6 +20,20 @@ constexpr bool isPowerOf2(size_t value){ template // BUFFER_SIZE needs to be a power of two class IndexableFile { +protected: + TFile _file; + char *_filename; + std::vector _buffers; + + indexedbuffer* find_with_index(uint32_t i) { + for (auto && x : _buffers){ + if (x->index == i) { + return x; + } + } + return nullptr; + } + public: static_assert(isPowerOf2(BUFFER_SIZE), "BUFFER_SIZE must be a power of 2"); @@ -85,20 +99,6 @@ public: _filename = nullptr; } } - -protected: - TFile _file; - char *_filename; - std::vector _buffers; - - indexedbuffer* find_with_index(uint32_t i) { - for (auto && x : _buffers){ - if (x->index == i) { - return x; - } - } - return nullptr; - } }; } diff --git a/third-party/TeensyVariablePlayback/src/IndexableLittleFSFile.h b/third-party/teensy-variable-playback/src/IndexableLittleFSFile.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/IndexableLittleFSFile.h rename to third-party/teensy-variable-playback/src/IndexableLittleFSFile.h diff --git a/third-party/TeensyVariablePlayback/src/IndexableSDFile.h b/third-party/teensy-variable-playback/src/IndexableSDFile.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/IndexableSDFile.h rename to third-party/teensy-variable-playback/src/IndexableSDFile.h diff --git a/third-party/TeensyVariablePlayback/src/IndexableSerialFlashFile.h b/third-party/teensy-variable-playback/src/IndexableSerialFlashFile.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/IndexableSerialFlashFile.h rename to third-party/teensy-variable-playback/src/IndexableSerialFlashFile.h diff --git a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h b/third-party/teensy-variable-playback/src/ResamplingArrayReader.h similarity index 95% rename from third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h rename to third-party/teensy-variable-playback/src/ResamplingArrayReader.h index 5e87fb2..d7b122b 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingArrayReader.h +++ b/third-party/teensy-variable-playback/src/ResamplingArrayReader.h @@ -34,7 +34,6 @@ public: void close(void) override { if (_playing) { stop(); - deleteInterpolationPoints(); } } diff --git a/third-party/TeensyVariablePlayback/src/ResamplingLfsReader.h b/third-party/teensy-variable-playback/src/ResamplingLfsReader.h similarity index 98% rename from third-party/TeensyVariablePlayback/src/ResamplingLfsReader.h rename to third-party/teensy-variable-playback/src/ResamplingLfsReader.h index 606286e..674a44c 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingLfsReader.h +++ b/third-party/teensy-variable-playback/src/ResamplingLfsReader.h @@ -52,7 +52,6 @@ public: delete [] _filename; _filename = nullptr; } - deleteInterpolationPoints(); } IndexableLittleFSFile<128, 2>* createSourceBuffer() override { diff --git a/third-party/TeensyVariablePlayback/src/ResamplingReader.h b/third-party/teensy-variable-playback/src/ResamplingReader.h similarity index 95% rename from third-party/TeensyVariablePlayback/src/ResamplingReader.h rename to third-party/teensy-variable-playback/src/ResamplingReader.h index a7455c7..0cf2b38 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingReader.h +++ b/third-party/teensy-variable-playback/src/ResamplingReader.h @@ -268,11 +268,11 @@ public: } } else { if (_playbackRate >= 0.0) { - if (_crossfade == 0.0 && _bufferPosition1 > (_loop_finish - _numChannels) - _crossfadeDurationInSamples) { + if (_crossfade == 0.0 && _bufferPosition1 > int32_t((_loop_finish - _numChannels) - _crossfadeDurationInSamples)) { _bufferPosition2 = _loop_start; _crossfade = 1.0 - (( (_loop_finish-_numChannels) - _bufferPosition1 ) / static_cast(_crossfadeDurationInSamples)); _crossfadeState = 1; - } else if (_crossfade == 1.0 && _bufferPosition2 > (_loop_finish - _numChannels)- _crossfadeDurationInSamples) { + } else if (_crossfade == 1.0 && _bufferPosition2 > int32_t((_loop_finish - _numChannels)- _crossfadeDurationInSamples)) { _bufferPosition1 = _loop_start; _crossfade = ((_loop_finish - _numChannels) - _bufferPosition2) / static_cast(_crossfadeDurationInSamples); _crossfadeState = 2; @@ -290,11 +290,11 @@ public: } } } else { - if (_crossfade == 0.0 && _bufferPosition1 < _crossfadeDurationInSamples + _header_offset) { + if (_crossfade == 0.0 && _bufferPosition1 < int32_t(_crossfadeDurationInSamples + _header_offset)) { _bufferPosition2 = _loop_finish - _numChannels; _crossfade = 1.0 - (_bufferPosition1 - _header_offset) / static_cast(_crossfadeDurationInSamples); _crossfadeState = 1; - } else if (_crossfade == 1.0 && _bufferPosition2 < _crossfadeDurationInSamples + _header_offset) { + } else if (_crossfade == 1.0 && _bufferPosition2 < int32_t(_crossfadeDurationInSamples + _header_offset)) { _bufferPosition1 = _loop_finish - _numChannels; _crossfade = (_bufferPosition2 - _header_offset) / static_cast(_crossfadeDurationInSamples); _crossfadeState = 2; @@ -659,8 +659,8 @@ protected: double _remainder = 0.0; loop_type _loopType = loop_type::looptype_none; play_start _play_start = play_start::play_start_sample; - int _bufferPosition1 = 0; - int _bufferPosition2 = 0; + int32_t _bufferPosition1 = 0; + int32_t _bufferPosition2 = 0; double _crossfade = 0.0; bool _useDualPlaybackHead = false; unsigned int _crossfadeDurationInSamples = 256; @@ -674,33 +674,12 @@ protected: ResampleInterpolationType _interpolationType = ResampleInterpolationType::resampleinterpolation_none; unsigned int _numInterpolationPoints[8] = {0}; - InterpolationData **_interpolationPoints = nullptr; + InterpolationData _interpolationPoints[8][4] = {0}; void initializeInterpolationPoints(void) { if (_numChannels < 0) return; - deleteInterpolationPoints(); - _interpolationPoints = new InterpolationData*[_numChannels]; - for (int channel=0; channel < _numChannels; channel++) { - InterpolationData *interpolation = new InterpolationData[4]; - interpolation[0].y = 0.0; - interpolation[1].y = 0.0; - interpolation[2].y = 0.0; - interpolation[3].y = 0.0; - _interpolationPoints[channel] = interpolation ; - } - _numInterpolationPointsChannels = _numChannels; - } - - void deleteInterpolationPoints(void) - { - if (!_interpolationPoints) return; - for (int i=0; i<_numInterpolationPointsChannels; i++) { - delete [] _interpolationPoints[i]; - } - delete [] _interpolationPoints; - _interpolationPoints = nullptr; _numInterpolationPointsChannels = 0; } diff --git a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h b/third-party/teensy-variable-playback/src/ResamplingSdReader.h similarity index 98% rename from third-party/TeensyVariablePlayback/src/ResamplingSdReader.h rename to third-party/teensy-variable-playback/src/ResamplingSdReader.h index c9c0390..b433578 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingSdReader.h +++ b/third-party/teensy-variable-playback/src/ResamplingSdReader.h @@ -56,7 +56,6 @@ public: delete [] _filename; _filename = nullptr; } - deleteInterpolationPoints(); } IndexableSDFile<128, 4>* createSourceBuffer() override { diff --git a/third-party/TeensyVariablePlayback/src/ResamplingSerialFlashReader.h b/third-party/teensy-variable-playback/src/ResamplingSerialFlashReader.h similarity index 98% rename from third-party/TeensyVariablePlayback/src/ResamplingSerialFlashReader.h rename to third-party/teensy-variable-playback/src/ResamplingSerialFlashReader.h index 8dafd13..cd189ac 100644 --- a/third-party/TeensyVariablePlayback/src/ResamplingSerialFlashReader.h +++ b/third-party/teensy-variable-playback/src/ResamplingSerialFlashReader.h @@ -56,7 +56,6 @@ public: delete [] _filename; _filename = nullptr; } - deleteInterpolationPoints(); } IndexableSerialFlashFile<128, 2>* createSourceBuffer() override { diff --git a/third-party/TeensyVariablePlayback/src/TeensyVariablePlayback.h b/third-party/teensy-variable-playback/src/TeensyVariablePlayback.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/TeensyVariablePlayback.h rename to third-party/teensy-variable-playback/src/TeensyVariablePlayback.h diff --git a/third-party/TeensyVariablePlayback/src/interpolation.cpp b/third-party/teensy-variable-playback/src/interpolation.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/src/interpolation.cpp rename to third-party/teensy-variable-playback/src/interpolation.cpp diff --git a/third-party/TeensyVariablePlayback/src/interpolation.h b/third-party/teensy-variable-playback/src/interpolation.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/interpolation.h rename to third-party/teensy-variable-playback/src/interpolation.h diff --git a/third-party/TeensyVariablePlayback/src/loop_type.h b/third-party/teensy-variable-playback/src/loop_type.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/loop_type.h rename to third-party/teensy-variable-playback/src/loop_type.h diff --git a/third-party/TeensyVariablePlayback/src/playarrayresmp.h b/third-party/teensy-variable-playback/src/playarrayresmp.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/playarrayresmp.h rename to third-party/teensy-variable-playback/src/playarrayresmp.h diff --git a/third-party/TeensyVariablePlayback/src/playlfsresmp.h b/third-party/teensy-variable-playback/src/playlfsresmp.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/playlfsresmp.h rename to third-party/teensy-variable-playback/src/playlfsresmp.h diff --git a/third-party/TeensyVariablePlayback/src/playresmp.h b/third-party/teensy-variable-playback/src/playresmp.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/playresmp.h rename to third-party/teensy-variable-playback/src/playresmp.h diff --git a/third-party/TeensyVariablePlayback/src/playsdresmp.h b/third-party/teensy-variable-playback/src/playsdresmp.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/playsdresmp.h rename to third-party/teensy-variable-playback/src/playsdresmp.h diff --git a/third-party/TeensyVariablePlayback/src/playserialflashresmp.h b/third-party/teensy-variable-playback/src/playserialflashresmp.h similarity index 100% rename from third-party/TeensyVariablePlayback/src/playserialflashresmp.h rename to third-party/teensy-variable-playback/src/playserialflashresmp.h diff --git a/third-party/TeensyVariablePlayback/src/waveheaderparser.h b/third-party/teensy-variable-playback/src/waveheaderparser.h similarity index 99% rename from third-party/TeensyVariablePlayback/src/waveheaderparser.h rename to third-party/teensy-variable-playback/src/waveheaderparser.h index 0a09476..c33d11e 100644 --- a/third-party/TeensyVariablePlayback/src/waveheaderparser.h +++ b/third-party/teensy-variable-playback/src/waveheaderparser.h @@ -9,8 +9,6 @@ #include #include "spi_interrupt.h" -//using namespace std; - // from https://gist.github.com/Jon-Schneider/8b7c53d27a7a13346a643dac9c19d34f struct wav_header { // RIFF Header diff --git a/third-party/TeensyVariablePlayback/test.sh b/third-party/teensy-variable-playback/test.sh similarity index 100% rename from third-party/TeensyVariablePlayback/test.sh rename to third-party/teensy-variable-playback/test.sh diff --git a/third-party/TeensyVariablePlayback/test/CMakeLists.txt b/third-party/teensy-variable-playback/test/CMakeLists.txt similarity index 100% rename from third-party/TeensyVariablePlayback/test/CMakeLists.txt rename to third-party/teensy-variable-playback/test/CMakeLists.txt diff --git a/third-party/TeensyVariablePlayback/test/audio/array/AudioArrayFixture.h b/third-party/teensy-variable-playback/test/audio/array/AudioArrayFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/array/AudioArrayFixture.h rename to third-party/teensy-variable-playback/test/audio/array/AudioArrayFixture.h diff --git a/third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_backward_playback.cpp b/third-party/teensy-variable-playback/test/audio/array/test_array_mono_loop_backward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_backward_playback.cpp rename to third-party/teensy-variable-playback/test/audio/array/test_array_mono_loop_backward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/audio/array/test_array_mono_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/array/test_array_mono_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/audio/array/test_array_mono_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_backward_playback.cpp b/third-party/teensy-variable-playback/test/audio/array/test_array_stereo_loop_backward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_backward_playback.cpp rename to third-party/teensy-variable-playback/test/audio/array/test_array_stereo_loop_backward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/audio/array/test_array_stereo_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/array/test_array_stereo_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/audio/array/test_array_stereo_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/output_test.cpp b/third-party/teensy-variable-playback/test/audio/output_test.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/output_test.cpp rename to third-party/teensy-variable-playback/test/audio/output_test.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/output_test.h b/third-party/teensy-variable-playback/test/audio/output_test.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/output_test.h rename to third-party/teensy-variable-playback/test/audio/output_test.h diff --git a/third-party/TeensyVariablePlayback/test/audio/wav/AudioWavFixture.h b/third-party/teensy-variable-playback/test/audio/wav/AudioWavFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/wav/AudioWavFixture.h rename to third-party/teensy-variable-playback/test/audio/wav/AudioWavFixture.h diff --git a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/audio/wav/test_wav_mono_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/audio/wav/test_wav_stereo_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/audio/wav/test_wav_tags.cpp b/third-party/teensy-variable-playback/test/audio/wav/test_wav_tags.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/audio/wav/test_wav_tags.cpp rename to third-party/teensy-variable-playback/test/audio/wav/test_wav_tags.cpp diff --git a/third-party/TeensyVariablePlayback/test/boost-to-junit-xml.xslt b/third-party/teensy-variable-playback/test/boost-to-junit-xml.xslt similarity index 100% rename from third-party/TeensyVariablePlayback/test/boost-to-junit-xml.xslt rename to third-party/teensy-variable-playback/test/boost-to-junit-xml.xslt diff --git a/third-party/TeensyVariablePlayback/test/embedfile.c b/third-party/teensy-variable-playback/test/embedfile.c similarity index 100% rename from third-party/TeensyVariablePlayback/test/embedfile.c rename to third-party/teensy-variable-playback/test/embedfile.c diff --git a/third-party/TeensyVariablePlayback/test/low_level/array/ResamplingArrayFixture.h b/third-party/teensy-variable-playback/test/low_level/array/ResamplingArrayFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/array/ResamplingArrayFixture.h rename to third-party/teensy-variable-playback/test/low_level/array/ResamplingArrayFixture.h diff --git a/third-party/TeensyVariablePlayback/test/low_level/array/test_array_mono_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/array/test_array_mono_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/array/test_array_mono_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/array/test_array_mono_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/array/test_array_stereo_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/array/test_array_stereo_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/array/test_array_stereo_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/array/test_array_stereo_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/arraywav/ResamplingArrayWavFixture.h b/third-party/teensy-variable-playback/test/low_level/arraywav/ResamplingArrayWavFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/arraywav/ResamplingArrayWavFixture.h rename to third-party/teensy-variable-playback/test/low_level/arraywav/ResamplingArrayWavFixture.h diff --git a/third-party/TeensyVariablePlayback/test/low_level/arraywav/test_array_mono_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/arraywav/test_array_mono_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/arraywav/test_array_mono_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/arraywav/test_array_mono_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/arraywav/test_array_stereo_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/arraywav/test_array_stereo_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/arraywav/test_array_stereo_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/arraywav/test_array_stereo_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/indexedfile/IndexedFileFixture.h b/third-party/teensy-variable-playback/test/low_level/indexedfile/IndexedFileFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/indexedfile/IndexedFileFixture.h rename to third-party/teensy-variable-playback/test/low_level/indexedfile/IndexedFileFixture.h diff --git a/third-party/TeensyVariablePlayback/test/low_level/indexedfile/test_indexablefile.cpp b/third-party/teensy-variable-playback/test/low_level/indexedfile/test_indexablefile.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/indexedfile/test_indexablefile.cpp rename to third-party/teensy-variable-playback/test/low_level/indexedfile/test_indexablefile.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/ResamplingReaderFixture.h b/third-party/teensy-variable-playback/test/low_level/sd/ResamplingReaderFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/ResamplingReaderFixture.h rename to third-party/teensy-variable-playback/test/low_level/sd/ResamplingReaderFixture.h diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/readme.MD b/third-party/teensy-variable-playback/test/low_level/sd/readme.MD similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/readme.MD rename to third-party/teensy-variable-playback/test/low_level/sd/readme.MD diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/test_raw_mono_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/sd/test_raw_mono_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/test_raw_mono_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/sd/test_raw_mono_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/test_raw_mono_noloop_forward_double_rate_playback.cpp b/third-party/teensy-variable-playback/test/low_level/sd/test_raw_mono_noloop_forward_double_rate_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/test_raw_mono_noloop_forward_double_rate_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/sd/test_raw_mono_noloop_forward_double_rate_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/test_raw_mono_noloop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/sd/test_raw_mono_noloop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/test_raw_mono_noloop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/sd/test_raw_mono_noloop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/test_wav_mono_loop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/sd/test_wav_mono_loop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/test_wav_mono_loop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/sd/test_wav_mono_loop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/test_wav_mono_noloop_backward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/sd/test_wav_mono_noloop_backward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/test_wav_mono_noloop_backward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/sd/test_wav_mono_noloop_backward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/sd/test_wav_mono_noloop_forward_playback.cpp b/third-party/teensy-variable-playback/test/low_level/sd/test_wav_mono_noloop_forward_playback.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/sd/test_wav_mono_noloop_forward_playback.cpp rename to third-party/teensy-variable-playback/test/low_level/sd/test_wav_mono_noloop_forward_playback.cpp diff --git a/third-party/TeensyVariablePlayback/test/low_level/wav_header/WaveHeaderParserFixture.h b/third-party/teensy-variable-playback/test/low_level/wav_header/WaveHeaderParserFixture.h similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/wav_header/WaveHeaderParserFixture.h rename to third-party/teensy-variable-playback/test/low_level/wav_header/WaveHeaderParserFixture.h diff --git a/third-party/TeensyVariablePlayback/test/low_level/wav_header/test_parse_wave_header.cpp b/third-party/teensy-variable-playback/test/low_level/wav_header/test_parse_wave_header.cpp similarity index 100% rename from third-party/TeensyVariablePlayback/test/low_level/wav_header/test_parse_wave_header.cpp rename to third-party/teensy-variable-playback/test/low_level/wav_header/test_parse_wave_header.cpp diff --git a/third-party/TeensyVariablePlayback/test/resources/input/PNO1C1.raw b/third-party/teensy-variable-playback/test/resources/input/PNO1C1.raw similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/PNO1C1.raw rename to third-party/teensy-variable-playback/test/resources/input/PNO1C1.raw diff --git a/third-party/TeensyVariablePlayback/test/resources/input/SDTEST1.wav b/third-party/teensy-variable-playback/test/resources/input/SDTEST1.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/SDTEST1.wav rename to third-party/teensy-variable-playback/test/resources/input/SDTEST1.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/input/SDTEST2.wav b/third-party/teensy-variable-playback/test/resources/input/SDTEST2.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/SDTEST2.wav rename to third-party/teensy-variable-playback/test/resources/input/SDTEST2.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/input/kick.raw b/third-party/teensy-variable-playback/test/resources/input/kick.raw similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/kick.raw rename to third-party/teensy-variable-playback/test/resources/input/kick.raw diff --git a/third-party/TeensyVariablePlayback/test/resources/input/kick.wav b/third-party/teensy-variable-playback/test/resources/input/kick.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/kick.wav rename to third-party/teensy-variable-playback/test/resources/input/kick.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/input/mono_souljah.wav b/third-party/teensy-variable-playback/test/resources/input/mono_souljah.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/mono_souljah.wav rename to third-party/teensy-variable-playback/test/resources/input/mono_souljah.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/input/stereo_souljah.raw b/third-party/teensy-variable-playback/test/resources/input/stereo_souljah.raw similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/stereo_souljah.raw rename to third-party/teensy-variable-playback/test/resources/input/stereo_souljah.raw diff --git a/third-party/TeensyVariablePlayback/test/resources/input/stereo_souljah.wav b/third-party/teensy-variable-playback/test/resources/input/stereo_souljah.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/input/stereo_souljah.wav rename to third-party/teensy-variable-playback/test/resources/input/stereo_souljah.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_5000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_5000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_5000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_5000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_5000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_5000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_5000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_5000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_0_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_0_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_0000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_0000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_0000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_0000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_0000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_0000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_0000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_0000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_1_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_1_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_0000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_0000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_0000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_0000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_0000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_0000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_0000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_0000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_5000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_5000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_5000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_5000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_5000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_5000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_2_5000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_2_5000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_8_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_8_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_8_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_8_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_8_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_bwd_8_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_bwd_8_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_bwd_8_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_5000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_5000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_5000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_5000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_5000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_5000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_5000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_5000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_0_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_0_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_0000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_0000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_0000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_0000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_0000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_0000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_0000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_0000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_1_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_1_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_2_0000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_2_0000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_2_0000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_2_0000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_2_0000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_2_0000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_2_0000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_2_0000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_2_5000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_2_5000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_2_5000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_2_5000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_8_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_8_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_8_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_8_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_8_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Array_fwd_8_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Array_fwd_8_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Array_fwd_8_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/SDTEST1.wav b/third-party/teensy-variable-playback/test/resources/reference/SDTEST1.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/SDTEST1.wav rename to third-party/teensy-variable-playback/test/resources/reference/SDTEST1.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/SDTEST2.wav b/third-party/teensy-variable-playback/test/resources/reference/SDTEST2.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/SDTEST2.wav rename to third-party/teensy-variable-playback/test/resources/reference/SDTEST2.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_5000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_5000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_5000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_5000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_5000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_5000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_5000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_5000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_0_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_0_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_0000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_0000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_0000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_0000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_0000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_0000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_0000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_0000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_1_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_1_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_2_0000_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_2_0000_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_2_0000_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_2_0000_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_2_0000_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_2_0000_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_2_0000_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_2_0000_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_8_7437_quadratic_mono_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_8_7437_quadratic_mono_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_8_7437_quadratic_mono_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_8_7437_quadratic_mono_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_8_7437_quadratic_stereo_noloop.wav b/third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_8_7437_quadratic_stereo_noloop.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/Wav_fwd_8_7437_quadratic_stereo_noloop.wav rename to third-party/teensy-variable-playback/test/resources/reference/Wav_fwd_8_7437_quadratic_stereo_noloop.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/kick.wav b/third-party/teensy-variable-playback/test/resources/reference/kick.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/kick.wav rename to third-party/teensy-variable-playback/test/resources/reference/kick.wav diff --git a/third-party/TeensyVariablePlayback/test/resources/reference/stereo_souljah.wav b/third-party/teensy-variable-playback/test/resources/reference/stereo_souljah.wav similarity index 100% rename from third-party/TeensyVariablePlayback/test/resources/reference/stereo_souljah.wav rename to third-party/teensy-variable-playback/test/resources/reference/stereo_souljah.wav