diff --git a/src/dexed.cpp b/src/dexed.cpp index f9af126..da4b18e 100644 --- a/src/dexed.cpp +++ b/src/dexed.cpp @@ -85,6 +85,7 @@ Dexed::Dexed(double rate) : lvtk::Synth(p_n_ports, p_midi_in) TRACE("%d->%f",i,data_float[i]); } + lastKeyDown = -1; max_notes=16; currentNote = 0; controllers.values_[kControllerPitch] = 0x2000; @@ -96,7 +97,8 @@ Dexed::Dexed(double rate) : lvtk::Synth(p_n_ports, p_midi_in) controllers.aftertouch_cc = 0; controllers.masterTune=0; controllers.opSwitch=0x3f; // enable all operators - //controllers.opSwitch=0x00; + controllers.portamento_enable_cc = false; + controllers.portamento_cc = 0; bufsize_=256; @@ -447,7 +449,7 @@ void Dexed::GetSamples(uint32_t n_samples, float* buffer) 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); + voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity, voices[i].porta); } lfo.reset(data+137); refreshVoice = false; @@ -687,6 +689,9 @@ bool Dexed::ProcessMidiMessage(const uint8_t *buf, uint32_t buf_size) { controllers.foot_cc = value; controllers.refresh(); break; + case 5: + controllers.portamento_cc = value; + break; case 64: TRACE("MIDI sustain event: %d %d",ctrl,value); sustain = value > 63; @@ -699,6 +704,9 @@ bool Dexed::ProcessMidiMessage(const uint8_t *buf, uint32_t buf_size) { } } break; + case 65: + controllers.portamento_enable_cc = value >= 64; + break; case 120: TRACE("MIDI all-sound-off: %d %d",ctrl,value); panic(); @@ -769,7 +777,14 @@ TRACE("pitch=%d, velo=%d\n",pitch,velo); } pitch += data[144] - 24; - + + int previousKeyDown = lastKeyDown; + lastKeyDown = pitch; + + int porta = -1; + if ( controllers.portamento_enable_cc && previousKeyDown >= 0 ) + porta = controllers.portamento_cc; + uint8_t note = currentNote; uint8_t keydown_counter=0; @@ -778,9 +793,10 @@ TRACE("pitch=%d, velo=%d\n",pitch,velo); currentNote = (note + 1) % max_notes; voices[note].midi_note = pitch; voices[note].velocity = velo; + voices[note].porta = porta; voices[note].sustained = sustain; voices[note].keydown = true; - voices[note].dx7_note->init(data, pitch, velo); + voices[note].dx7_note->init(data, pitch, velo, previousKeyDown, porta); if ( data[136] ) voices[note].dx7_note->oscSync(); break; diff --git a/src/dexed.h b/src/dexed.h index 8791d0f..dc5e355 100644 --- a/src/dexed.h +++ b/src/dexed.h @@ -39,6 +39,7 @@ struct ProcessorVoice { uint8_t midi_note; uint8_t velocity; + int porta; bool keydown; bool sustained; bool live; @@ -121,6 +122,7 @@ class Dexed : public lvtk::Synth uint32_t bufsize_; float extra_buf_[N]; uint32_t extra_buf_size_; + int lastKeyDown; private: double _rate; diff --git a/src/msfa/dx7note.cc b/src/msfa/dx7note.cc index ce34341..dde1752 100644 --- a/src/msfa/dx7note.cc +++ b/src/msfa/dx7note.cc @@ -19,6 +19,7 @@ #include #include "synth.h" #include "freqlut.h" +#include "porta.h" #include "exp2.h" #include "controllers.h" #include "dx7note.h" @@ -31,6 +32,13 @@ int32_t midinote_to_logfreq(int midinote) { 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, @@ -144,7 +152,7 @@ Dx7Note::Dx7Note() { } } -void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { +void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity, int srcnote, int porta) { int rates[4]; int levels[4]; for (int op = 0; op < 6; op++) { @@ -173,6 +181,9 @@ void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { opMode[op] = mode; basepitch_[op] = freq; ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; + + if (porta >= 0) + porta_curpitch_[op] = osc_freq(srcnote, mode, coarse, fine, detune); } for (int i = 0; i < 4; i++) { rates[i] = patch[126 + i]; @@ -185,6 +196,8 @@ void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { pitchmoddepth_ = (patch[139] * 165) >> 6; pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; ampmoddepth_ = (patch[140] * 165) >> 6; + porta_rateindex_ = (porta < 128) ? porta : 127; + porta_gliss_ = patch[68]; } void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) { @@ -232,12 +245,20 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co 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_[op] + pitch_base); - else - params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_mod); - + params_[op].freq = Freqlut::lookup(basepitch + pitch_base); + else { + if ( porta_rateindex_ >= 0 ) { + basepitch = porta_curpitch_[op]; + if ( porta_gliss_ ) + basepitch = logfreq_round2semi(basepitch); + } + params_[op].freq = Freqlut::lookup(basepitch + pitch_mod); + } + int32_t level = env_[op].getsample(); if (ampmodsens_[op] != 0) { uint32_t sensamp = (uint32_t)(((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24); @@ -250,6 +271,25 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co params_[op].level_in = level; } } + + // ==== PORTAMENTO ==== + int porta = porta_rateindex_; + if ( porta >= 0 ) { + int32_t rate = Porta::rates[porta]; + for (int op = 0; op < 6; op++) { + int32_t cur = porta_curpitch_[op]; + int32_t dst = basepitch_[op]; + + bool going_up = cur < dst; + int32_t newpitch = cur + (going_up ? +rate : -rate); + + if ( going_up ? (cur > dst) : (cur < dst) ) + newpitch = dst; + + porta_curpitch_[op] = newpitch; + } + } + ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_); } @@ -260,7 +300,7 @@ void Dx7Note::keyup() { pitchenv_.keydown(false); } -void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) { +void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity, int porta) { int rates[4]; int levels[4]; for (int op = 0; op < 6; op++) { @@ -272,7 +312,10 @@ void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) { basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune); ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; opMode[op] = mode; - + + if (porta >= 0) + porta_curpitch_[op] = basepitch_[op]; + for (int i = 0; i < 4; i++) { rates[i] = patch[off + i]; levels[i] = patch[off + 4 + i]; @@ -295,6 +338,8 @@ void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) { pitchmoddepth_ = (patch[139] * 165) >> 6; pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; ampmoddepth_ = (patch[140] * 165) >> 6; + porta_rateindex_ = (porta < 128) ? porta : 127; + porta_gliss_ = patch[68]; } void Dx7Note::peekVoiceStatus(VoiceStatus &status) { diff --git a/src/msfa/dx7note.h b/src/msfa/dx7note.h index da0120e..953e165 100644 --- a/src/msfa/dx7note.h +++ b/src/msfa/dx7note.h @@ -37,7 +37,7 @@ struct VoiceStatus { class Dx7Note { public: Dx7Note(); - void init(const uint8_t patch[156], int midinote, int velocity); + void init(const uint8_t patch[156], int midinote, int velocity, int srcnote, int porta); // Note: this _adds_ to the buffer. Interesting question whether it's // worth it... @@ -52,7 +52,7 @@ public: // keyup, that won't work. // PG:add the update - void update(const uint8_t patch[156], int midinote, int velocity); + void update(const uint8_t patch[156], int midinote, int velocity, int porta); void peekVoiceStatus(VoiceStatus &status); void transferState(Dx7Note& src); void transferSignal(Dx7Note &src); @@ -72,6 +72,10 @@ private: int algorithm_; int pitchmoddepth_; int pitchmodsens_; + + int porta_rateindex_; + int porta_gliss_; + int32_t porta_curpitch_[6]; }; #endif // SYNTH_DX7NOTE_H_