/* * Carla UI bridge code * Copyright (C) 2011-2012 Filipe Coelho * * This program 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 2 of the License, or * any later version. * * This program 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. * * For a full copy of the GNU General Public License see the COPYING file */ #include "carla_bridge_client.h" #include "carla_vst.h" #include "carla_midi.h" #include //CARLA_BRIDGE_START_NAMESPACE; namespace CarlaBridge { // ------------------------------------------------------------------------- #define FAKE_SAMPLE_RATE 44100.0 #define FAKE_BUFFER_SIZE 512 class CarlaBridgeVstClient : public CarlaBridgeClient { public: CarlaBridgeVstClient(CarlaBridgeToolkit* const toolkit) : CarlaBridgeClient(toolkit) { effect = nullptr; widget = new QDialog; } ~CarlaBridgeVstClient() { } // --------------------------------------------------------------------- // initialization bool init(const char* binary, const char*) { // ----------------------------------------------------------------- // open DLL if ( !lib_open(binary)) return false; // ----------------------------------------------------------------- // get DLL main entry VST_Function vstfn = (VST_Function)libSymbol("VSTPluginMain"); if (! vstfn) vstfn = (VST_Function)libSymbol("main"); if (! vstfn) return false; // ----------------------------------------------------------------- // initialize plugin effect = vstfn(VstHostCallback); if ((! effect) || effect->magic != kEffectMagic) return false; // ----------------------------------------------------------------- // initialize VST stuff effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f); #if ! VST_FORCE_DEPRECATED effect->dispatcher(effect, effSetBlockSizeAndSampleRate, 0, FAKE_BUFFER_SIZE, nullptr, FAKE_SAMPLE_RATE); #endif effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr, FAKE_SAMPLE_RATE); effect->dispatcher(effect, effSetBlockSize, 0, FAKE_BUFFER_SIZE, nullptr, 0.0f); effect->dispatcher(effect, effEditOpen, 0, 0, (void*)widget->winId(), 0.0f); #ifdef VESTIGE_HEADER effect->ptr1 = this; #else effect->resvd1 = (intptr_t)this; #endif // ----------------------------------------------------------------- // initialize gui stuff ERect* vstRect; if (effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0.0f)) { int width = vstRect->right - vstRect->left; int height = vstRect->bottom - vstRect->top; widget->setFixedSize(width, height); return true; } return false; } void close() { if (effect) { effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0.0f); effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f); } } // --------------------------------------------------------------------- // processing void setParameter(const int32_t rindex, const double value) { if (effect) effect->setParameter(effect, rindex, value); } void setProgram(const uint32_t index) { if (effect) effect->dispatcher(effect, effSetProgram, 0, index, nullptr, 0.0f); } void setMidiProgram(uint32_t, uint32_t) { } void noteOn(uint8_t, uint8_t) { } void noteOff(uint8_t) { } // --------------------------------------------------------------------- // gui void* getWidget() const { return widget; } bool isResizable() const { return false; } bool needsReparent() const { return true; } // --------------------------------------------------------------------- static intptr_t VstHostCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) { switch (opcode) { case audioMasterAutomate: //osc_send_control(index, opt); break; case audioMasterVersion: return kVstVersion; case audioMasterCurrentId: return 0; // TODO case audioMasterIdle: if (effect) effect->dispatcher(effect, effEditIdle, 0, 0, nullptr, 0.0f); break; case audioMasterGetTime: static VstTimeInfo_R timeInfo; memset(&timeInfo, 0, sizeof(VstTimeInfo_R)); timeInfo.sampleRate = FAKE_SAMPLE_RATE; return (intptr_t)&timeInfo; case audioMasterProcessEvents: #if 0 if (client && ptr) { const VstEvents* const events = (VstEvents*)ptr; for (int32_t i=0; i < events->numEvents; i++) { const VstMidiEvent* const midi_event = (VstMidiEvent*)events->events[i]; uint8_t status = midi_event->midiData[0]; // Fix bad note-off if (MIDI_IS_STATUS_NOTE_ON(status) && midi_event->midiData[2] == 0) status -= 0x10; uint8_t midi_buf[4] = { 0, status, (uint8_t)midi_event->midiData[1], (uint8_t)midi_event->midiData[2] }; osc_send_midi(midi_buf); } } else qDebug("VstHostCallback:audioMasterProcessEvents - Some MIDI Out events were ignored"); #endif break; #if ! VST_FORCE_DEPRECATED case audioMasterTempoAt: // Deprecated in VST SDK 2.4 return 120.0 * 10000; #endif case audioMasterSizeWindow: //if (client) // client->queque_message(BRIDGE_MESSAGE_RESIZE_GUI, index, value, 0.0f); return 1; case audioMasterGetSampleRate: return FAKE_SAMPLE_RATE; case audioMasterGetBlockSize: return FAKE_BUFFER_SIZE; case audioMasterGetVendorString: strcpy((char*)ptr, "falkTX"); break; case audioMasterGetProductString: strcpy((char*)ptr, "Carla-Bridge"); break; case audioMasterGetVendorVersion: return 0x05; // 0.5 case audioMasterVendorSpecific: break; case audioMasterCanDo: #if DEBUG qDebug("VstHostCallback:audioMasterCanDo - %s", (char*)ptr); #endif if (strcmp((char*)ptr, "supplyIdle") == 0) return 1; if (strcmp((char*)ptr, "sendVstEvents") == 0) return 1; if (strcmp((char*)ptr, "sendVstMidiEvent") == 0) return 1; if (strcmp((char*)ptr, "sendVstMidiEventFlagIsRealtime") == 0) return -1; if (strcmp((char*)ptr, "sendVstTimeInfo") == 0) return 1; if (strcmp((char*)ptr, "receiveVstEvents") == 0) return 1; if (strcmp((char*)ptr, "receiveVstMidiEvent") == 0) return 1; if (strcmp((char*)ptr, "receiveVstTimeInfo") == 0) return -1; if (strcmp((char*)ptr, "reportConnectionChanges") == 0) return -1; if (strcmp((char*)ptr, "acceptIOChanges") == 0) return 1; if (strcmp((char*)ptr, "sizeWindow") == 0) return 1; if (strcmp((char*)ptr, "offline") == 0) return -1; if (strcmp((char*)ptr, "openFileSelector") == 0) return -1; if (strcmp((char*)ptr, "closeFileSelector") == 0) return -1; if (strcmp((char*)ptr, "startStopProcess") == 0) return 1; if (strcmp((char*)ptr, "supportShell") == 0) return -1; if (strcmp((char*)ptr, "shellCategory") == 0) return -1; // unimplemented qWarning("VstHostCallback:audioMasterCanDo - Got unknown feature request '%s'", (char*)ptr); return 0; case audioMasterGetLanguage: return kVstLangEnglish; case audioMasterUpdateDisplay: //osc_send_configure("reloadprograms", ""); break; default: #if DEBUG qDebug("VstHostCallback() - code: %s, index: %i, value: " P_INTPTR ", opt: %f", VstOpcode2str(opcode), index, value, opt); #endif break; } return 0; } private: AEffect* effect; QDialog* widget; }; CARLA_BRIDGE_END_NAMESPACE int main(int argc, char* argv[]) { if (argc != 4) { qCritical("%s: bad arguments", argv[0]); return 1; } const char* osc_url = argv[1]; const char* binary = argv[2]; const char* ui_title = argv[3]; using namespace CarlaBridge; // Init toolkit CarlaBridgeToolkit* const toolkit = CarlaBridgeToolkit::createNew(ui_title); toolkit->init(); // Init VST-UI CarlaBridgeVstClient client(toolkit); // Init OSC client.oscInit(osc_url); // Load UI int ret; if (client.init(binary, nullptr)) { toolkit->exec(&client); ret = 0; } else { qCritical("Failed to load VST UI"); ret = 1; } // Close OSC osc_send_exiting(client.getOscServerData()); client.oscClose(); // Close VST-UI client.close(); // Close toolkit toolkit->quit(); delete toolkit; return ret; }