You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MicroDexed/synth_dexed.h

1215 lines
35 KiB

#include <Arduino.h>
#include <Audio.h>
#include "config.h"
/*****************************************************
CODE: orig_code/synth.h
*****************************************************/
/*
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 SUPER_PRECISE
// This IS not be present on MSVC.
// See http://stackoverflow.com/questions/126279/c99-stdint-h-header-and-ms-visual-studio
#ifdef _MSC_VER
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int16 SInt16;
#endif
#define LG_N 6
#define _N_ (1 << LG_N)
#if defined(__APPLE__)
#include <libkern/OSAtomic.h>
#define SynthMemoryBarrier() OSMemoryBarrier()
#elif defined(__GNUC__)
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
#define SynthMemoryBarrier() __sync_synchronize()
#endif
#endif
// #undef SynthMemoryBarrier()
#ifndef SynthMemoryBarrier
// need to understand why this must be defined
// #warning Memory barrier is not enabled
#define SynthMemoryBarrier()
#endif
template<typename T>
inline static T min(const T& a, const T& b) {
return a < b ? a : b;
}
template<typename T>
inline static T max(const T& a, const T& b) {
return a > b ? a : b;
}
#define QER(n,b) ( ((float)n)/(1<<b) )
#define FRAC_NUM float
#define SIN_FUNC sinf
// #define SIN_FUNC arm_sin_f32 // very fast but not as accurate
#define COS_FUNC cosf
// #define COS_FUNC arm_cos_f32 // very fast but not as accurate
#define LOG_FUNC logf
#define EXP_FUNC expf
#define SQRT_FUNC sqrtf
// #define ARM_SQRT_FUNC arm_sqrt_f32 // fast but not as accurate
//=====================================================
/*****************************************************
CODE: orig_code/aligned_buf.h
*****************************************************/
/*
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.
template<typename T, size_t size, size_t alignment = 16>
class AlignedBuf {
public:
T *get() {
return (T *)((((intptr_t)storage_) + alignment - 1) & -alignment);
}
private:
unsigned char storage_[size * sizeof(T) + alignment];
};
//=====================================================
/*****************************************************
CODE: orig_code/sin.h
*****************************************************/
/*
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 int SHIFT = 24 - SIN_LG_N_SAMPLES;
int 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];
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];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif
}
#endif
//=====================================================
/*****************************************************
CODE: orig_code/exp2.h
*****************************************************/
/*
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 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];
int 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 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);
return y ^ signum;
}
}
//=====================================================
/*****************************************************
CODE: orig_code/fast_log.h
*****************************************************/
/* ----------------------------------------------------------------------
https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621
Fast approximation to the log2() function. It uses a two step
process. First, it decomposes the floating-point number into
a fractional component F and an exponent E. The fraction component
is used in a polynomial approximation and then the exponent added
to the result. A 3rd order polynomial is used and the result
when computing db20() is accurate to 7.984884e-003 dB.
** ------------------------------------------------------------------- */
static 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;
int E;
// This is the approximation to log2()
F = frexpf(fabsf(X), &E);
// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E;
Y = *C++;
Y *= F;
Y += (*C++);
Y *= F;
Y += (*C++);
Y *= F;
Y += (*C++);
Y += E;
return (Y);
}
// https://codingforspeed.com/using-faster-exponential-approximation/
inline float expf_approx(float x) {
x = 1.0f + x / 1024;
x *= x; x *= x; x *= x; x *= x;
x *= x; x *= x; x *= x; x *= x;
x *= x; x *= x;
return x;
}
inline float unitToDb(float unit) {
return 6.02f * log2f_approx(unit);
}
inline float dbToUnit(float db) {
return expf_approx(db * 2.302585092994046f * 0.05f);
}
//=====================================================
/*****************************************************
CODE: orig_code/freqlut.h
*****************************************************/
/*
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 Freqlut {
public:
static void init(FRAC_NUM sample_rate);
static int32_t lookup(int32_t logfreq);
};
//=====================================================
/*****************************************************
CODE: orig_code/lfo.h
*****************************************************/
/*
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_;
};
//=====================================================
/*****************************************************
CODE: orig_code/env.h
*****************************************************/
/*
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.
*/
// 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 int rates[4], const int levels[4], int outlevel,
int rate_scaling);
void update(const int rates[4], const int levels[4], int outlevel,
int 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 int scaleoutlevel(int 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;
int rates_[4];
int levels_[4];
int outlevel_;
int 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_;
bool rising_;
int ix_;
int inc_;
#ifdef ACCURATE_ENVELOPE
int staticcount_;
#endif
bool down_;
void advance(int newix);
};
//=====================================================
/*****************************************************
CODE: orig_code/pitchenv.h
*****************************************************/
/*
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.
*/
// 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 int rates[4], const int 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];
int32_t level_;
int targetlevel_;
bool rising_;
int ix_;
int inc_;
bool down_;
void advance(int newix);
};
extern const uint8_t pitchenv_rate[];
extern const int8_t pitchenv_tab[];
//=====================================================
/*****************************************************
CODE: orig_code/controllers.h
*****************************************************/
/*
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.
*/
// State of MIDI controllers
const int kControllerPitch = 0;
const int kControllerPitchRange = 1;
const int kControllerPitchStep = 2;
const int kControllerPortamentoGlissando = 3;
class FmCore;
class FmMod {
public:
uint8_t range;
bool pitch;
bool amp;
bool eg;
uint8_t ctrl_mode;
uint8_t _dummy_;
FmMod()
{
range = 0;
ctrl_mode = 0;
pitch = false;
amp = false;
eg = false;
}
void setRange(uint8_t r)
{
range = r < 0 && r > 99 ? 0 : r;
}
void setTarget(uint8_t assign)
{
assign = assign < 0 && assign > 7 ? 0 : assign;
pitch = assign & 1; // PITCH
amp = assign & 2; // AMP
eg = assign & 4; // 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 = max(amp_mod, total);
if (mod.pitch)
pitch_mod = max(pitch_mod, total);
if (mod.eg)
eg_mod = 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;
int portamento_cc;
bool portamento_gliss_cc;
int 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;
}
FmCore *core;
};
//=====================================================
/*****************************************************
CODE: orig_code/PluginFx.h
*****************************************************/
/**
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
*/
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;
int 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;
int 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(int sampleRate);
void process(float *work, int sampleSize);
float getGain(void);
};
//=====================================================
/*****************************************************
CODE: orig_code/fm_op_kernel.h
*****************************************************/
/*
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.
*/
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, int fb_gain, bool add);
};
//=====================================================
/*****************************************************
CODE: orig_code/fm_core.h
*****************************************************/
/*
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 FmOperatorInfo {
public:
int in;
int 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:
int ops[6];
};
class FmCore {
public:
virtual ~FmCore() {};
static void dump();
uint8_t get_carrier_operators(uint8_t algorithm);
virtual void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_gain);
protected:
AlignedBuf<int32_t, _N_>buf_[2];
const static FmAlgorithm algorithms[32];
};
//=====================================================
/*****************************************************
CODE: orig_code/dx7note.h
*****************************************************/
/*
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.
*/
// 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.
#pragma once
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];
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];
};
//=====================================================
/*****************************************************
CODE: orig_code/dexed.h
*****************************************************/
/*
MicroDexed
MicroDexed is a port of the Dexed sound engine
(https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield.
Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android
(c)2018-2021 H. Wirtz <wirtz@parasitstudio.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
struct ProcessorVoice {
int16_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
};
/* #define DEXED_GLOBAL_PARAMETER_OFFSET 155
enum DexedGlobalParameters {
DEXED_PITCHBEND_RANGE, // 0
DEXED_PITCHBEND_STEP, // 1
DEXED_MODWHEEL_RANGE, // 2
DEXED_MODWHEEL_ASSIGN, // 3
DEXED_FOOTCTRL_RANGE, // 4
DEXED_FOOTCTRL_ASSIGN, // 5
DEXED_BREATHCTRL_RANGE, // 6
DEXED_BREATHCTRL_ASSIGN, // 7
DEXED_AT_RANGE, // 8
DEXED_AT_ASSIGN, // 9
DEXED_MASTER_TUNE, // 10
DEXED_OP1_ENABLE, // 11
DEXED_OP2_ENABLE, // 12
DEXED_OP3_ENABLE, // 13
DEXED_OP4_ENABLE, // 14
DEXED_OP5_ENABLE, // 15
DEXED_OP6_ENABLE, // 16
DEXED_MAX_NOTES, // 17
DEXED_PORTAMENTO_MODE, // 18
DEXED_PORTAMENTO_GLISSANDO, // 19
DEXED_PORTAMENTO_TIME, // 20
}; */
// GLOBALS
//==============================================================================
class Dexed
{
public:
Dexed(int rate);
~Dexed();
void activate(void);
void deactivate(void);
bool isMonoMode(void);
void setMonoMode(bool mode);
void setRefreshMode(bool mode);
//void getSamples(uint16_t n_samples, int16_t* buffer);
void panic(void);
void notesOff(void);
void resetControllers(void);
void setMaxNotes(uint8_t n);
uint8_t getMaxNotes(void);
void doRefreshVoice(void);
void setOPs(uint8_t ops);
bool decodeVoice(uint8_t* encoded_data, uint8_t* data);
bool encodeVoice(uint8_t* encoded_data);
bool getVoiceData(uint8_t* data_copy);
bool loadVoiceParameters(uint8_t* data);
bool loadGlobalParameters(uint8_t* data);
bool initGlobalParameters(void);
void keyup(int16_t pitch);
void keydown(int16_t pitch, uint8_t velo);
void setSustain(bool sustain);
bool getSustain(void);
uint8_t getNumNotesPlaying(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 setPortamentoMode(uint8_t portamento_mode, uint8_t portamento_glissando, uint8_t portamento_time);
void setRateAllOP(uint8_t rate);
void setLevelAllOP(uint8_t level);
void setRateOPAllCarrier(uint8_t step, uint8_t rate);
void setLevelOPAllCarrier(uint8_t step, uint8_t level);
void setRateOPAllModulator(uint8_t step, uint8_t rate);
void setLevelOPAllModulator(uint8_t step, uint8_t level);
void setRateOP(uint8_t op, uint8_t step, uint8_t rate);
void setLevelOP(uint8_t op, uint8_t step, uint8_t level);
uint8_t getRateOP(uint8_t op, uint8_t step);
uint8_t getLevelOP(uint8_t op, uint8_t step);
ProcessorVoice voices[MAX_NOTES];
Controllers controllers;
PluginFx fx;
uint8_t data[156] = {
95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, // 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
95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, // OP5
95, 29, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 06, 89, 00, 01, 00, 07, // OP4
95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 07, // OP3
95, 50, 35, 78, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 07, 58, 00, 14, 00, 07, // OP2
96, 25, 25, 67, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 10, // OP1
94, 67, 95, 60, 50, 50, 50, 50, // 4 * pitch EG rates, 4 * pitch EG level
04, 06, 00, // algorithm, feedback, osc sync
34, 33, 00, 00, 00, 04, // lfo speed, lfo delay, lfo pitch_mod_depth, lfo_amp_mod_depth, lfo_sync, lfo_waveform
03, 24, // pitch_mod_sensitivity, transpose
69, 68, 80, 56, 85, 76, 84, 00, 00, 00 // 10 * char for name ("DEFAULT ")
}; // FM-Piano
int lastKeyDown;
protected:
static const uint8_t MAX_ACTIVE_NOTES = MAX_NOTES;
uint8_t max_notes = MAX_ACTIVE_NOTES;
int16_t currentNote;
bool sustain;
float vuSignal;
bool monoMode;
bool refreshMode;
bool refreshVoice;
uint8_t engineType;
VoiceStatus voiceStatus;
Lfo lfo;
FmCore* engineMsfa;
void getSamples(uint16_t n_samples, float32_t* buffer);
void getSamples(uint16_t n_samples, int16_t* buffer);
};
//=====================================================
/*****************************************************
CODE: orig_code/porta.h
*****************************************************/
/*
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.
*/
struct Porta {
public:
static void init_sr(double sampleRate);
static int32_t rates[128];
};
//=====================================================
/*****************************************************
CODE: orig_code/synth_microdexed.h
*****************************************************/
class AudioSynthDexed : public AudioStream, public Dexed {
public:
const uint16_t audio_block_time_us = 1000000 / (SAMPLE_RATE / AUDIO_BLOCK_SAMPLES);
uint32_t xrun = 0;
uint16_t render_time_max = 0;
AudioSynthDexed(uint16_t sample_rate) : AudioStream(0, NULL), Dexed(sample_rate) { };
void update(void)
{
if (in_update == true)
{
xrun++;
return;
}
else
in_update = true;
elapsedMicros render_time;
audio_block_t *lblock;
lblock = allocate();
if (!lblock)
{
in_update = false;
return;
}
getSamples(AUDIO_BLOCK_SAMPLES, lblock->data);
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(lblock, 0);
release(lblock);
in_update = false;
};
private:
volatile bool in_update = false;
};