Unison in Synth_Dexed

pull/901/head
probonopd 1 week ago
parent ce6810a8b7
commit d17cdfb070
  1. 15
      src/common.h
  2. 46
      src/dexedadapter.h
  3. 12
      src/effect_platervbstereo.h
  4. 136
      src/minidexed.cpp
  5. 1937
      src/patches/dexed.cpp
  6. 415
      src/patches/dexed.h
  7. 401
      src/patches/dx7note.cpp
  8. 83
      src/patches/dx7note.h

@ -1,7 +1,8 @@
#ifndef _common_h
#define _common_h
#include <stdint.h>
inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
@ -16,11 +17,11 @@ inline float32_t mapfloat(int val, int in_min, int in_max, float32_t out_min, fl
return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
#define constrain(amt, low, high) ({ \
__typeof__(amt) _amt = (amt); \
__typeof__(low) _low = (low); \
__typeof__(high) _high = (high); \
(_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \
})
template<typename T>
// Code that used to use constrain shall use clamp instead
// This was renamed to avoid conflicts with the Synth_Dexed submodule
inline T clamp(T amt, T low, T high) {
return (amt < low) ? low : ((amt > high) ? high : amt);
}
#endif

@ -32,6 +32,8 @@
class CDexedAdapter : public Dexed
{
private:
CSpinLock m_SpinLock;
public:
CDexedAdapter (uint8_t maxnotes, int rate)
: Dexed (maxnotes, rate)
@ -80,8 +82,46 @@ public:
m_SpinLock.Release ();
}
private:
CSpinLock m_SpinLock;
};
// Unison wrapper: for now, simulate unison by calling keydown/keyup multiple times with detune/pan offsets
void keydown_unison(int16_t pitch, uint8_t velo, unsigned unisonVoices, unsigned unisonDetune, unsigned unisonSpread, int baseDetune, unsigned basePan) {
if (unisonVoices < 1) unisonVoices = 1;
for (unsigned v = 0; v < unisonVoices; ++v) {
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
int detune = baseDetune + (int)detuneOffset;
m_SpinLock.Acquire();
this->setMasterTune((int8_t)detune);
Dexed::keydown(pitch, velo);
m_SpinLock.Release();
}
}
void keyup_unison(int16_t pitch, unsigned unisonVoices, unsigned unisonDetune, unsigned unisonSpread, int baseDetune, unsigned basePan) {
if (unisonVoices < 1) unisonVoices = 1;
for (unsigned v = 0; v < unisonVoices; ++v) {
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
int detune = baseDetune + (int)detuneOffset;
m_SpinLock.Acquire();
this->setMasterTune((int8_t)detune);
Dexed::keyup(pitch);
m_SpinLock.Release();
}
}
#ifdef ARM_ALLOW_MULTI_CORE
// Stereo version for unison pan
void getSamplesStereo(float32_t* bufferL, float32_t* bufferR, uint16_t n_samples) {
m_SpinLock.Acquire();
Dexed::getSamplesStereo(bufferL, bufferR, n_samples);
m_SpinLock.Release();
}
// Set unison parameters for Dexed, now with base pan
void setUnisonParameters(unsigned voices, unsigned detune, unsigned spread, float basePan) {
m_SpinLock.Acquire();
// Map detune: 0..99 -> 0..0.02 (2 cents)
float detuneCents = ((float)detune / 99.0f) * 0.02f;
Dexed::setUnisonParameters((uint8_t)voices, detuneCents, (float)spread / 99.0f, basePan);
m_SpinLock.Release();
}
#endif
};
#endif // _dexedadapter_h

