mirror of https://github.com/probonopd/MiniDexed
parent
ce6810a8b7
commit
d17cdfb070
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…
Reference in new issue