diff options
author | David Robillard <d@drobilla.net> | 2008-08-08 22:45:58 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2008-08-08 22:45:58 +0000 |
commit | e360047054117d63fb579ec9231e9dc77c99f12a (patch) | |
tree | 4a497365f6ecd30449e2c66c1fe77c816bd1fe4a /src/mdaLooplex.cpp | |
download | mda.lv2-e360047054117d63fb579ec9231e9dc77c99f12a.tar.gz mda.lv2-e360047054117d63fb579ec9231e9dc77c99f12a.tar.bz2 mda.lv2-e360047054117d63fb579ec9231e9dc77c99f12a.zip |
Add preliminary (library side only) LV2 port of MDA (open-sourced VST plugins).
git-svn-id: http://svn.drobilla.net/lad/mda-lv2@1321 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/mdaLooplex.cpp')
-rw-r--r-- | src/mdaLooplex.cpp | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/src/mdaLooplex.cpp b/src/mdaLooplex.cpp new file mode 100644 index 0000000..33c5e29 --- /dev/null +++ b/src/mdaLooplex.cpp @@ -0,0 +1,585 @@ +// +// Plug-in: "mdaLooplex" v1.0 +// +// Copyright(c)1999-2000 Paul Kellett (maxim digital audio) +// + +#include "mdaLooplex.h" + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + + +AudioEffect *createEffectInstance(audioMasterCallback audioMaster) +{ + return new mdaLooplex(audioMaster); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//replacement for fxIdle that Steinberg stupidly removed from VST2.4 forcing loads of plug-ins to set up their own timers... + +#define IDLE_MSEC 40 //25 Hz + +class IdleList //wzDSP objects to receive idle timer (same timer is used for all instances) +{ +public: + IdleList(mdaLooplex *effect, IdleList *next); + + mdaLooplex *effect; + IdleList *next; + bool remove; + +} static idleList(0, 0); //idleList->next points to start of idle list + + +#if _WIN32 + #include <windows.h>
+ #pragma comment(lib, "user32.lib") + static UINT timer = 0; + VOID CALLBACK TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) +#else //OSX + #include <Carbon/Carbon.h> + EventLoopTimerRef timer = 0; + pascal void TimerCallback(EventLoopTimerRef timerRef, void *userData) +#endif +{ + IdleList *prev = &idleList; + IdleList *item = prev->next; + while(item) + { + if(item->remove) + { + prev->next = item->next; //remove item from list and close gap + delete item; + item = prev->next; + + if(prev == &idleList && prev->next == 0) //stop timer after last item removed + { + #if _WIN32 + KillTimer(NULL, timer); + #else //OSX + RemoveEventLoopTimer(timer); + #endif + timer = 0; + } + } + else + { + item->effect->idle(); //call idle() for each item in list + prev = item; + item = item->next; + } + } +} + + +IdleList::IdleList(mdaLooplex *effect, IdleList *next) : effect(effect), next(next), remove(false) +{ + if(effect && !timer) //start timer + { + #if WIN32 + timer = SetTimer(NULL, 0, IDLE_MSEC, TimerCallback); + #else //OSX + double ms = kEventDurationMillisecond * (double)IDLE_MSEC; + InstallEventLoopTimer(GetCurrentEventLoop(), ms, ms, NewEventLoopTimerUPP(TimerCallback), 0, &timer); + #endif + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +mdaLooplexProgram::mdaLooplexProgram() +{ + param[0] = 0.00f; //max delay + param[1] = 0.00f; //reset (malloc) + param[2] = 0.00f; //record (sus ped) + param[3] = 1.00f; //input mix + param[4] = 0.50f; //input pan + param[5] = 1.00f; //feedback (mod whl) + param[6] = 1.00f; //out mix + //param[7] = 0.00f; + + strcpy (name, "mda Looplex"); +} + + +mdaLooplex::mdaLooplex(audioMasterCallback audioMaster) : AudioEffectX(audioMaster, NPROGS, NPARAMS) +{ +
+ Fs = 44100.0f; //won't we know the sample rate by the time we need it? + + //initialise... + bypass = bypassed = busy = status = 0; //0=not recorded 1=first pass 2=looping + bufmax = 882000; + buffer = new short[bufmax + 10]; + memset(buffer, 0, (bufmax + 10) * sizeof(short)); + bufpos = 0; + buflen = 0; + recreq = 0; + oldParam0 = oldParam1 = oldParam2 = 0.0f; + modwhl = 1.0f; + + programs = new mdaLooplexProgram[NPROGS]; + if(programs) setProgram(0); + + if(audioMaster) + { + setNumInputs(NOUTS); + setNumOutputs(NOUTS); + canProcessReplacing(); + //needIdle(); idle is broken in VST2.4 + setUniqueID("MDA~"); /// + } + + update(); + suspend(); + + idleList.next = new IdleList(this, idleList.next); //add to idle list, start timer if not running... +} + + +void mdaLooplex::update() //parameter change +{ + if(fabs(param[1] - oldParam1) > 0.1f) + { + oldParam1 = param[1]; + if(fabs(oldParam0 - param[0]) > 0.01f) + { + oldParam0 = param[0]; + bypass = 1; //re-assign memory + } + } + + if(param[2] > 0.5f && oldParam2 < 0.5f) + { + if(recreq == 0) recreq = 1; + oldParam2 = param[2]; + } + if(param[2] < 0.5f && oldParam2 > 0.5f) + { + if(recreq == 1) recreq = 0; + oldParam2 = param[2]; + } + + in_mix = 2.0f * param[3] * param[3]; + + in_pan = param[4]; + + feedback = param[5]; + + out_mix = 0.000030517578f * param[6] * param[6]; +} + + +void mdaLooplex::setSampleRate(float sampleRate) +{ + AudioEffectX::setSampleRate(sampleRate); + Fs = sampleRate; +} + + +void mdaLooplex::resume() +{ + //should reset position here... + bufpos = 0; +
+ //needIdle(); //idle broken in VST2.4
+ DECLARE_LVZ_DEPRECATED (wantEvents) ();
+} + + +void mdaLooplex::idle() +{
+ if(bypassed) + { + if(busy) return; //only do once per bypass + busy = 1; + + bufmax = 2 * (long)Fs * (long)(10.5f + 190.0f * param[0]); + if(buffer) delete [] buffer; + buffer = new short[bufmax + 10]; + if(buffer) memset(buffer, 0, (bufmax + 10) * sizeof(short)); else bufmax = 0; + + bypass = busy = status = bufpos = buflen = 0; + } +} + + +mdaLooplex::~mdaLooplex () //destroy any buffers... +{ + for(IdleList *item=idleList.next; item; item=item->next) //remove from idle list, stop timer if last item + { + if(item->effect == this) + { + item->remove = true;
+ #if _WIN32 //and stop timer in case our last instance is about to unload
+ TimerCallback(0, 0, 0, 0);
+ #else
+ TimerCallback(0, 0);
+ #endif + break; + } + } + + if(programs) delete [] programs; + + if(buffer) + { + FILE *fp; //dump loop to file + + if(buflen && (fp = fopen("looplex.wav", "wb"))) + { + char wh[44]; + memcpy(wh, "RIFF____WAVEfmt \20\0\0\0\1\0\2\0________\4\0\20\0data____", 44); + + long l = 36 + buflen * 2; + wh[4] = (char)(l & 0xFF); l >>= 8; + wh[5] = (char)(l & 0xFF); l >>= 8; + wh[6] = (char)(l & 0xFF); l >>= 8; + wh[7] = (char)(l & 0xFF); + + l = (long)(Fs + 0.5f); + wh[24] = (char)(l & 0xFF); l >>= 8; + wh[25] = (char)(l & 0xFF); l >>= 8; + wh[26] = (char)(l & 0xFF); l >>= 8; + wh[27] = (char)(l & 0xFF); + + l = 4 * (long)(Fs + 0.5f); + wh[28] = (char)(l & 0xFF); l >>= 8; + wh[29] = (char)(l & 0xFF); l >>= 8; + wh[30] = (char)(l & 0xFF); l >>= 8; + wh[31] = (char)(l & 0xFF); + + l = buflen * 2; + wh[40] = (char)(l & 0xFF); l >>= 8; + wh[41] = (char)(l & 0xFF); l >>= 8; + wh[42] = (char)(l & 0xFF); l >>= 8; + wh[43] = (char)(l & 0xFF); + + fwrite(wh, 1, 44, fp); + + #if __BIG_ENDIAN__ + char *c = (char *)buffer; + char t; + + for(l=0; l<buflen; l++) //swap endian-ness + { + t = *c; + *c = *(c + 1); + c++; + *c = t; + c++; + } + #endif + + fwrite(buffer, sizeof(short), buflen, fp); + fclose(fp); + } + + delete [] buffer; + }
+} + + +void mdaLooplex::setProgram(LvzInt32 program) +{
+ long i; + + mdaLooplexProgram *p = &programs[program]; + curProgram = program;
+ setProgramName(p->name);
+ for(i=0; i<NPARAMS; i++) param[i] = p->param[i]; + update(); +} + + +void mdaLooplex::setParameter(LvzInt32 index, float value) +{
+ mdaLooplexProgram *p = &programs[curProgram]; + param[index] = p->param[index] = value; + update(); +} + + +float mdaLooplex::getParameter(LvzInt32 index) { return param[index]; } +void mdaLooplex::setProgramName(char *name) { strcpy(programs[curProgram].name, name); } +void mdaLooplex::getProgramName(char *name) { strcpy(name, programs[curProgram].name); } +void mdaLooplex::setBlockSize(LvzInt32 blockSize) { AudioEffectX::setBlockSize(blockSize); } +bool mdaLooplex::getEffectName(char* name) { strcpy(name, "Looplex"); return true; } +bool mdaLooplex::getVendorString(char* text) { strcpy(text, "mda"); return true; } +bool mdaLooplex::getProductString(char* text) { strcpy(text, "mda Looplex"); return true; } + + +bool mdaLooplex::getProgramNameIndexed(LvzInt32 category, LvzInt32 index, char* text) +{ + if(index<NPROGS) + { + strcpy(text, programs[index].name); + return true; + } + return false; +} + + +bool mdaLooplex::copyProgram(LvzInt32 destination) +{
+ if(destination<NPROGS) + { + programs[destination] = programs[curProgram]; + return true; + } + return false; +} + + +LvzInt32 mdaLooplex::canDo(char* text) +{ + if(strcmp(text, "receiveLvzEvents") == 0) return 1; + if(strcmp(text, "receiveLvzMidiEvent") == 0) return 1; + return -1; +} + + +void mdaLooplex::getParameterName(LvzInt32 index, char *label) +{ + switch (index) + { + case 0: strcpy(label, "Max Del "); break; + case 1: strcpy(label, "Reset "); break; + case 2: strcpy(label, "Record "); break; + case 3: strcpy(label, "In Mix "); break; + case 4: strcpy(label, "In Pan "); break; + case 5: strcpy(label, "Feedback"); break; + case 6: strcpy(label, "Out Mix "); break; + + default: strcpy(label, " "); + } +} + + +void mdaLooplex::getParameterDisplay(LvzInt32 index, char *text) +{ + char string[16]; + + switch(index) + { + case 0: sprintf(string, "%4d s", (int)(10.5f + 190.0f * param[index])); break; //10 to 200 sec + + case 1: sprintf(string, "%5.1f MB", (float)bufmax / 524288.0f); break; + + case 2: if(recreq) strcpy(string, "RECORD"); else strcpy(string, "MONITOR"); break; + + case 3: + case 6: if(param[index] < 0.01f) strcpy(string, "OFF"); + else sprintf(string, "%.1f dB", 20.0f * log10(param[index] * param[index])); break; + + case 5: if(param[index] < 0.01f) strcpy(string, "OFF"); + else sprintf(string, "%.1f dB", 20.0f * log10(param[index])); break; + + case 4: if(param[index] < 0.505f) + { + if(param[index] > 0.495f) strcpy(string, "C"); else + sprintf(string, "L%.0f", 100.0f - 200.0f * param[index]); + } + else sprintf(string, "R%.0f", 200.0f * param[index] - 100.0f); break; + + default: strcpy(string, " "); + } + string[8] = 0; + strcpy(text, (char *)string); +} + + +void mdaLooplex::getParameterLabel(LvzInt32 index, char *label) +{ + switch(index) + { + case 2: strcpy(label, "(susped)"); break; + case 5: strcpy(label, "(modwhl)"); break; + + default: strcpy(label, " "); + } +} + + +void mdaLooplex::process(float **inputs, float **outputs, LvzInt32 sampleFrames) +{ + notes[0] = EVENTS_DONE; //mark events buffer as done +} + + +void mdaLooplex::processReplacing(float **inputs, float **outputs, LvzInt32 sampleFrames) +{ + float* in1 = inputs[0]; + float* in2 = inputs[1]; + float* out1 = outputs[0]; + float* out2 = outputs[1]; + long event=0, frame=0, frames; + float l, r, dl, dr, d0 = 0.0f, d1; + float imix = in_mix, ipan = in_pan, omix = out_mix, fb = feedback * modwhl; + long x; + + if((bypassed = bypass)) return; + + while(frame<sampleFrames) + { + frames = notes[event++]; + if(frames>sampleFrames) frames = sampleFrames; + frames -= frame; + frame += frames; + + while(--frames>=0) + { + l = *in1++; + r = *in2++; + + //input mix + r *= imix * ipan; + l *= imix - ipan * imix; + + if(recreq == 1 && status == 0) status = 1; //first pass + if(recreq == 0 && status == 1) status = 2; //later pass + + if(status) + { + //dither + d1 = d0; + #if WIN32 + d0 = 0.000061f * (float)(rand() - 16384); + #else + d0 = 0.000061f * (float)((rand() & 32767) - 16384); + #endif + + //left delay + dl = fb * (float)buffer[bufpos]; + if(recreq) + { + x = (long)(32768.0f * l + dl + d0 - d1 + 100000.5f) - 100000; + if(x > 32767) x = 32767; else if(x < -32768) x = -32768; + buffer[bufpos] = (short)x; + } + bufpos++; + + //right delay + dr = fb * (float)buffer[bufpos]; + if(recreq) + { + x = (long)(32768.0f * r + dr - d0 + d1 + 100000.5f) - 100000; + if(x > 32767) x = 32767; else if(x < -32768) x = -32768; + buffer[bufpos] = (short)x; + } + bufpos++; + + //looping + if(bufpos >= bufmax) + { + buflen = bufmax; + bufpos -= buflen; + status = 2; + } + else + { + if(status == 1) buflen = bufpos; else if(bufpos >= buflen) bufpos -= buflen; + } + + //output + l += omix * dl; + r += omix * dr; + } + + *out1++ = l; + *out2++ = r; + } + + if(frame<sampleFrames) + { + long note = notes[event++]; + //long vel = notes[event++]; + + if(note == 2) + { + bufpos = 0; //resync + } + else if(note == 1) + { + if(recreq) recreq = 0; else recreq = 1; //toggle recording + } + } + } + notes[0] = EVENTS_DONE; //mark events buffer as done +} + + +LvzInt32 mdaLooplex::processEvents(LvzEvents* ev) +{ + long npos=0; + + for (long i=0; i<ev->numEvents; 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 = 1.0f - 0.007874f * (float)midiData[2]; + break; + + case 0x40: //sustain + if(midiData[2] > 63) + { + notes[npos++] = event->deltaFrames; //delta + notes[npos++] = 1; //note + notes[npos++] = 127; //vel + } + break; + + case 0x42: //soft/sost + case 0x43: + if(midiData[2] > 63) + { + notes[npos++] = event->deltaFrames; //delta + notes[npos++] = 2; //note + notes[npos++] = 127; //vel + } + break; + + default: //all notes off + if(midiData[1]>0x7A) + { + + } + break; + } + break; + + default: break; + } + + if(npos>EVENTBUFFER) npos -= 3; //discard events if buffer full!! + event++; + } + notes[npos] = EVENTS_DONE; + return 1; +} + |