@ -64,7 +64,7 @@ public:
void size(float n)
{
n = constrain(n, 0.0f, 1.0f);
n = clamp(n, 0.0f, 1.0f);
n = mapfloat(n, 0.0f, 1.0f, 0.2f, rv_time_k_max);
float32_t attn = mapfloat(n, 0.0f, rv_time_k_max, 0.5f, 0.25f);
rv_time_k = n;
@ -73,27 +73,27 @@ public:
void hidamp(float n)
{
n = constrain(n, 0.0f, 1.0f);
n = clamp(n, 0.0f, 1.0f);
lp_hidamp_k = 1.0f - n;
}
void lodamp(float n)
{
n = constrain(n, 0.0f, 1.0f);
n = clamp(n, 0.0f, 1.0f);
lp_lodamp_k = -n;
rv_time_scaler = 1.0f - n * 0.12f; // limit the max reverb time, otherwise it will clip
}
void lowpass(float n)
{
n = constrain(n, 0.0f, 1.0f);
n = clamp(n, 0.0f, 1.0f);
n = mapfloat(n*n*n, 0.0f, 1.0f, 0.05f, 1.0f);
master_lowpass_f = n;
}
void diffusion(float n)
{
n = constrain(n, 0.0f, 1.0f);
n = clamp(n, 0.0f, 1.0f);
n = mapfloat(n, 0.0f, 1.0f, 0.005f, 0.65f);
in_allp_k = n;
loop_allp_k = n;
@ -101,7 +101,7 @@ public:
void level(float n)
{
reverb_level = constrain(n, 0.0f, 1.0f);
reverb_level = clamp(n, 0.0f, 1.0f);
}
float32_t get_size(void) {return rv_time_k;}

@ -30,9 +30,7 @@
#include <stdio.h>
#include <assert.h>
#include "arm_float_to_q23.h"
// Forward declaration for getPhysicalTG
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned unisonVoices);
#include "common.h"
const char WLANFirmwarePath[] = "SD:firmware/";
const char WLANConfigFile[] = "SD:wpa_supplicant.conf";
@ -846,98 +844,76 @@ void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG)
m_UI.ParameterChanged ();
}
void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
#ifndef ARM_ALLOW_MULTI_CORE
// Use original mono logic for single-core
void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
assert (m_pTG[nTG]);
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch < 0) return;
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, unisonVoices);
if (physicalTG >= m_nToneGenerators) break;
// Ensure virtual TG plays the same voice as logical TG
if (physicalTG != nTG) {
uint8_t voiceData[156];
m_pTG[nTG]->getVoiceData(voiceData);
m_pTG[physicalTG]->loadVoiceParameters(voiceData);
}
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
m_pTG[physicalTG]->keyup(pitch);
m_pTG[nTG]->keydown(pitch, velocity);
}
void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
assert (m_pTG[nTG]);
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch < 0) return;
m_pTG[nTG]->keyup(pitch);
}
#endif
#ifdef ARM_ALLOW_MULTI_CORE
// Use unison/stereo logic for multicore
void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
assert (m_pTG[nTG]);
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch < 0) return;
unsigned unisonVoices = m_nUnisonVoices[nTG];
if (unisonVoices < 1) unisonVoices = 1;
unsigned maxLogicalTGs = m_nToneGenerators / unisonVoices;
if (nTG >= maxLogicalTGs) return; // Don't exceed available physical TGs
int baseDetune = m_nMasterTune[nTG];
unsigned basePan = m_nPan[nTG];
unsigned unisonDetune = m_nUnisonDetune[nTG];
unsigned unisonSpread = m_nUnisonSpread[nTG];
for (unsigned v = 0; v < unisonVoices; ++v) {
unsigned physicalTG = getPhysicalTG(nTG, v, unisonVoices);
if (physicalTG >= m_nToneGenerators) break;
float detuneOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonDetune;
float panOffset = ((float)v - (unisonVoices - 1) / 2.0f) * (float)unisonSpread;
int detune = baseDetune + (int)detuneOffset;
unsigned pan = basePan + (int)panOffset;
detune = constrain(detune, -99, 99);
pan = constrain((int)pan, 0, 127);
m_pTG[physicalTG]->setMasterTune((int8_t)detune);
tg_mixer->pan(physicalTG, mapfloat(pan, 0, 127, 0.0f, 1.0f));
m_pTG[physicalTG]->keydown(pitch, velocity);
// Set unison parameters for Dexed engine before keydown
m_pTG[nTG]->setUnisonParameters(
m_nUnisonVoices[nTG],
m_nUnisonDetune[nTG],
m_nUnisonSpread[nTG],
mapfloat(m_nPan[nTG], 0, 127, 0.0f, 1.0f)
);
m_pTG[nTG]->keydown(pitch, velocity);
}
void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return; // Not an active TG
assert (m_pTG[nTG]);
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch < 0) return;
// Set unison parameters for Dexed engine before keyup
m_pTG[nTG]->setUnisonParameters(
m_nUnisonVoices[nTG],
m_nUnisonDetune[nTG],
m_nUnisonSpread[nTG],
mapfloat(m_nPan[nTG], 0, 127, 0.0f, 1.0f)
);
m_pTG[nTG]->keyup(pitch);
}
#endif
// Complete the ApplyNoteLimits function definition
int16_t CMiniDexed::ApplyNoteLimits(int16_t pitch, unsigned nTG)
{
assert (nTG < CConfig::AllToneGenerators);
if (nTG >= m_nToneGenerators) return -1; // Not an active TG
if ( pitch < (int16_t) m_nNoteLimitLow[nTG]
|| pitch > (int16_t) m_nNoteLimitHigh[nTG])
{
if (pitch < (int16_t)m_nNoteLimitLow[nTG] || pitch > (int16_t)m_nNoteLimitHigh[nTG])
return -1;
}
pitch += m_nNoteShift[nTG];
if ( pitch < 0
|| pitch > 127)
{
if (pitch < 0 || pitch > 127)
return -1;
}
return pitch;
}
@ -1184,6 +1160,7 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
case TGParameterUnisonVoices:
m_nUnisonVoices[nTG] = constrain(nValue, 1, 4);
panic(0, nTG); // Stop all notes on TG when unison voices changes
break;
case TGParameterUnisonDetune:
m_nUnisonDetune[nTG] = constrain(nValue, 0, 99);
@ -1342,12 +1319,16 @@ void CMiniDexed::ProcessSound (void)
m_GetChunkTimer.Start ();
}
float32_t SampleBuffer[nFrames];
m_pTG[0]->getSamples (SampleBuffer, nFrames);
float32_t SampleBufferL[nFrames];
float32_t SampleBufferR[nFrames];
m_pTG[0]->getSamplesStereo(SampleBufferL, SampleBufferR, nFrames);
// Convert single float array (mono) to int16 array
int32_t tmp_int[nFrames];
arm_float_to_q23(SampleBuffer,tmp_int,nFrames);
// Convert stereo float arrays to interleaved int32 array
int32_t tmp_int[nFrames * 2];
for (unsigned i = 0; i < nFrames; ++i) {
tmp_int[i*2] = float_to_q23(SampleBufferL[i]);
tmp_int[i*2+1] = float_to_q23(SampleBufferR[i]);
}
if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int))
{
@ -1888,8 +1869,10 @@ void CMiniDexed::setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG)
if (nTG >= m_nToneGenerators) return; // Not an active TG
assert (m_pTG[nTG]);
assert (number <= 99);
assert (data <= 155);
m_pTG[nTG]->setVoiceDataElement(constrain(data, 0, 155),constrain(number, 0, 99));
m_pTG[nTG]->setVoiceDataElement (data, number);
m_UI.ParameterChanged ();
}
@ -2174,6 +2157,7 @@ void CMiniDexed::SetVoiceName (const std::string &VoiceName, unsigned nTG)
assert (m_pTG[nTG]);
char Name[11];
// Fix strncpy overload issue by casting to const char*
strncpy(Name, VoiceName.c_str(), 10);
Name[10] = '\0';
m_pTG[nTG]->setName (Name);
@ -2562,9 +2546,3 @@ bool CMiniDexed::InitNetwork()
return false;
}
}
// Forward declaration and definition for getPhysicalTG
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned unisonVoices);
static unsigned getPhysicalTG(unsigned logicalTG, unsigned unisonVoice, unsigned unisonVoices) {
return logicalTG * unisonVoices + unisonVoice;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,415 @@
/*
MicroDexed
MicroDexed is a port of the Dexed sound engine
(https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield.
Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android
(c)2018-2021 H. Wirtz <wirtz@parasitstudio.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//#define DISABLE_DEXED_COMPRESSOR 1
#ifndef DEXED_H_INCLUDED
#define DEXED_H_INCLUDED
#include <stdint.h>
#include <cstdlib>
#include <arm_math.h>
#if defined(TEENSYDUINO)
#include <Audio.h>
#endif
#include "fm_op_kernel.h"
#include "synth.h"
#include "env.h"
#include "aligned_buf.h"
#include "pitchenv.h"
#include "controllers.h"
#include "dx7note.h"
#include "lfo.h"
#include "PluginFx.h"
#include "compressor.h"
#include "EngineMsfa.h"
#include "EngineMkI.h"
#include "EngineOpl.h"
#define NUM_VOICE_PARAMETERS 156
struct ProcessorVoice {
uint8_t midi_note;
uint8_t velocity;
int16_t porta;
bool keydown;
bool sustained;
bool sostenuted;
bool held;
bool live;
uint32_t key_pressed_timer;
Dx7Note *dx7_note;
float pan; // pan value for stereo spread (0.0 = left, 1.0 = right)
};
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 setSostenuto(bool sostenuto);
bool getSostenuto(void);
void setHold(bool hold);
bool getHold(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[11]);
void getName(char buffer[11]);
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;
bool sostenuto;
bool hold;
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
// Unison state
uint8_t unisonVoices = 1;
float unisonDetune = 0.0f;
float unisonSpread = 0.0f;
#ifdef ARM_ALLOW_MULTI_CORE
// Unison parameters and stereo output only for multicore
void setUnisonParameters(uint8_t voices, float detune, float spread, float basePan);
void getSamplesStereo(float* bufferL, float* bufferR, uint16_t n_samples);
float basePan = 0.5f;
#endif
// --- Unison note tracking for robust note-off ---
static constexpr int kMaxMidiNotes = 128;
static constexpr int kMaxUnison = 8; // adjust as needed
float unisonDetunedPitches[kMaxMidiNotes][kMaxUnison] = {};
uint8_t unisonDetunedCount[kMaxMidiNotes] = {};
int unisonVoiceIndices[kMaxMidiNotes][kMaxUnison] = {};
};
#endif

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

@ -0,0 +1,83 @@
/*
Copyright 2016-2017 Pascal Gauthier.
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef SYNTH_DX7NOTE_H_
#define SYNTH_DX7NOTE_H_
// This is the logic to put together a note from the MIDI description
// and run the low-level modules.
// It will continue to evolve a bit, as note-stealing logic, scaling,
// and real-time control of parameters live here.
#include <stdint.h>
#include "env.h"
#include "pitchenv.h"
#include "fm_core.h"
struct VoiceStatus {
uint32_t amp[6];
char ampStep[6];
char pitchStep;
};
class Dx7Note {
public:
Dx7Note();
// Change midinote to float for fractional detune
void init(const uint8_t patch[156], float midinote, int velocity, float 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], float 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
Loading…
Cancel
Save