// from: http://ll-plugins.nongnu.org/lv2pftci/#A_synth #include #include "dexed.peg" #include "dexed.h" #include "EngineMkI.h" #include "EngineOpl.h" #include "msfa/exp2.h" #include "msfa/sin.h" #include "msfa/freqlut.h" #include "msfa/controllers.h" #include "PluginFx.h" #include #include Dexed::Dexed(double rate) : lvtk::Synth(p_n_ports, p_midi_in) { TRACE("Hi"); bufsize_=256; outbuf_=new float[bufsize_]; Exp2::init(); Tanh::init(); Sin::init(); currentNote = -1; engineType = -1; monoMode = 0; normalizeDxVelocity = false; memset(&voiceStatus, 0, sizeof(VoiceStatus)); setEngineType(DEXED_ENGINE_MARKI); Freqlut::init(rate); Lfo::init(rate); PitchEnv::init(rate); Env::init_sr(rate); fx.init(rate); for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { voices[note].dx7_note = new Dx7Note; voices[note].keydown = false; voices[note].sustained = false; voices[note].live = false; } currentNote = 0; controllers.values_[kControllerPitch] = 0x2000; controllers.modwheel_cc = 0; controllers.foot_cc = 0; controllers.breath_cc = 0; controllers.aftertouch_cc = 0; sustain = false; lfo.reset(data + 137); extra_buf_size_ = 0; add_voices(new DexedVoice(rate)); add_audio_outputs(p_audio_out); TRACE("Bye"); } Dexed::~Dexed() { TRACE("Hi"); delete [] outbuf_; currentNote = -1; for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { if ( voices[note].dx7_note != NULL ) { delete voices[note].dx7_note; voices[note].dx7_note = NULL; } voices[note].keydown = false; voices[note].sustained = false; voices[note].live = false; } TRACE("Bye"); } void Dexed::set_params(void) { /* // Dexed-Unisono if(getEngineType()!=int(*p(p_unisono))) { panic(); setMonoMode(int(*p(p_unisono))); } */ // Dexed-Engine if(getEngineType()!=int(*p(p_engine))) { panic(); setEngineType(int(*p(p_engine))); } // Dexed-Filter if(fx.uiCutoff!=*p(p_cutoff)) { fx.uiCutoff=*p(p_cutoff); refreshVoice=true; } if(fx.uiReso!=*p(p_resonance)) { fx.uiReso=*p(p_resonance); refreshVoice=true; } if(fx.uiGain!=*p(p_output)) { fx.uiGain=*p(p_output); refreshVoice=true; } // OP6 onParam(0,static_cast(*p(p_op6_eg_rate_1))); onParam(1,static_cast(*p(p_op6_eg_rate_2))); onParam(2,static_cast(*p(p_op6_eg_rate_3))); onParam(3,static_cast(*p(p_op6_eg_rate_4))); onParam(4,static_cast(*p(p_op6_eg_level_1))); onParam(5,static_cast(*p(p_op6_eg_level_2))); onParam(6,static_cast(*p(p_op6_eg_level_3))); onParam(7,static_cast(*p(p_op6_eg_level_4))); onParam(8,static_cast(*p(p_op6_kbd_lev_scl_brk_pt))); onParam(9,static_cast(*p(p_op6_kbd_lev_scl_lft_depth))); onParam(10,static_cast(*p(p_op6_kbd_lev_scl_rht_depth))); onParam(11,static_cast(*p(p_op6_kbd_lev_scl_lft_curve))); onParam(12,static_cast(*p(p_op6_kbd_lev_scl_rht_curve))); onParam(13,static_cast(*p(p_op6_kbd_rate_scaling))); onParam(14,static_cast(*p(p_op6_amp_mod_sensitivity))); onParam(15,static_cast(*p(p_op6_key_vel_sensitivity))); onParam(16,static_cast(*p(p_op6_operator_output_level))); onParam(17,static_cast(*p(p_op6_osc_mode))); onParam(18,static_cast(*p(p_op6_osc_freq_coarse))); onParam(19,static_cast(*p(p_op6_osc_freq_fine))); onParam(20,static_cast(*p(p_op6_osc_detune)+7)); // OP5 onParam(21,static_cast(*p(p_op5_eg_rate_1))); onParam(22,static_cast(*p(p_op5_eg_rate_2))); onParam(23,static_cast(*p(p_op5_eg_rate_3))); onParam(24,static_cast(*p(p_op5_eg_rate_4))); onParam(25,static_cast(*p(p_op5_eg_level_1))); onParam(26,static_cast(*p(p_op5_eg_level_2))); onParam(27,static_cast(*p(p_op5_eg_level_3))); onParam(28,static_cast(*p(p_op5_eg_level_4))); onParam(29,static_cast(*p(p_op5_kbd_lev_scl_brk_pt))); onParam(30,static_cast(*p(p_op5_kbd_lev_scl_lft_depth))); onParam(31,static_cast(*p(p_op5_kbd_lev_scl_rht_depth))); onParam(32,static_cast(*p(p_op5_kbd_lev_scl_lft_curve))); onParam(33,static_cast(*p(p_op5_kbd_lev_scl_rht_curve))); onParam(34,static_cast(*p(p_op5_kbd_rate_scaling))); onParam(35,static_cast(*p(p_op5_amp_mod_sensitivity))); onParam(36,static_cast(*p(p_op5_key_vel_sensitivity))); onParam(37,static_cast(*p(p_op5_operator_output_level))); onParam(38,static_cast(*p(p_op5_osc_mode))); onParam(39,static_cast(*p(p_op5_osc_freq_coarse))); onParam(40,static_cast(*p(p_op5_osc_freq_fine))); onParam(41,static_cast(*p(p_op5_osc_detune)+7)); // OP4 onParam(42,static_cast(*p(p_op4_eg_rate_1))); onParam(43,static_cast(*p(p_op4_eg_rate_2))); onParam(44,static_cast(*p(p_op4_eg_rate_3))); onParam(45,static_cast(*p(p_op4_eg_rate_4))); onParam(46,static_cast(*p(p_op4_eg_level_1))); onParam(47,static_cast(*p(p_op4_eg_level_2))); onParam(48,static_cast(*p(p_op4_eg_level_3))); onParam(49,static_cast(*p(p_op4_eg_level_4))); onParam(50,static_cast(*p(p_op4_kbd_lev_scl_brk_pt))); onParam(51,static_cast(*p(p_op4_kbd_lev_scl_lft_depth))); onParam(52,static_cast(*p(p_op4_kbd_lev_scl_rht_depth))); onParam(53,static_cast(*p(p_op4_kbd_lev_scl_lft_curve))); onParam(54,static_cast(*p(p_op4_kbd_lev_scl_rht_curve))); onParam(55,static_cast(*p(p_op4_kbd_rate_scaling))); onParam(56,static_cast(*p(p_op4_amp_mod_sensitivity))); onParam(57,static_cast(*p(p_op4_key_vel_sensitivity))); onParam(58,static_cast(*p(p_op4_operator_output_level))); onParam(59,static_cast(*p(p_op4_osc_mode))); onParam(60,static_cast(*p(p_op4_osc_freq_coarse))); onParam(61,static_cast(*p(p_op4_osc_freq_fine))); onParam(62,static_cast(*p(p_op4_osc_detune)+7)); // OP3 onParam(63,static_cast(*p(p_op3_eg_rate_1))); onParam(64,static_cast(*p(p_op3_eg_rate_2))); onParam(65,static_cast(*p(p_op3_eg_rate_3))); onParam(66,static_cast(*p(p_op3_eg_rate_4))); onParam(67,static_cast(*p(p_op3_eg_level_1))); onParam(68,static_cast(*p(p_op3_eg_level_2))); onParam(69,static_cast(*p(p_op3_eg_level_3))); onParam(70,static_cast(*p(p_op3_eg_level_4))); onParam(71,static_cast(*p(p_op3_kbd_lev_scl_brk_pt))); onParam(72,static_cast(*p(p_op3_kbd_lev_scl_lft_depth))); onParam(73,static_cast(*p(p_op3_kbd_lev_scl_rht_depth))); onParam(74,static_cast(*p(p_op3_kbd_lev_scl_lft_curve))); onParam(75,static_cast(*p(p_op3_kbd_lev_scl_rht_curve))); onParam(76,static_cast(*p(p_op3_kbd_rate_scaling))); onParam(77,static_cast(*p(p_op3_amp_mod_sensitivity))); onParam(78,static_cast(*p(p_op3_key_vel_sensitivity))); onParam(79,static_cast(*p(p_op3_operator_output_level))); onParam(80,static_cast(*p(p_op3_osc_mode))); onParam(81,static_cast(*p(p_op3_osc_freq_coarse))); onParam(82,static_cast(*p(p_op3_osc_freq_fine))); onParam(83,static_cast(*p(p_op3_osc_detune)+7)); // OP2 onParam(84,static_cast(*p(p_op2_eg_rate_1))); onParam(85,static_cast(*p(p_op2_eg_rate_2))); onParam(86,static_cast(*p(p_op2_eg_rate_3))); onParam(87,static_cast(*p(p_op2_eg_rate_4))); onParam(88,static_cast(*p(p_op2_eg_level_1))); onParam(89,static_cast(*p(p_op2_eg_level_2))); onParam(90,static_cast(*p(p_op2_eg_level_3))); onParam(91,static_cast(*p(p_op2_eg_level_4))); onParam(92,static_cast(*p(p_op2_kbd_lev_scl_brk_pt))); onParam(93,static_cast(*p(p_op2_kbd_lev_scl_lft_depth))); onParam(94,static_cast(*p(p_op2_kbd_lev_scl_rht_depth))); onParam(95,static_cast(*p(p_op2_kbd_lev_scl_lft_curve))); onParam(96,static_cast(*p(p_op2_kbd_lev_scl_rht_curve))); onParam(97,static_cast(*p(p_op2_kbd_rate_scaling))); onParam(98,static_cast(*p(p_op2_amp_mod_sensitivity))); onParam(99,static_cast(*p(p_op2_key_vel_sensitivity))); onParam(100,static_cast(*p(p_op2_operator_output_level))); onParam(101,static_cast(*p(p_op2_osc_mode))); onParam(102,static_cast(*p(p_op2_osc_freq_coarse))); onParam(103,static_cast(*p(p_op2_osc_freq_fine))); onParam(104,static_cast(*p(p_op2_osc_detune)+7)); // OP1 onParam(105,static_cast(*p(p_op1_eg_rate_1))); onParam(106,static_cast(*p(p_op1_eg_rate_2))); onParam(107,static_cast(*p(p_op1_eg_rate_3))); onParam(108,static_cast(*p(p_op1_eg_rate_4))); onParam(109,static_cast(*p(p_op1_eg_level_1))); onParam(110,static_cast(*p(p_op1_eg_level_2))); onParam(111,static_cast(*p(p_op1_eg_level_3))); onParam(112,static_cast(*p(p_op1_eg_level_4))); onParam(113,static_cast(*p(p_op1_kbd_lev_scl_brk_pt))); onParam(114,static_cast(*p(p_op1_kbd_lev_scl_lft_depth))); onParam(115,static_cast(*p(p_op1_kbd_lev_scl_rht_depth))); onParam(116,static_cast(*p(p_op1_kbd_lev_scl_lft_curve))); onParam(117,static_cast(*p(p_op1_kbd_lev_scl_rht_curve))); onParam(118,static_cast(*p(p_op1_kbd_rate_scaling))); onParam(119,static_cast(*p(p_op1_amp_mod_sensitivity))); onParam(120,static_cast(*p(p_op1_key_vel_sensitivity))); onParam(121,static_cast(*p(p_op1_operator_output_level))); onParam(122,static_cast(*p(p_op1_osc_mode))); onParam(123,static_cast(*p(p_op1_osc_freq_coarse))); onParam(124,static_cast(*p(p_op1_osc_freq_fine))); onParam(125,static_cast(*p(p_op1_osc_detune)+7)); // Global for all OPs onParam(126,static_cast(*p(p_pitch_eg_rate_1))); onParam(127,static_cast(*p(p_pitch_eg_rate_2))); onParam(128,static_cast(*p(p_pitch_eg_rate_3))); onParam(129,static_cast(*p(p_pitch_eg_rate_4))); onParam(130,static_cast(*p(p_pitch_eg_level_1))); onParam(131,static_cast(*p(p_pitch_eg_level_2))); onParam(132,static_cast(*p(p_pitch_eg_level_3))); onParam(133,static_cast(*p(p_pitch_eg_level_4))); onParam(134,static_cast(*p(p_algorithm_num)-1)); onParam(135,static_cast(*p(p_feedback))); onParam(136,static_cast(*p(p_oscillator_sync))); onParam(137,static_cast(*p(p_lfo_speed))); onParam(138,static_cast(*p(p_lfo_delay))); onParam(139,static_cast(*p(p_lfo_pitch_mod_depth))); onParam(140,static_cast(*p(p_lfo_amp_mod_depth))); onParam(141,static_cast(*p(p_lfo_sync))); onParam(142,static_cast(*p(p_lfo_waveform))); onParam(143,static_cast(*p(p_pitch_mod_sensitivity))); onParam(144,static_cast(*p(p_transpose))); // 10 bytes (145-154) are the name of the patch onParam(155,0x3f); // operator on/off => All OPs on } // override the run() method void Dexed::run (uint32_t sample_count) { const LV2_Atom_Sequence* seq = p (p_midi_in); float* output = p(p_audio_out); uint32_t last_frame = 0, num_this_time = 0; set_params(); // pre_process: copy actual voice params for (LV2_Atom_Event* ev = lv2_atom_sequence_begin (&seq->body); !lv2_atom_sequence_is_end(&seq->body, seq->atom.size, ev); ev = lv2_atom_sequence_next (ev)) { num_this_time = ev->time.frames - last_frame; // If it's midi, send it to the engine if (ev->body.type == m_midi_type) { #ifdef DEBUG for(uint i=0;ibody.size;i++) { TRACE("midi msg %d: %d\n",i,((uint8_t*)LV2_ATOM_BODY(&ev->body))[i]); } #endif if(ProcessMidiMessage((uint8_t*) LV2_ATOM_BODY (&ev->body),ev->body.size)==false) break; } // render audio from the last frame until the timestamp of this event GetSamples (num_this_time, outbuf_); // i is the index of the engine's buf, which always starts at 0 (i think) // j is the index of the plugin's float output buffer which will be the timestamp // of the last processed atom event. for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j) output[j]=outbuf_[i]; last_frame = ev->time.frames; } // render remaining samples if any left if (last_frame < sample_count) { // do the same thing as above except from last frame until the end of // the processing cycles last sample. at this point, all events have // already been handled. num_this_time = sample_count - last_frame; GetSamples (num_this_time, outbuf_); for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j) output[j] = outbuf_[i]; } fx.process(output, sample_count); } void Dexed::GetSamples(int n_samples, float *buffer) { int i; VoiceStatus vs; for(i=0;i < MAX_ACTIVE_NOTES;i++) { if(voices[i].live==true &&voices[i].keydown==false) { uint8_t op_amp=0; voices[i].dx7_note->peekVoiceStatus(vs); for(int op=0;op<6;op++) { TRACE("Voice[%2d] OP [%d] amp=%ld,amp_step=%d,pitch_step=%d",i,op,vs.amp[op],vs.ampStep[op],vs.pitchStep); if(vs.amp[op]<=1069) op_amp++; } if(op_amp==6) voices[i].live=false; } TRACE("Voice[%2d] live=%d keydown=%d",i,voices[i].live,voices[i].keydown); } if ( refreshVoice ) { for(i=0;i < MAX_ACTIVE_NOTES;i++) { if ( voices[i].live ) voices[i].dx7_note->update(data, voices[i].midi_note, feedback_bitdepth); } lfo.reset(data + 137); refreshVoice = false; } // flush first events for (i=0; i < n_samples && i < extra_buf_size_; i++) { buffer[i] = extra_buf_[i]; } // remaining buffer is still to be processed if (extra_buf_size_ > n_samples) { for (int j = 0; j < extra_buf_size_ - n_samples; j++) { extra_buf_[j] = extra_buf_[j + n_samples]; } extra_buf_size_ -= n_samples; } else { for (; i < n_samples; i += N) { AlignedBuf audiobuf; float sumbuf[N]; for (int j = 0; j < N; ++j) { audiobuf.get()[j] = 0; sumbuf[j] = 0.0; } int32_t lfovalue = lfo.getsample(); int32_t lfodelay = lfo.getdelay(); for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) { if (voices[note].live) { voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); for (int j=0; j < N; ++j) { int32_t val = audiobuf.get()[j]; val = val >> 4; int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; float f = ((float) clip_val) / (float) 0x8000; if(f>1.0) f=1.0; if(f<-1.0) f=-1.0; sumbuf[j]+=f; audiobuf.get()[j]=0; } } } int jmax = n_samples - i; for (int j = 0; j < N; ++j) { if (j < jmax) { buffer[i + j] = sumbuf[j]; } else { extra_buf_[j - jmax] = sumbuf[j]; } } } extra_buf_size_ = i - n_samples; } } bool Dexed::ProcessMidiMessage(const uint8_t *buf, int buf_size) { TRACE("Hi"); uint8_t cmd = buf[0]; switch(cmd & 0xf0) { case 0x80 : keyup(buf[1]); return(true); case 0x90 : keydown(buf[1], buf[2]); return(true); case 0xb0 : { int ctrl = buf[1]; int value = buf[2]; switch(ctrl) { case 1: controllers.modwheel_cc = value; controllers.refresh(); break; case 2: controllers.breath_cc = value; controllers.refresh(); break; case 4: controllers.foot_cc = value; controllers.refresh(); break; case 64: sustain = value > 63; if (!sustain) { for (int note = 0; note < MAX_ACTIVE_NOTES; note++) { if (voices[note].sustained && !voices[note].keydown) { voices[note].dx7_note->keyup(); voices[note].sustained = false; } } } break; } } return(true); case 0xc0 : //setCurrentProgram(buf[1]); return(true); // aftertouch case 0xd0 : controllers.aftertouch_cc = buf[1]; controllers.refresh(); return(true); } switch (cmd) { case 0xe0 : controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7); return(true); break; } TRACE("MIDI event unknown: cmd=%d, val1=%d, val2=%d",buf[0],buf[1],buf[2]); TRACE("Bye"); return(false); } void Dexed::keydown(uint8_t pitch, uint8_t velo) { TRACE("Hi"); TRACE("pitch=%d, velo=%d\n",pitch,velo); if ( velo == 0 ) { keyup(pitch); return; } pitch += data[144] - 24; if ( normalizeDxVelocity ) { velo = ((float)velo) * 0.7874015; // 100/127 } int note = currentNote; for (int i=0; iinit(data, pitch, velo, feedback_bitdepth); if ( data[136] ) voices[note].dx7_note->oscSync(); break; } note = (note + 1) % MAX_ACTIVE_NOTES; } if ( monoMode ) { for(int 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; TRACE("Bye"); } void Dexed::keyup(uint8_t pitch) { TRACE("Hi"); TRACE("pitch=%d\n",pitch); pitch += data[144] - 24; int note; for (note=0; note= MAX_ACTIVE_NOTES ) { TRACE("note-off not found???"); return; } if ( monoMode ) { int highNote = -1; int target = 0; for (int 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(); } TRACE("Bye"); } void Dexed::onParam(int param_num,int param_val) { if(param_val!=data[param_num]) { refreshVoice=true; data[param_num]=param_val; } } int Dexed::getEngineType() { return engineType; } void Dexed::setEngineType(int tp) { TRACE("settings engine %d", tp); switch (tp) { case DEXED_ENGINE_MARKI: controllers.core = &engineMkI; feedback_bitdepth = 11; break; case DEXED_ENGINE_OPL: controllers.core = &engineOpl; feedback_bitdepth = 11; break; default: controllers.core = &engineMsfa; feedback_bitdepth = 8; break; } engineType = tp; } void Dexed::setMonoMode(bool mode) { monoMode = mode; } void Dexed::panic(void) { for(int i=0;ioscSync(); } } } //============================================================================== DexedVoice::DexedVoice(double rate) : m_key(lvtk::INVALID_KEY), m_rate(rate) { TRACE("Hi"); TRACE("Bye"); } DexedVoice::~DexedVoice() { TRACE("Hi"); TRACE("Bye"); } void DexedVoice::on(unsigned char key, unsigned char velocity) { TRACE("Hi"); m_key = key; TRACE("Bye"); } void DexedVoice::off(unsigned char velocity) { TRACE("Hi"); m_key = lvtk::INVALID_KEY; TRACE("Bye"); } unsigned char DexedVoice::get_key(void) const { TRACE("Hi"); return m_key; TRACE("Bye"); } static int _ = Dexed::register_class(p_uri);