From 781169d0c7131f06f4d1e4715094216e2b1dec3f Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Mon, 30 Apr 2018 14:55:45 +0200 Subject: [PATCH] ... --- DueDexed.ino | 34 +- dexed.cpp | 760 ++++++++++++++++++++++++++ dexed.h | 20 +- PluginFx.cpp => not_used/PluginFx.cpp | 0 PluginFx.h => not_used/PluginFx.h | 0 5 files changed, 778 insertions(+), 36 deletions(-) create mode 100644 dexed.cpp rename PluginFx.cpp => not_used/PluginFx.cpp (100%) rename PluginFx.h => not_used/PluginFx.h (100%) diff --git a/DueDexed.ino b/DueDexed.ino index 04110c0..997d474 100644 --- a/DueDexed.ino +++ b/DueDexed.ino @@ -3,38 +3,22 @@ // $HOME/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/arm-none-eabi/include/sys/unistd.h: // //int _EXFUN(link, (const char *__path1, const char *__path2 )); // -#include "EngineMkI.h" -#include "EngineOpl.h" -#include "fm_core.h" -#include "exp2.h" -#include "sin.h" -#include "freqlut.h" -#include "lfo.h" -#include "pitchenv.h" -#include "env.h" -#include "controllers.h" -#include "PluginFx.h" -#include -//#include -const uint8_t rate = 128; -PluginFx fx; +#include +#include "dexed.h" + +#define RATE 128 + +Dexed* dexed=new Dexed(RATE); void setup() { - Tanh::init(); - Sin::init(); - Exp2::init(); - Freqlut::init(rate); - Lfo::init(rate); - PitchEnv::init(rate); - Env::init_sr(rate); - fx.init(rate); - + Serial.begin(115200); + Serial.println("Dexed"); } void loop() { - + } diff --git a/dexed.cpp b/dexed.cpp new file mode 100644 index 0000000..a231923 --- /dev/null +++ b/dexed.cpp @@ -0,0 +1,760 @@ +/** + * + * Copyright (c) 2016-2017 Holger Wirtz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "dexed.h" +#include "EngineMkI.h" +#include "EngineOpl.h" +#include "fm_core.h" +#include "exp2.h" +#include "sin.h" +#include "freqlut.h" +#include "controllers.h" +//#include "PluginFx.h" +#include +#include +#include + +Dexed::Dexed(double num_samples) +{ + uint8_t i; + + Exp2::init(); + Tanh::init(); + Sin::init(); + + _rate=num_samples; + + Freqlut::init(_rate); + Lfo::init(_rate); + PitchEnv::init(_rate); + Env::init_sr(_rate); + //fx.init(_rate); + + engineMkI=new EngineMkI; + engineOpl=new EngineOpl; + engineMsfa=new FmCore; + + for(i=0; iPARAM_CHANGE_LEVEL) + { + panic(); + controllers.refresh(); + } +} +*/ + +// override the run() method +void Dexed::run (uint8_t* midi_data) +{ + static int16_t buffer; + + ProcessMidiMessage(midi_data); + + // render audio from the last frame until the timestamp of this event + GetSamples(&buffer); + + //fx.process(&buffer,_rate); +} + +void Dexed::GetSamples(int16_t* buffer) +{ + uint32_t i; + + if(refreshVoice) { + for(i=0;i < max_notes;i++) { + if ( voices[i].live ) + voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity); + } + lfo.reset(data+137); + refreshVoice = false; + } + + // remaining buffer is still to be processed + for (; i < _rate; i += N) { + AlignedBuf audiobuf; + float sumbuf[N]; + + for (uint32_t j = 0; j < N; ++j) { + audiobuf.get()[j] = 0; + sumbuf[j] = 0.0; + } + + int32_t lfovalue = lfo.getsample(); + int32_t lfodelay = lfo.getdelay(); + + for (uint8_t note = 0; note < max_notes; ++note) { + if (voices[note].live) { + voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); + for (uint32_t j=0; j < N; ++j) { + int32_t val = audiobuf.get()[j]; + val = val >> 4; + int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; + float f = static_cast(clip_val>>1)/0x8000; + if(f>1) f=1; + if(f<-1) f=-1; + sumbuf[j]+=f; + audiobuf.get()[j]=0; + } + } + } + + for (uint32_t j = 0; j < N; ++j) + buffer[i + j] = sumbuf[j]; + } + + if(++_k_rate_counter%32 && !monoMode) + { + uint8_t op_carrier=controllers.core->get_carrier_operators(data[134]); // look for carriers + + for(i=0;i < max_notes;i++) + { + if(voices[i].live==true) + { + uint8_t op_amp=0; + uint8_t op_carrier_num=0; + + voices[i].dx7_note->peekVoiceStatus(voiceStatus); + + for(uint8_t op=0;op<6;op++) + { + uint8_t op_bit=pow(2,op); + + if((op_carrier&op_bit)>0) + { + // this voice is a carrier! + op_carrier_num++; + + if(voiceStatus.amp[op]<=1069 && voiceStatus.ampStep[op]==4) // this voice produces no audio output + op_amp++; + } + } + if(op_amp==op_carrier_num) + { + // all carrier-operators are silent -> disable the voice + voices[i].live=false; + voices[i].sustained=false; + voices[i].keydown=false; + } + } + } + } +} + +bool Dexed::ProcessMidiMessage(uint8_t *buf) { + uint8_t cmd = buf[0]; + + switch(cmd & 0xf0) { + case 0x80 : + // TRACE("MIDI keyup event: %d",buf[1]); + keyup(buf[1]); + return(false); + break; + case 0x90 : + // TRACE("MIDI keydown event: %d %d",buf[1],buf[2]); + keydown(buf[1], buf[2]); + return(false); + break; + case 0xb0 : { + uint8_t ctrl = buf[1]; + uint8_t value = buf[2]; + + switch(ctrl) { + case 1: + // TRACE("MIDI modwheel event: %d %d",ctrl,value); + controllers.modwheel_cc = value; + controllers.refresh(); + break; + case 2: + // TRACE("MIDI breath event: %d %d",ctrl,value); + controllers.breath_cc = value; + controllers.refresh(); + break; + case 4: + // TRACE("MIDI footsw event: %d %d",ctrl,value); + controllers.foot_cc = value; + controllers.refresh(); + break; + case 64: + // TRACE("MIDI sustain event: %d %d",ctrl,value); + sustain = value > 63; + if (!sustain) { + for (uint8_t note = 0; note < max_notes; note++) { + if (voices[note].sustained && !voices[note].keydown) { + voices[note].dx7_note->keyup(); + voices[note].sustained = false; + } + } + } + break; + case 120: + // TRACE("MIDI all-sound-off: %d %d",ctrl,value); + panic(); + return(true); + break; + case 123: + // TRACE("MIDI all-notes-off: %d %d",ctrl,value); + notes_off(); + return(true); + break; + } + break; + } + +// case 0xc0 : +// setCurrentProgram(buf[1]); +// break; + + // channel aftertouch + case 0xd0 : + // TRACE("MIDI aftertouch 0xd0 event: %d %d",buf[1]); + controllers.aftertouch_cc = buf[1]; + controllers.refresh(); + break; + // pitchbend + case 0xe0 : + // TRACE("MIDI pitchbend 0xe0 event: %d %d",buf[1],buf[2]); + controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7); + break; + + default: + // TRACE("MIDI event unknown: cmd=%d, val1=%d, val2=%d",buf[0],buf[1],buf[2]); + break; + } + + return(false); +} + +void Dexed::keydown(uint8_t pitch, uint8_t velo) { + if ( velo == 0 ) { + keyup(pitch); + return; + } + + pitch += data[144] - 24; + + uint8_t note = currentNote; + uint8_t keydown_counter=0; + + for (uint8_t i=0; iinit(data, pitch, velo); + if ( data[136] ) + voices[note].dx7_note->oscSync(); + break; + } + else + keydown_counter++; + + note = (note + 1) % max_notes; + } + + if(keydown_counter==0) + lfo.keydown(); + if ( monoMode ) { + for(uint8_t i=0; itransferSignal(*voices[i].dx7_note); + break; + } + if ( voices[i].midi_note < pitch ) { + voices[i].live = false; + voices[note].dx7_note->transferState(*voices[i].dx7_note); + break; + } + return; + } + } + } + + voices[note].live = true; +} + +void Dexed::keyup(uint8_t pitch) { + pitch += data[144] - 24; + + uint8_t note; + for (note=0; note= max_notes ) { + return; + } + + if ( monoMode ) { + int8_t highNote = -1; + int8_t target = 0; + for (int8_t i=0; i highNote ) { + target = i; + highNote = voices[i].midi_note; + } + } + + if ( highNote != -1 ) { + voices[note].live = false; + voices[target].live = true; + voices[target].dx7_note->transferState(*voices[note].dx7_note); + } + } + + if ( sustain ) { + voices[note].sustained = true; + } else { + voices[note].dx7_note->keyup(); + } +} + +/*void Dexed::onParam(uint8_t param_num,float param_val) +{ + int32_t tune; + + if(param_val!=data_float[param_num]) + { + _param_change_counter++; + + if(param_num==144 || param_num==134 || param_num==172) + panic(); + + refreshVoice=true; + data[param_num]=static_cast(param_val); + data_float[param_num]=param_val; + + switch(param_num) + { + case 155: + controllers.values_[kControllerPitchRange]=data[param_num]; + break; + case 156: + controllers.values_[kControllerPitchStep]=data[param_num]; + break; + case 157: + // TRACE("wheel.setRange(%d)",data[param_num]); + controllers.wheel.setRange(data[param_num]); + break; + case 158: + controllers.wheel.setTarget(data[param_num]); + break; + case 159: + controllers.foot.setRange(data[param_num]); + break; + case 160: + controllers.foot.setTarget(data[param_num]); + break; + case 161: + controllers.breath.setRange(data[param_num]); + break; + case 162: + controllers.breath.setTarget(data[param_num]); + break; + case 163: + controllers.at.setRange(data[param_num]); + break; + case 164: + controllers.at.setTarget(data[param_num]); + break; + case 165: + tune=param_val*0x4000; + controllers.masterTune=(tune<<11)*(1.0/12); + break; + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + controllers.opSwitch=(data[166]<<5)|(data[167]<<4)|(data[168]<<3)|(data[169]<<2)|(data[170]<<1)|data[171]; + break; + case 172: + max_notes=data[param_num]; + break; + } + } +} +*/ + +uint8_t Dexed::getEngineType() { + return engineType; +} + +void Dexed::setEngineType(uint8_t tp) { + if(engineType==tp && controllers.core!=NULL) + return; + + switch (tp) { + case DEXED_ENGINE_MARKI: + controllers.core = engineMkI; + break; + case DEXED_ENGINE_OPL: + controllers.core = engineOpl; + break; + default: + controllers.core = engineMsfa; + tp=DEXED_ENGINE_MODERN; + break; + } + engineType = tp; + panic(); + controllers.refresh(); +} + +bool Dexed::isMonoMode(void) { + return monoMode; +} + +void Dexed::setMonoMode(bool mode) { + if(monoMode==mode) + return; + + monoMode = mode; +} + +void Dexed::panic(void) { + for(uint8_t i=0;ioscSync(); + } + } + } +} + +void Dexed::notes_off(void) { + for(uint8_t i=0;i +class Dexed { public: Dexed(double rate); ~Dexed(); - void run(uint32_t sample_count); + void run(uint8_t* midi_data); void activate(void); void deactivate(void); uint8_t getEngineType(); @@ -83,14 +82,14 @@ class Dexed : public lvtk::Synth bool isMonoMode(void); void setMonoMode(bool mode); void set_params(void); - void GetSamples(uint32_t n_samples, float *buffer); + void GetSamples(int16_t* buffer); Controllers controllers; VoiceStatus voiceStatus; protected: - bool ProcessMidiMessage(const uint8_t *buf, uint32_t buf_size); - void onParam(uint8_t param_num,float param_val); + bool ProcessMidiMessage(uint8_t* buf); + //void onParam(uint8_t param_num,float param_val); void keyup(uint8_t pitch); void keydown(uint8_t pitch, uint8_t velo); void panic(void); @@ -104,7 +103,7 @@ class Dexed : public lvtk::Synth bool monoMode; bool refreshVoice; uint8_t engineType; - PluginFx fx; + //PluginFx fx; Lfo lfo; FmCore* engineMsfa; EngineMkI* engineMkI; @@ -115,10 +114,9 @@ class Dexed : public lvtk::Synth uint32_t extra_buf_size_; private: - double _rate; + uint16_t _rate; uint8_t _k_rate_counter; uint8_t _param_change_counter; - float data_float[173]; uint8_t data[173]={ 95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, 95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, diff --git a/PluginFx.cpp b/not_used/PluginFx.cpp similarity index 100% rename from PluginFx.cpp rename to not_used/PluginFx.cpp diff --git a/PluginFx.h b/not_used/PluginFx.h similarity index 100% rename from PluginFx.h rename to not_used/PluginFx.h