diff options
Diffstat (limited to 'src/mdaPiano.cpp')
-rw-r--r-- | src/mdaPiano.cpp | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/src/mdaPiano.cpp b/src/mdaPiano.cpp new file mode 100644 index 0000000..75df330 --- /dev/null +++ b/src/mdaPiano.cpp @@ -0,0 +1,526 @@ +/* + Copyright 2008-2011 David Robillard <http://drobilla.net> + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "mdaPianoData.h" +#include "mdaPiano.h" + +#include "lv2/lv2plug.in/ns/ext/atom/util.h" + +#include <stdio.h> +#include <math.h> + + +//#include "AEffEditor.hpp" ////for GUI + +AudioEffect *createEffectInstance(audioMasterCallback audioMaster) +{ + return new mdaPiano(audioMaster); +} + +mdaPianoProgram::mdaPianoProgram() +{ + param[0] = 0.50f; //Decay + param[1] = 0.50f; //Release + param[2] = 0.50f; //Hardness + + param[3] = 0.50f; //Vel>Hard + param[4] = 1.00f; //Muffle + param[5] = 0.50f; //Vel>Muff + + param[6] = 0.33f; //Vel Curve + param[7] = 0.50f; //Stereo + param[8] = 0.33f; //Max Poly + + param[9] = 0.50f; //Tune + param[10] = 0.00f; //Random + param[11] = 0.50f; //Stretch + + strcpy (name, "MDA Piano"); +} + + +mdaPiano::mdaPiano(audioMasterCallback audioMaster) : AudioEffectX(audioMaster, NPROGS, NPARAMS) +{ + Fs = 44100.0f; iFs = 1.0f/Fs; cmax = 0x7F; //just in case... + + programs = new mdaPianoProgram[NPROGS]; + if(programs) + { + //fill patches... + int32_t i=0; + fillpatch(i++, "MDA Piano", 0.500f, 0.500f, 0.500f, 0.5f, 0.803f, 0.251f, 0.376f, 0.500f, 0.330f, 0.500f, 0.246f, 0.500f); + fillpatch(i++, "Plain Piano", 0.500f, 0.500f, 0.500f, 0.5f, 0.751f, 0.000f, 0.452f, 0.000f, 0.000f, 0.500f, 0.000f, 0.500f); + fillpatch(i++, "Compressed Piano", 0.902f, 0.399f, 0.623f, 0.5f, 1.000f, 0.331f, 0.299f, 0.499f, 0.330f, 0.500f, 0.000f, 0.500f); + fillpatch(i++, "Dance Piano", 0.399f, 0.251f, 1.000f, 0.5f, 0.672f, 0.124f, 0.127f, 0.249f, 0.330f, 0.500f, 0.283f, 0.667f); + fillpatch(i++, "Concert Piano", 0.648f, 0.500f, 0.500f, 0.5f, 0.298f, 0.602f, 0.550f, 0.850f, 0.356f, 0.500f, 0.339f, 0.660f); + fillpatch(i++, "Dark Piano", 0.500f, 0.602f, 0.000f, 0.5f, 0.304f, 0.200f, 0.336f, 0.651f, 0.330f, 0.500f, 0.317f, 0.500f); + fillpatch(i++, "School Piano", 0.450f, 0.598f, 0.626f, 0.5f, 0.603f, 0.500f, 0.174f, 0.331f, 0.330f, 0.500f, 0.421f, 0.801f); + fillpatch(i++, "Broken Piano", 0.050f, 0.957f, 0.500f, 0.5f, 0.299f, 1.000f, 0.000f, 0.500f, 0.330f, 0.450f, 0.718f, 0.000f); + + setProgram(0); + } + + setUniqueID("mdaPiano"); + + if(audioMaster) + { + setNumInputs(0); + setNumOutputs(NOUTS); + canProcessReplacing(); + isSynth(); + } + + waves = pianoData; + + + //Waveform data and keymapping is hard-wired in *this* version + kgrp[ 0].root = 36; kgrp[ 0].high = 37; kgrp[ 0].pos = 0; kgrp[ 0].end = 36275; kgrp[ 0].loop = 14774; + kgrp[ 1].root = 40; kgrp[ 1].high = 41; kgrp[ 1].pos = 36278; kgrp[ 1].end = 83135; kgrp[ 1].loop = 16268; + kgrp[ 2].root = 43; kgrp[ 2].high = 45; kgrp[ 2].pos = 83137; kgrp[ 2].end = 146756; kgrp[ 2].loop = 33541; + kgrp[ 3].root = 48; kgrp[ 3].high = 49; kgrp[ 3].pos = 146758; kgrp[ 3].end = 204997; kgrp[ 3].loop = 21156; + kgrp[ 4].root = 52; kgrp[ 4].high = 53; kgrp[ 4].pos = 204999; kgrp[ 4].end = 244908; kgrp[ 4].loop = 17191; + kgrp[ 5].root = 55; kgrp[ 5].high = 57; kgrp[ 5].pos = 244910; kgrp[ 5].end = 290978; kgrp[ 5].loop = 23286; + kgrp[ 6].root = 60; kgrp[ 6].high = 61; kgrp[ 6].pos = 290980; kgrp[ 6].end = 342948; kgrp[ 6].loop = 18002; + kgrp[ 7].root = 64; kgrp[ 7].high = 65; kgrp[ 7].pos = 342950; kgrp[ 7].end = 391750; kgrp[ 7].loop = 19746; + kgrp[ 8].root = 67; kgrp[ 8].high = 69; kgrp[ 8].pos = 391752; kgrp[ 8].end = 436915; kgrp[ 8].loop = 22253; + kgrp[ 9].root = 72; kgrp[ 9].high = 73; kgrp[ 9].pos = 436917; kgrp[ 9].end = 468807; kgrp[ 9].loop = 8852; + kgrp[10].root = 76; kgrp[10].high = 77; kgrp[10].pos = 468809; kgrp[10].end = 492772; kgrp[10].loop = 9693; + kgrp[11].root = 79; kgrp[11].high = 81; kgrp[11].pos = 492774; kgrp[11].end = 532293; kgrp[11].loop = 10596; + kgrp[12].root = 84; kgrp[12].high = 85; kgrp[12].pos = 532295; kgrp[12].end = 560192; kgrp[12].loop = 6011; + kgrp[13].root = 88; kgrp[13].high = 89; kgrp[13].pos = 560194; kgrp[13].end = 574121; kgrp[13].loop = 3414; + kgrp[14].root = 93; kgrp[14].high = 999; kgrp[14].pos = 574123; kgrp[14].end = 586343; kgrp[14].loop = 2399; + + //initialise... + for(int32_t v=0; v<NVOICES; v++) + { + memset(&voice[v], 0, sizeof(voice[v])); + voice[v].env = 0.0f; + voice[v].dec = 0.99f; //all notes off + } + volume = 0.2f; + muff = 160.0f; + cpos = sustain = activevoices = 0; + comb = new float[256]; + memset(comb, 0, sizeof(float) * 256); + + guiUpdate = 0; + + update(); + suspend(); +} + + +void mdaPiano::update() //parameter change +{ + float * param = programs[curProgram].param; + size = (int32_t)(12.0f * param[2] - 6.0f); + sizevel = 0.12f * param[3]; + muffvel = param[5] * param[5] * 5.0f; + + velsens = 1.0f + param[6] + param[6]; + if(param[6] < 0.25f) velsens -= 0.75f - 3.0f * param[6]; + + fine = param[9] - 0.5f; + random = 0.077f * param[10] * param[10]; + stretch = 0.000434f * (param[11] - 0.5f); + + cdep = param[7] * param[7]; + trim = 1.50f - 0.79f * cdep; + width = 0.04f * param[7]; if(width > 0.03f) width = 0.03f; + + poly = 8 + (int32_t)(24.9f * param[8]); +} + + +void mdaPiano::setSampleRate(float rate) +{ + AudioEffectX::setSampleRate(rate); + Fs = rate; + iFs = 1.0f / Fs; + if(Fs > 64000.0f) cmax = 0xFF; else cmax = 0x7F; +} + + +void mdaPiano::resume() +{ + memset(comb, 0, sizeof(float) * 256); + + DECLARE_LVZ_DEPRECATED (wantEvents) (); +} + + +mdaPiano::~mdaPiano () //destroy any buffers... +{ + if(programs) delete [] programs; + if(comb) delete[] comb; +} + + +void mdaPiano::setProgram(int32_t program) +{ + curProgram = program; + update(); + + // TODO: guiUpdate ??? +} + + +void mdaPiano::setParameter(int32_t index, float value) +{ + programs[curProgram].param[index] = value; + update(); + +// if(editor) editor->postUpdate(); //For GUI + + guiUpdate = index + 0x100 + (guiUpdate & 0xFFFF00); +} + + +void mdaPiano::fillpatch(int32_t 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) +{ + 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; +} + + +float mdaPiano::getParameter(int32_t index) { return programs[curProgram].param[index]; } +void mdaPiano::setProgramName(char *name) { strcpy(programs[curProgram].name, name); } +void mdaPiano::getProgramName(char *name) { strcpy(name, programs[curProgram].name); } +void mdaPiano::setBlockSize(int32_t blockSize) { AudioEffectX::setBlockSize(blockSize); } +bool mdaPiano::getEffectName(char* name) { strcpy(name, "Piano"); return true; } +bool mdaPiano::getVendorString(char* text) { strcpy(text, "mda"); return true; } +bool mdaPiano::getProductString(char* text) { strcpy(text, "MDA Piano"); return true; } + + +bool mdaPiano::getOutputProperties(int32_t index, LvzPinProperties* properties) +{ + if(index<NOUTS) + { + if(index) sprintf(properties->label, "Piano R"); + else sprintf(properties->label, "Piano L"); + properties->flags = kLvzPinIsActive; + if(index<2) properties->flags |= kLvzPinIsStereo; //make channel 1+2 stereo + return true; + } + return false; +} + + +bool mdaPiano::getProgramNameIndexed(int32_t category, int32_t index, char* text) +{ + if ((unsigned int)index < NPROGS) + { + strcpy(text, programs[index].name); + return true; + } + return false; +} + + +bool mdaPiano::copyProgram(int32_t destination) +{ + if(destination<NPROGS) + { + programs[destination] = programs[curProgram]; + return true; + } + return false; +} + + +int32_t mdaPiano::canDo(const char* text) +{ + if(strcmp(text, "receiveLvzEvents") == 0) return 1; + if(strcmp(text, "receiveLvzMidiEvent") == 0) return 1; + return -1; +} + + +void mdaPiano::getParameterName(int32_t index, char *label) +{ + switch (index) + { + case 0: strcpy(label, "Envelope Decay"); break; + case 1: strcpy(label, "Envelope Release"); break; + case 2: strcpy(label, "Hardness Offset"); break; + + case 3: strcpy(label, "Velocity to Hardness"); break; + case 4: strcpy(label, "Muffling Filter"); break; + case 5: strcpy(label, "Velocity to Muffling"); break; + + case 6: strcpy(label, "Velocity Sensitivity"); break; + case 7: strcpy(label, "Stereo Width"); break; + case 8: strcpy(label, "Polyphony"); break; + + case 9: strcpy(label, "Fine Tuning"); break; + case 10: strcpy(label, "Random Detuning"); break; + default: strcpy(label, "Stretch Tuning"); break; + } +} + + +void mdaPiano::getParameterDisplay(int32_t index, char *text) +{ + char string[16]; + float * param = programs[curProgram].param; + + switch(index) + { + case 4: sprintf(string, "%.0f", 100.0f - 100.0f * param[index]); break; + case 7: sprintf(string, "%.0f", 200.0f * param[index]); break; + case 8: sprintf(string, "%d", poly); break; + case 10: sprintf(string, "%.1f", 50.0f * param[index] * param[index]); break; + case 2: + case 9: + case 11: sprintf(string, "%+.1f", 100.0f * param[index] - 50.0f); break; + default: sprintf(string, "%.0f", 100.0f * param[index]); + } + string[8] = 0; + strcpy(text, (char *)string); +} + + +void mdaPiano::getParameterLabel(int32_t index, char *label) +{ + switch(index) + { + case 8: strcpy(label, "voices"); break; + case 9: + case 10: + case 11: strcpy(label, "cents"); break; + default: strcpy(label, "%"); + } +} + + +void mdaPiano::guiGetDisplay(int32_t index, char *label) +{ + getParameterName(index, label); + strcat(label, " = "); + getParameterDisplay(index, label + strlen(label)); + getParameterLabel(index, label + strlen(label)); +} + +void mdaPiano::processReplacing(float **inputs, float **outputs, int32_t sampleFrames) +{ + float* out0 = outputs[0]; + float* out1 = outputs[1]; + int32_t frame=0, frames, v; + float x, l, r; + int32_t i; + + LV2_Atom_Event* ev = lv2_atom_sequence_begin(&eventInput->body); + while(frame<sampleFrames) + { + bool end = lv2_atom_sequence_is_end(&eventInput->body, eventInput->atom.size, ev); + frames = end ? sampleFrames : ev->time.frames; + frames -= frame; + frame += frames; + + while(--frames>=0) + { + VOICE *V = voice; + l = r = 0.0f; + + for(v=0; v<activevoices; v++) + { + V->frac += V->delta; //integer-based linear interpolation + V->pos += V->frac >> 16; + V->frac &= 0xFFFF; + if(V->pos > V->end) V->pos -= V->loop; + //i = (i << 7) + (V->frac >> 9) * (waves[V->pos + 1] - i) + 0x40400000; //not working on intel mac !?! + i = waves[V->pos] + ((V->frac * (waves[V->pos + 1] - waves[V->pos])) >> 16); + x = V->env * (float)i / 32768.0f; + //x = V->env * (*(float *)&i - 3.0f); //fast int->float + + V->env = V->env * V->dec; //envelope + V->f0 += V->ff * (x + V->f1 - V->f0); //muffle filter + V->f1 = x; + + l += V->outl * V->f0; + r += V->outr * V->f0; + + if(!(l > -2.0f) || !(l < 2.0f)) + { + printf("what is this shit? %d, %f, %f\n", i, x, V->f0); + l = 0.0f; + } +if(!(r > -2.0f) || !(r < 2.0f)) + { + r = 0.0f; + } + + V++; + } + comb[cpos] = l + r; + ++cpos &= cmax; + x = cdep * comb[cpos]; //stereo simulator + + *out0++ = l + x; + *out1++ = r - x; + } + + if(!end) + { + processEvent(ev); + ev = lv2_atom_sequence_next(ev); + } + } + for(v=0; v<activevoices; v++) if(voice[v].env < SILENCE) voice[v] = voice[--activevoices]; +} + + +void mdaPiano::noteOn(int32_t note, int32_t velocity) +{ + float * param = programs[curProgram].param; + float l=99.0f; + int32_t v, vl=0, k, s; + + if(velocity>0) + { + if(activevoices < poly) //add a note + { + vl = activevoices; + activevoices++; + } + else //steal a note + { + for(v=0; v<poly; v++) //find quietest voice + { + if(voice[v].env < l) { l = voice[v].env; vl = v; } + } + } + + k = (note - 60) * (note - 60); + l = fine + random * ((float)(k % 13) - 6.5f); //random & fine tune + if(note > 60) l += stretch * (float)k; //stretch + + s = size; + if(velocity > 40) s += (int32_t)(sizevel * (float)(velocity - 40)); + + k = 0; + while(note > (kgrp[k].high + s)) k++; //find keygroup + + l += (float)(note - kgrp[k].root); //pitch + l = 22050.0f * iFs * (float)exp(0.05776226505 * l); + voice[vl].delta = (int32_t)(65536.0f * l); + voice[vl].frac = 0; + voice[vl].pos = kgrp[k].pos; + voice[vl].end = kgrp[k].end; + voice[vl].loop = kgrp[k].loop; + + voice[vl].env = (0.5f + velsens) * (float)pow(0.0078f * velocity, velsens); //velocity + + l = 50.0f + param[4] * param[4] * muff + muffvel * (float)(velocity - 64); //muffle + if(l < (55.0f + 0.25f * (float)note)) l = 55.0f + 0.25f * (float)note; + if(l > 210.0f) l = 210.0f; + voice[vl].ff = l * l * iFs; + voice[vl].f0 = voice[vl].f1 = 0.0f; + + voice[vl].note = note; //note->pan + if(note < 12) note = 12; + if(note > 108) note = 108; + l = volume * trim; + voice[vl].outr = l + l * width * (float)(note - 60); + voice[vl].outl = l + l - voice[vl].outr; + + if(note < 44) note = 44; //limit max decay length + l = 2.0f * param[0]; + if(l < 1.0f) l += 0.25f - 0.5f * param[0]; + voice[vl].dec = (float)exp(-iFs * exp(-0.6 + 0.033 * (double)note - l)); + } + else //note off + { + for(v=0; v<NVOICES; v++) if(voice[v].note==note) //any voices playing that note? + { + if(sustain==0) + { + if(note < 94 || note == SUSTAIN) //no release on highest notes + voice[v].dec = (float)exp(-iFs * exp(2.0 + 0.017 * (double)note - 2.0 * param[1])); + } + else voice[v].note = SUSTAIN; + } + } +} + + +int32_t mdaPiano::processEvent(const LV2_Atom_Event* ev) +{ + if (ev->body.type != midiEventType) + return 0; + + const uint8_t* midiData = (const uint8_t*)LV2_ATOM_BODY_CONST(&ev->body); + + switch(midiData[0] & 0xf0) //status byte (all channels) + { + case 0x80: //note off + noteOn(midiData[1] & 0x7F, 0); + break; + + case 0x90: //note on + noteOn(midiData[1] & 0x7F, midiData[2] & 0x7F); + break; + + case 0xB0: //controller + switch(midiData[1]) + { + case 0x01: //mod wheel + case 0x43: //soft pedal + muff = 0.01f * (float)((127 - midiData[2]) * (127 - midiData[2])); + break; + + case 0x07: //volume + volume = 0.00002f * (float)(midiData[2] * midiData[2]); + break; + + case 0x40: //sustain pedal + case 0x42: //sustenuto pedal + sustain = midiData[2] & 0x40; + if(sustain==0) + { + noteOn(SUSTAIN, 0); //end all sustained notes + } + break; + + default: //all notes off + if(midiData[1]>0x7A) + { + for(int32_t v=0; v<NVOICES; v++) voice[v].dec=0.99f; + sustain = 0; + muff = 160.0f; + } + break; + } + break; + + case 0xC0: //program change + if(midiData[1]<NPROGS) setProgram(midiData[1]); + break; + + default: break; + } + + return 1; +} + |