diff options
Diffstat (limited to 'muse2/muse/vst.cpp')
-rw-r--r-- | muse2/muse/vst.cpp | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/muse2/muse/vst.cpp b/muse2/muse/vst.cpp new file mode 100644 index 00000000..ddb2e87d --- /dev/null +++ b/muse2/muse/vst.cpp @@ -0,0 +1,630 @@ +//=================================================================== +// MusE +// Linux Music Editor +// $Id: vst.cpp,v 1.5.2.6 2009/12/06 10:05:00 terminator356 Exp $ +// +// This code is based on jack_fst: +// Copyright (C) 2004 Paul Davis <paul@linuxaudiosystems.com> +// Torben Hohn <torbenh@informatik.uni-bremen.de> +// +// (C) Copyright 2004 Werner Schweer (ws@seh.de) +//=================================================================== + +#include "config.h" + +#ifdef VST_SUPPORT + +#include <qdir.h> +#include <cmath> +#include <fst.h> +#include <vst/aeffectx.h> +#include <jack/jack.h> + +#include "vst.h" +#include "globals.h" +#include "synth.h" +#include "jackaudio.h" +#include "midi.h" +#include "xml.h" + +extern "C" void fst_error(const char *fmt, ...); +extern long vstHostCallback (AEffect*, long, long, long, void*, float); + +extern JackAudioDevice* jackAudio; + +//--------------------------------------------------------- +// vstHostCallback +//--------------------------------------------------------- + +long vstHostCallback(AEffect* effect, + long opcode, long index, long value, void* ptr, float opt) + { + static VstTimeInfo _timeInfo; + +// JackVST* jackvst = effect ? ((JackVST*) effect->user) : NULL; + jack_position_t jack_pos; + jack_transport_state_t tstate; + + switch (opcode) { + case audioMasterAutomate: + // index, value, returns 0 + effect->setParameter (effect, index, opt); + return 0; + + case audioMasterVersion: + // vst version, currently 2 (0 for older) + return 2; + + case audioMasterCurrentId: + // returns the unique id of a plug that's currently + // loading + return 0; + + case audioMasterIdle: + // call application idle routine (this will + // call effEditIdle for all open editors too) + effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); + return 0; + + case audioMasterPinConnected: + // inquire if an input or output is beeing connected; + // index enumerates input or output counting from zero: + // value is 0 for input and != 0 otherwise. note: the + // return value is 0 for <true> such that older versions + // will always return true. + return 1; + + case audioMasterWantMidi: + // <value> is a filter which is currently ignored + return 0; + + case audioMasterGetTime: + // returns const VstTimeInfo* (or 0 if not supported) + // <value> should contain a mask indicating which fields are required + // (see valid masks above), as some items may require extensive + // conversions + + memset(&_timeInfo, 0, sizeof(_timeInfo)); + + if (effect) { + tstate = jackAudio->transportQuery(&jack_pos); + + _timeInfo.samplePos = jack_pos.frame; + _timeInfo.sampleRate = jack_pos.frame_rate; + _timeInfo.flags = 0; + + if ((value & (kVstBarsValid|kVstTempoValid)) && (jack_pos.valid & JackPositionBBT)) { + _timeInfo.tempo = jack_pos.beats_per_minute; + _timeInfo.timeSigNumerator = (long) floor (jack_pos.beats_per_bar); + _timeInfo.timeSigDenominator = (long) floor (jack_pos.beat_type); + _timeInfo.flags |= (kVstBarsValid|kVstTempoValid); + } + if (tstate == JackTransportRolling) { + _timeInfo.flags |= kVstTransportPlaying; + } + } + else { + _timeInfo.samplePos = 0; + _timeInfo.sampleRate = sampleRate; + } + return (long)&_timeInfo; + + case audioMasterProcessEvents: + // VstEvents* in <ptr> + return 0; + + case audioMasterSetTime: + // VstTimenfo* in <ptr>, filter in <value>, not supported + + case audioMasterTempoAt: + // returns tempo (in bpm * 10000) at sample frame location passed in <value> + return 0; + + case audioMasterGetNumAutomatableParameters: + return 0; + + case audioMasterGetParameterQuantization: + // returns the integer value for +1.0 representation, + // or 1 if full single float precision is maintained + // in automation. parameter index in <value> (-1: all, any) + return 0; + + case audioMasterIOChanged: + // numInputs and/or numOutputs has changed + return 0; + + case audioMasterNeedIdle: + // plug needs idle calls (outside its editor window) + return 0; + + case audioMasterSizeWindow: + // index: width, value: height + return 0; + + case audioMasterGetSampleRate: + return 0; + + case audioMasterGetBlockSize: + return 0; + + case audioMasterGetInputLatency: + return 0; + + case audioMasterGetOutputLatency: + return 0; + + case audioMasterGetPreviousPlug: + // input pin in <value> (-1: first to come), returns cEffect* + return 0; + + case audioMasterGetNextPlug: + // output pin in <value> (-1: first to come), returns cEffect* + + case audioMasterWillReplaceOrAccumulate: + // returns: 0: not supported, 1: replace, 2: accumulate + return 0; + + case audioMasterGetCurrentProcessLevel: + // returns: 0: not supported, + // 1: currently in user thread (gui) + // 2: currently in audio thread (where process is called) + // 3: currently in 'sequencer' thread (midi, timer etc) + // 4: currently offline processing and thus in user thread + // other: not defined, but probably pre-empting user thread. + return 0; + + case audioMasterGetAutomationState: + // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + // offline + return 0; + + case audioMasterOfflineStart: + case audioMasterOfflineRead: + // ptr points to offline structure, see below. return 0: error, 1 ok + return 0; + + case audioMasterOfflineWrite: + // same as read + return 0; + + case audioMasterOfflineGetCurrentPass: + case audioMasterOfflineGetCurrentMetaPass: + return 0; + + case audioMasterSetOutputSampleRate: + // for variable i/o, sample rate in <opt> + return 0; + + case audioMasterGetSpeakerArrangement: + // (long)input in <value>, output in <ptr> + return 0; + + case audioMasterGetVendorString: + // fills <ptr> with a string identifying the vendor (max 64 char) + strcpy ((char*) ptr, "LAD"); + return 0; + + case audioMasterGetProductString: + // fills <ptr> with a string with product name (max 64 char) + strcpy ((char*) ptr, "FreeST"); + + case audioMasterGetVendorVersion: + // returns vendor-specific version + return 1000; + + case audioMasterVendorSpecific: + // no definition, vendor specific handling + return 0; + + case audioMasterSetIcon: + // void* in <ptr>, format not defined yet + return 0; + + case audioMasterCanDo: + // string in ptr, see below + return 0; + + case audioMasterGetLanguage: + // see enum + return 0; + + case audioMasterOpenWindow: + // returns platform specific ptr + return 0; + + case audioMasterCloseWindow: + // close window, platform specific handle in <ptr> + return 0; + + case audioMasterGetDirectory: + // get plug directory, FSSpec on MAC, else char* + return 0; + + case audioMasterUpdateDisplay: + // something has changed, update 'multi-fx' display + effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); + return 0; + + case audioMasterBeginEdit: + // begin of automation session (when mouse down), parameter index in <index> + return 0; + + case audioMasterEndEdit: + // end of automation session (when mouse up), parameter index in <index> + return 0; + + case audioMasterOpenFileSelector: + // open a fileselector window with VstFileSelect* in <ptr> + return 0; + + default: + break; + } + return 0; + } + +//--------------------------------------------------------- +// scanVstDir +//--------------------------------------------------------- + +static void scanVstDir(const QString& s) + { + if (debugMsg) + printf("scan vst plugin dir <%s>\n", s.latin1()); + QDir pluginDir(s, QString("*.dll"), QDir::Files); + if (pluginDir.exists()) { + const QFileInfoList* list = pluginDir.entryInfoList(); + QFileInfoListIterator it(*list); + QFileInfo* fi; + while((fi = it.current())) { + char* path = strdup(fi->filePath().latin1()); + FSTInfo* info = fst_get_info(path); + if (info) { + if (info->numInputs == 0 && info->numOutputs) + //synthis.push_back(new VstSynth(*fi)); + synthis.push_back(new VstSynth(*fi, fi->baseName(), QString(), QString(), QString())); + fst_free_info(info); + } + free(path); + ++it; + } + } + } + +//--------------------------------------------------------- +// fstSignalHandler +//--------------------------------------------------------- + +static void fstSignalHandler(int sig, siginfo_t* /*info*/, void* /*context*/) + { + fst_error("fst signal handler %d, thread = 0x%x", sig, pthread_self ()); + if (sig == SIGSEGV || sig == SIGABRT) { + char*p = 0; + *p = 0; + } + exit(-1); + } + +void jfst_reserve_mem (int bufsize) +{ + char buf [bufsize]; + int i; + + fprintf (stderr, "Reserving memory: base=%p, size=%d, end=%p\n", + buf, sizeof(buf), buf+sizeof(buf)); + for (i=0; i<bufsize; i++) + { + buf[i] = (char) (i % 256); + } +} + +//--------------------------------------------------------- +// initVST +//--------------------------------------------------------- + +void initVST() + { + jfst_reserve_mem(1000000); + + if (fst_init(fstSignalHandler)) { + printf("initVST failed\n"); + return; + } + + char* vstPath = getenv("VST_PATH"); + if (vstPath == 0) + vstPath = "/usr/lib/vst:/usr/local/lib/vst"; + + char* p = vstPath; + while (*p != '\0') { + char* pe = p; + while (*pe != ':' && *pe != '\0') + pe++; + + int n = pe - p; + if (n) { + char* buffer = new char[n + 1]; + strncpy(buffer, p, n); + buffer[n] = '\0'; + scanVstDir(QString(buffer)); + delete[] buffer; + } + p = pe; + if (*p == ':') + p++; + } + } + +//--------------------------------------------------------- +// guiVisible +//--------------------------------------------------------- + +bool VstSynthIF::guiVisible() const + { + return _guiVisible; + } + + +//--------------------------------------------------------- +// showGui +//--------------------------------------------------------- + +void VstSynthIF::showGui(bool v) + { + if (v == guiVisible()) + return; + if (v) + fst_run_editor(_fst); + else + fst_destroy_editor(_fst); + _guiVisible = v; + } + +//--------------------------------------------------------- +// receiveEvent +//--------------------------------------------------------- + +MidiPlayEvent VstSynthIF::receiveEvent() + { + return MidiPlayEvent(); + } + +//--------------------------------------------------------- +// hasGui +//--------------------------------------------------------- + +bool VstSynthIF::hasGui() const + { + return _fst->plugin->flags & effFlagsHasEditor; + } + +//--------------------------------------------------------- +// incInstances +//--------------------------------------------------------- + +void VstSynth::incInstances(int val) + { + _instances += val; + if (_instances == 0 && fstHandle) { + fst_unload(fstHandle); + fstHandle = 0; + } + } + +//--------------------------------------------------------- +// instantiate +//--------------------------------------------------------- + +void* VstSynth::instantiate() + { + ++_instances; + QString n; + n.setNum(_instances); + QString instanceName = baseName() + "-" + n; + doSetuid(); + const char* path = info.filePath().latin1(); + + fstHandle = fst_load(path); + if (fstHandle == 0) { + printf("Synth::instantiate: cannot load vst plugin %s\n", path); + undoSetuid(); + return 0; + } + FST* fst = fst_instantiate(fstHandle, vstHostCallback, 0); + if (fst == 0) { + printf("Synth::instantiate:: cannot instantiate plugin %s\n", path); + undoSetuid(); + return 0; + } + AEffect* plugin = fst->plugin; + plugin->dispatcher (plugin, effMainsChanged, 0, 1, 0, 0.0f); + + /* set program to zero */ + + plugin->dispatcher (plugin, effSetProgram, 0, 0, NULL, 0.0f); + + if (fst_run_editor(fst)) { + printf("Synth::instantiate: cannot create gui"); + undoSetuid(); + return 0; + } +// int vst_version = plugin->dispatcher (plugin, effGetVstVersion, 0, 0, NULL, 0.0f); + undoSetuid(); + return fst; + } + +//--------------------------------------------------------- +// init +//--------------------------------------------------------- + +bool VstSynthIF::init(Synth* s) + { + _fst = (FST*)((VstSynth*)s)->instantiate(); + return (_fst == 0); + } + +//--------------------------------------------------------- +// channels +//--------------------------------------------------------- + +int VstSynthIF::channels() const + { + AEffect* plugin = _fst->plugin; + return plugin->numOutputs; + } + +int VstSynthIF::totalOutChannels() const + { + AEffect* plugin = _fst->plugin; + return plugin->numOutputs; + } + +int VstSynthIF::totalInChannels() const + { + AEffect* plugin = _fst->plugin; + return plugin->numInputs; + } + +//--------------------------------------------------------- +// createSIF +//--------------------------------------------------------- + +//SynthIF* VstSynth::createSIF() const +SynthIF* VstSynth::createSIF(SynthI* s) + { + //return new VstSynthIF(); + + VstSynthIF* sif = new VstSynthIF(s); + sif->init(this, s); + return sif; + } + +//--------------------------------------------------------- +// deactivate3 +//--------------------------------------------------------- + +void VstSynthIF::deactivate3() + { + if (_fst) { + if (_guiVisible) + fst_destroy_editor(_fst); + fst_close(_fst); + _fst = 0; + } + } + +//--------------------------------------------------------- +// getParameter +//--------------------------------------------------------- + +float VstSynthIF::getParameter(unsigned long idx) const + { + return _fst->plugin->getParameter(_fst->plugin, idx); + } + +//--------------------------------------------------------- +// setParameter +//--------------------------------------------------------- + +void VstSynthIF::setParameter(unsigned long idx, float value) + { + _fst->plugin->setParameter(_fst->plugin, idx, value); + } + +//--------------------------------------------------------- +// write +//--------------------------------------------------------- + +void VstSynthIF::write(int level, Xml& xml) const + { + //--------------------------------------------- + // dump current state of synth + //--------------------------------------------- + + int len = 0; + const unsigned char* p; + AEffect* plugin = _fst->plugin; + int params = plugin->numParams; + for (int i = 0; i < params; ++i) { + float f = plugin->getParameter(plugin, i); + xml.floatTag(level, "param", f); + } + } + +//--------------------------------------------------------- +// getData +//--------------------------------------------------------- + +iMPEvent VstSynthIF::getData(MidiPort* mp, MPEventList* el, iMPEvent i, unsigned pos, int ports, unsigned n, float** buffer) + { + AEffect* plugin = _fst->plugin; + for (; i != el->end(); ++i) { + if (mp) + mp->sendEvent(*i); + else { + if (putEvent(*i)) + break; + } + } + if (plugin->flags & effFlagsCanReplacing) { + plugin->processReplacing(plugin, 0, buffer, n); + } + else { + plugin->process(plugin, 0, buffer, n); + } + return el->end(); + } + +//--------------------------------------------------------- +// putEvent +//--------------------------------------------------------- + +bool VstSynthIF::putEvent(const MidiPlayEvent& ev) + { + if (midiOutputTrace) + ev.dump(); + AEffect* plugin = _fst->plugin; + static struct VstEvents events; + static struct VstMidiEvent event; + events.numEvents = 1; + events.reserved = 0; + events.events[0] = (VstEvent*)(&event); + + event.type = kVstMidiType; + event.byteSize = 24; + event.deltaFrames = 0; + event.flags = 0; + event.detune = 0; + event.noteLength = 0; + event.noteOffset = 0; + event.reserved1 = 0; + event.reserved2 = 0; + event.noteOffVelocity = 0; + switch (ev.type()) { + case ME_PITCHBEND: + { + int a = ev.dataA() + 8192; + int b = a >> 7; + event.midiData[0] = (ev.type() | ev.channel()) & 0xff; + event.midiData[1] = a & 0x7f; + event.midiData[2] = b & 0x7f; + event.midiData[3] = 0; + } + break; + + case ME_CONTROLLER: + case ME_NOTEON: + default: + event.midiData[0] = (ev.type() | ev.channel()) & 0xff; + event.midiData[1] = ev.dataA() & 0xff; + event.midiData[2] = ev.dataB() & 0xff; + event.midiData[3] = 0; + break; + } + int rv = plugin->dispatcher(plugin, effProcessEvents, 0, 0, &events, 0.0f); + return false; + } +#else +void initVST() {} +#endif + |