/* Copyright 2008-2011 David Robillard Copyright 1999-2000 Paul Kellett (Maxim Digital Audio) This 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 software 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 software. If not, see . */ #include "mdaJX10.h" #include #include //rand() #include AudioEffect *createEffectInstance(audioMasterCallback audioMaster) { return new mdaJX10(audioMaster); } mdaJX10Program::mdaJX10Program() { param[0] = 0.00f; //OSC Mix param[1] = 0.25f; //OSC Tune param[2] = 0.50f; //OSC Fine param[3] = 0.00f; //OSC Mode param[4] = 0.35f; //OSC Rate param[5] = 0.50f; //OSC Bend param[6] = 1.00f; //VCF Freq param[7] = 0.15f; //VCF Reso param[8] = 0.75f; //VCF postUpdate(); } void mdaJX10::fillpatch(LvzInt32 p, const char *name, float p0, float p1, float p2, float p3, float p4, float p5, float p6, float p7, float p8, float p9, float p10, float p11, float p12, float p13, float p14, float p15, float p16, float p17, float p18, float p19, float p20, float p21, float p22, float p23) { strcpy(programs[p].name, name); programs[p].param[0] = p0; programs[p].param[1] = p1; programs[p].param[2] = p2; programs[p].param[3] = p3; programs[p].param[4] = p4; programs[p].param[5] = p5; programs[p].param[6] = p6; programs[p].param[7] = p7; programs[p].param[8] = p8; programs[p].param[9] = p9; programs[p].param[10] = p10; programs[p].param[11] = p11; programs[p].param[12] = p12; programs[p].param[13] = p13; programs[p].param[14] = p14; programs[p].param[15] = p15; programs[p].param[16] = p16; programs[p].param[17] = p17; programs[p].param[18] = p18; programs[p].param[19] = p19; programs[p].param[20] = p20; programs[p].param[21] = p21; programs[p].param[22] = p22; programs[p].param[23] = p23; } float mdaJX10::getParameter(LvzInt32 index) { return programs[curProgram].param[index]; } void mdaJX10::setProgramName(char *name) { strcpy(programs[curProgram].name, name); } void mdaJX10::getProgramName(char *name) { strcpy(name, programs[curProgram].name); } void mdaJX10::setBlockSize(LvzInt32 blockSize) { AudioEffectX::setBlockSize(blockSize); } bool mdaJX10::getEffectName(char* name) { strcpy(name, "MDA JX10 Synth"); return true; } bool mdaJX10::getVendorString(char* text) { strcpy(text, "MDA"); return true; } bool mdaJX10::getProductString(char* text) { strcpy(text, "MDA JX10 Synth"); return true; } bool mdaJX10::getOutputProperties(LvzInt32 index, LvzPinProperties* properties) { if(indexlabel, "JX10%d", index + 1); properties->flags = kLvzPinIsActive; if(index<2) properties->flags |= kLvzPinIsStereo; //make channel 1+2 stereo return true; } return false; } bool mdaJX10::getProgramNameIndexed(LvzInt32 category, LvzInt32 index, char* text) { if ((unsigned int)index < NPROGS) { strcpy(text, programs[index].name); return true; } return false; } bool mdaJX10::copyProgram(LvzInt32 destination) { if(destination0 || notes[event]sampleFrames) frames = sampleFrames; frames -= frame; frame += frames; while(--frames>=0) { VOICE *V = voice; o = 0.0f; noise = (noise * 196314165) + 907633515; r = (noise & 0x7FFFFF) + 0x40000000; //generate noise + fast convert to float w = *(float *)&r; w = ww * (w - 3.0f); if(--k<0) { lfo += dlfo; if(lfo>PI) lfo -= TWOPI; vib = (float)sin(lfo); ff = filtf + filtwhl + (filtlfo + press) * vib; pwm = 1.0f + vib * (modwhl + pwmdep); vib = 1.0f + vib * (modwhl + vibrato); k = KMAX; } for(v=0; venv; if(e > SILENCE) { //Sinc-Loop Oscillator x = V->p + V->dp; if(x > min) { if(x > V->pmax) { x = V->pmax + V->pmax - x; V->dp = -V->dp; } V->p = x; x = V->sin0 * V->sinx - V->sin1; //sine osc V->sin1 = V->sin0; V->sin0 = x; x = x / V->p; } else { V->p = x = - x; V->dp = V->period * vib * pb; //set period for next cycle V->pmax = (float)floor(0.5f + V->dp) - 0.5f; V->dc = -0.5f * V->lev / V->pmax; V->pmax *= PI; V->dp = V->pmax / V->dp; V->sin0 = V->lev * (float)sin(x); V->sin1 = V->lev * (float)sin(x - V->dp); V->sinx = 2.0f * (float)cos(V->dp); if(x*x > .1f) x = V->sin0 / x; else x = V->lev; //was 0.01f; } y = V->p2 + V->dp2; //osc2 if(y > min) { if(y > V->pmax2) { y = V->pmax2 + V->pmax2 - y; V->dp2 = -V->dp2; } V->p2 = y; y = V->sin02 * V->sinx2 - V->sin12; V->sin12 = V->sin02; V->sin02 = y; y = y / V->p2; } else { V->p2 = y = - y; V->dp2 = V->period * V->detune * pwm * pb; V->pmax2 = (float)floor(0.5f + V->dp2) - 0.5f; V->dc2 = -0.5f * V->lev2 / V->pmax2; V->pmax2 *= PI; V->dp2 = V->pmax2 / V->dp2; V->sin02 = V->lev2 * (float)sin(y); V->sin12 = V->lev2 * (float)sin(y - V->dp2); V->sinx2 = 2.0f * (float)cos(V->dp2); if(y*y > .1f) y = V->sin02 / y; else y = V->lev2; } V->saw = V->saw * hpf + V->dc + x - V->dc2 - y; //integrated sinc = saw x = V->saw + w; V->env += V->envd * (V->envl - V->env); if(k==KMAX) //filter freq update at LFO rate { if((V->env+V->envl)>3.0f) { V->envd=dec; V->envl=sus; } //envelopes V->fenv += V->fenvd * (V->fenvl - V->fenv); if((V->fenv+V->fenvl)>3.0f) { V->fenvd=fdec; V->fenvl=fsus; } fz += 0.005f * (ff - fz); //filter zipper noise filter y = V->fc * (float)exp(fz + fe * V->fenv) * ipb; //filter cutoff if(y<0.005f) y=0.005f; V->ff = y; V->period += gl * (V->target - V->period); //glide if(V->target < V->period) V->period += gl * (V->target - V->period); } if(V->ff > fx) V->ff = fx; //stability limit V->f0 += V->ff * V->f1; //state-variable filter V->f1 -= V->ff * (V->f0 + fq * V->f1 - x - V->f2); V->f1 -= 0.2f * V->f1 * V->f1 * V->f1; //soft limit //was 0.08f V->f2 = x; o += V->env * V->f0; } V++; } *out1++ += o; *out2++ += o; } if(frame0 || notes[event]sampleFrames) frames = sampleFrames; frames -= frame; frame += frames; while(--frames>=0) { VOICE *V = voice; o = 0.0f; noise = (noise * 196314165) + 907633515; r = (noise & 0x7FFFFF) + 0x40000000; //generate noise + fast convert to float w = *(float *)&r; w = ww * (w - 3.0f); if(--k<0) { lfo += dlfo; if(lfo>PI) lfo -= TWOPI; vib = (float)sin(lfo); ff = filtf + filtwhl + (filtlfo + press) * vib; pwm = 1.0f + vib * (modwhl + pwmdep); vib = 1.0f + vib * (modwhl + vibrato); k = KMAX; } for(v=0; venv; if(e > SILENCE) { //Sinc-Loop Oscillator x = V->p + V->dp; if(x > min) { if(x > V->pmax) { x = V->pmax + V->pmax - x; V->dp = -V->dp; } V->p = x; x = V->sin0 * V->sinx - V->sin1; //sine osc V->sin1 = V->sin0; V->sin0 = x; x = x / V->p; } else { V->p = x = - x; V->dp = V->period * vib * pb; //set period for next cycle V->pmax = (float)floor(0.5f + V->dp) - 0.5f; V->dc = -0.5f * V->lev / V->pmax; V->pmax *= PI; V->dp = V->pmax / V->dp; V->sin0 = V->lev * (float)sin(x); V->sin1 = V->lev * (float)sin(x - V->dp); V->sinx = 2.0f * (float)cos(V->dp); if(x*x > .1f) x = V->sin0 / x; else x = V->lev; //was 0.01f; } y = V->p2 + V->dp2; //osc2 if(y > min) { if(y > V->pmax2) { y = V->pmax2 + V->pmax2 - y; V->dp2 = -V->dp2; } V->p2 = y; y = V->sin02 * V->sinx2 - V->sin12; V->sin12 = V->sin02; V->sin02 = y; y = y / V->p2; } else { V->p2 = y = - y; V->dp2 = V->period * V->detune * pwm * pb; V->pmax2 = (float)floor(0.5f + V->dp2) - 0.5f; V->dc2 = -0.5f * V->lev2 / V->pmax2; V->pmax2 *= PI; V->dp2 = V->pmax2 / V->dp2; V->sin02 = V->lev2 * (float)sin(y); V->sin12 = V->lev2 * (float)sin(y - V->dp2); V->sinx2 = 2.0f * (float)cos(V->dp2); if(y*y > .1f) y = V->sin02 / y; else y = V->lev2; } V->saw = V->saw * hpf + V->dc + x - V->dc2 - y; //integrated sinc = saw x = V->saw + w; V->env += V->envd * (V->envl - V->env); if(k==KMAX) //filter freq update at LFO rate { if((V->env+V->envl)>3.0f) { V->envd=dec; V->envl=sus; } //envelopes V->fenv += V->fenvd * (V->fenvl - V->fenv); if((V->fenv+V->fenvl)>3.0f) { V->fenvd=fdec; V->fenvl=fsus; } fz += 0.005f * (ff - fz); //filter zipper noise filter y = V->fc * (float)exp(fz + fe * V->fenv) * ipb; //filter cutoff if(y<0.005f) y=0.005f; V->ff = y; V->period += gl * (V->target - V->period); //glide if(V->target < V->period) V->period += gl * (V->target - V->period); } if(V->ff > fx) V->ff = fx; //stability limit V->f0 += V->ff * V->f1; //state-variable filter V->f1 -= V->ff * (V->f0 + fq * V->f1 - x - V->f2); V->f1 -= 0.2f * V->f1 * V->f1 * V->f1; //soft limit V->f2 = x; o += V->env * V->f0; } V++; } *out1++ = o; *out2++ = o; } if(frame= 0) { *out1++ = 0.0f; *out2++ = 0.0f; } } notes[0] = EVENTS_DONE; //mark events buffer as done fzip = fz; K = k; } void mdaJX10::noteOn(LvzInt32 note, LvzInt32 velocity) { float p, l=100.0f; //louder than any envelope! LvzInt32 v=0, tmp, held=0; if(velocity>0) //note on { if(veloff) velocity = 80; if(mode & 4) //monophonic { if(voice[0].note > 0) //legato pitch change { for(tmp=(NVOICES-1); tmp>0; tmp--) //queue any held notes { voice[tmp].note = voice[tmp - 1].note; } p = tune * (float)exp(-0.05776226505 * ((double)note + ANALOG * (double)v)); while(p<3.0f || (p * detune)<3.0f) p += p; voice[v].target = p; if((mode & 2)==0) voice[v].period = p; voice[v].fc = (float)exp(filtvel * (float)(velocity - 64)) / p; voice[v].env += SILENCE + SILENCE; ///was missed out below if returned? voice[v].note = note; return; } } else //polyphonic { for(tmp=0; tmp 0) held++; if(voice[tmp].env0.0f) { p = voice[v].pmax + voice[v].pmax - voice[v].p; voice[v].dp2 = -voice[v].dp; } else { p = voice[v].p; voice[v].dp2 = voice[v].dp; } voice[v].p2 = voice[v].pmax2 = p + PI * voice[v].period; voice[v].dc2 = 0.0f; voice[v].sin02 = voice[v].sin12 = voice[v].sinx2 = 0.0f; } if(mode & 4) //monophonic retriggering { voice[v].env += SILENCE + SILENCE; } else { //if(programs[curProgram].param[15] < 0.28f) //{ // voice[v].f0 = voice[v].f1 = voice[v].f2 = 0.0f; //reset filter // voice[v].env = SILENCE + SILENCE; // voice[v].fenv = 0.0f; //} //else voice[v].env += SILENCE + SILENCE; //anti-glitching trick } voice[v].envl = 2.0f; voice[v].envd = att; voice[v].fenvl = 2.0f; voice[v].fenvd = fatt; } else //note off { if((mode & 4) && (voice[0].note==note)) //monophonic (and current note) { for(v=(NVOICES-1); v>0; v--) { if(voice[v].note>0) held = v; //any other notes queued? } if(held>0) { voice[v].note = voice[held].note; voice[held].note = 0; p = tune * (float)exp(-0.05776226505 * ((double)voice[v].note + ANALOG * (double)v)); while(p<3.0f || (p * detune)<3.0f) p += p; voice[v].target = p; if((mode & 2)==0) voice[v].period = p; voice[v].fc = 1.0f / p; } else { voice[v].envl = 0.0f; voice[v].envd = rel; voice[v].fenvl = 0.0f; voice[v].fenvd = frel; voice[v].note = 0; } } else //polyphonic { for(v=0; vnumEvents; i++) { if((ev->events[i])->type != kLvzMidiType) continue; LvzMidiEvent* event = (LvzMidiEvent*)ev->events[i]; char* midiData = event->midiData; switch(midiData[0] & 0xf0) //status byte (all channels) { case 0x80: //note off notes[npos++] = event->deltaFrames; //delta notes[npos++] = midiData[1] & 0x7F; //note notes[npos++] = 0; //vel break; case 0x90: //note on notes[npos++] = event->deltaFrames; //delta notes[npos++] = midiData[1] & 0x7F; //note notes[npos++] = midiData[2] & 0x7F; //vel break; case 0xB0: //controller switch(midiData[1]) { case 0x01: //mod wheel modwhl = 0.000005f * (float)(midiData[2] * midiData[2]); break; case 0x02: //filter + case 0x4A: filtwhl = 0.02f * (float)(midiData[2]); break; case 0x03: //filter - filtwhl = -0.03f * (float)(midiData[2]); break; case 0x07: //volume volume = 0.00000005f * (float)(midiData[2] * midiData[2]); break; case 0x10: //resonance case 0x47: rezwhl = 0.0065f * (float)(154 - midiData[2]); break; case 0x40: //sustain sustain = midiData[2] & 0x40; if(sustain==0) { notes[npos++] = event->deltaFrames; notes[npos++] = SUSTAIN; //end all sustained notes notes[npos++] = 0; } break; default: //all notes off if(midiData[1]>0x7A) { for(LvzInt32 v=0; vEVENTBUFFER) npos -= 3; //discard events if buffer full!! event++; } notes[npos] = EVENTS_DONE; return 1; }