/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * pluginvst.cc * * Mon Feb 8 19:24:40 CET 2016 * Copyright 2016 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of PluginGizmo. * * PluginGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * PluginGizmo 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with PluginGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "pluginvst.h" #include "midievent.h" #include #include bool PluginVST::getFreeWheel() const { return free_wheel; } float PluginVST::getSamplerate() { return AudioEffectX::getSampleRate(); } std::size_t PluginVST::getFramesize() { return AudioEffectX::getBlockSize(); } bool PluginVST::getActive() { return active; } float PluginVST::getLatency() { return current_latency; } void PluginVST::setLatency(float latency) { update_latency = latency; } void PluginVST::updateLatency() { if(update_latency != current_latency) { AudioEffectX::setInitialDelay(update_latency); AudioEffectX::ioChanged(); current_latency = update_latency; } } static uint32_t sdbm_hash(std::string input) { unsigned long hash = 0; for(auto& cha : input) { hash = (unsigned char)cha + (hash << 6) + (hash << 16) - hash; } return hash; } // // VST AudioEffectX implementation: // PluginVST::PluginVST(audioMasterCallback audioMaster) : AudioEffectX(audioMaster, 0, 0) { } PluginVST::~PluginVST() { } void PluginVST::init() { // virtual void setUniqueID (VstInt32 iD) // Must be called to set the plug-ins unique ID! uint32_t hash = sdbm_hash(getId()); AudioEffect::setUniqueID(hash); // virtual void setNumInputs (VstInt32 inputs) // Set the number of inputs the plug-in will handle. For a plug-in which // could change its IO configuration, this number is the maximun available // inputs. AudioEffect::setNumInputs(getNumberOfAudioInputs()); // virtual void setNumOutputs (VstInt32 outputs) // Set the number of outputs the plug-in will handle. For a plug-in which // could change its IO configuration, this number is the maximun available // ouputs. AudioEffect::setNumOutputs(getNumberOfAudioOutputs()); // virtual void canProcessReplacing (bool state=true) // Tells that processReplacing() could be used. Mandatory in VST 2.4! AudioEffect::canProcessReplacing(true); // virtual void canDoubleReplacing (bool state=true) // Tells that processDoubleReplacing() is implemented. AudioEffect::canDoubleReplacing(false); // virtual void programsAreChunks (bool state=true) // Program data is handled in formatless chunks (using getChunk-setChunks). AudioEffect::programsAreChunks(true); // for generic config string support. // virtual void setInitialDelay (VstInt32 delay) // Use to report the plug-in's latency (Group Delay). AudioEffect::setInitialDelay(0); AudioEffectX::isSynth(getNumberOfMidiInputs() > 0); // We might produce output when there is no input. AudioEffectX::noTail(false); if(hasGUI()) { editor = std::make_shared(*this); setEditor(editor.get()); } } void PluginVST::open() { // Called when plug-in is initialized. } void PluginVST::close() { // Called when plug-in will be released. setEditor(nullptr); editor = nullptr; } void PluginVST::suspend() { // Called when plug-in is switched to off. active = false; onActiveChange(active); updateLatency(); } void PluginVST::resume() { updateLatency(); // Called when plug-in is switched to on. active = true; onActiveChange(active); } bool PluginVST::getInputProperties(VstInt32 index, VstPinProperties* props) { if(index < (VstInt32)getNumberOfAudioInputs()) { vst_strncpy(props->label, "Channel ", 63); char temp[11] = {0}; int2string(index + 1, temp, 10); vst_strncat(props->label, temp, 63); props->flags = kVstPinIsActive; return true; } return false; } bool PluginVST::getOutputProperties(VstInt32 index, VstPinProperties* props) { if(index < (VstInt32)getNumberOfAudioOutputs()) { vst_strncpy(props->label, "Channel ", 63); char temp[11] = {0}; int2string(index + 1, temp, 10); vst_strncat(props->label, temp, 63); props->flags = kVstPinIsActive; return true; } return false; } void PluginVST::setSampleRate(float sampleRate) { // Called when the sample rate changes (always in a suspend state). onSamplerateChange(sampleRate); } void PluginVST::setBlockSize(VstInt32 blockSize) { // Called when the Maximun block size changes (always in a suspend state). // Note that the sampleFrames in Process Calls could be smaller than this // block size, but NOT bigger. onFramesizeChange(blockSize); } bool PluginVST::getEffectName(char* name) { vst_strncpy (name, this->getEffectName().c_str(), kVstMaxEffectNameLen); return true; } bool PluginVST::getVendorString(char* text) { vst_strncpy (text, this->getVendorString().c_str(), kVstMaxVendorStrLen); return true; } bool PluginVST::getProductString(char* text) { vst_strncpy (text, this->getProductString().c_str(), kVstMaxProductStrLen); return true; } VstPlugCategory PluginVST::getPlugCategory() { switch(this->getPluginCategory()) { case PluginCategory::Unknown: return kPlugCategUnknown; case PluginCategory::Effect: return kPlugCategEffect; case PluginCategory::Synth: return kPlugCategSynth; case PluginCategory::Analysis: return kPlugCategAnalysis; case PluginCategory::Mastering: return kPlugCategMastering; case PluginCategory::Spacializer: return kPlugCategSpacializer; case PluginCategory::RoomFx: return kPlugCategRoomFx; case PluginCategory::SurroundFx: return kPlugSurroundFx; case PluginCategory::Restoration: return kPlugCategRestoration; case PluginCategory::OfflineProcess: return kPlugCategOfflineProcess; case PluginCategory::Shell: return kPlugCategShell; case PluginCategory::Generator: return kPlugCategGenerator; } return kPlugCategUnknown; } VstInt32 PluginVST::processEvents(VstEvents* events) { // For each process cycle, processEvents() is called once before a // processReplacing() call. for(VstInt32 i = 0; i < events->numEvents; ++i) { auto event = events->events[i]; if(event->type != kVstMidiType) { continue; } auto midi_event = (VstMidiEvent*)event; input_events.emplace_back(midi_event->deltaFrames, midi_event->midiData, midi_event->byteSize); } return 0; } void PluginVST::processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames) { updateLatency(); // Process 32 bit (single precision) floats (always in a resume state). // 0 = realtime/normal // 1 = non-realtime/rendering // 2 = offline processing long lvl = AudioEffectX::getCurrentProcessLevel(); bool last_free_wheel = free_wheel; free_wheel = (lvl != 0); if(last_free_wheel != free_wheel) { onFreeWheelChange(free_wheel); } std::vector input_audio_ports; for(std::size_t i = 0; i < getNumberOfAudioInputs(); ++i) { input_audio_ports.emplace_back(inputs[i]); } std::vector output_audio_ports; output_audio_ports.resize(getNumberOfAudioOutputs()); for(std::size_t i = 0; i < getNumberOfAudioOutputs(); ++i) { output_audio_ports[i] = outputs[i]; } std::vector output_events; // Process events and audio process(pos, input_events, output_events, input_audio_ports, output_audio_ports, (std::size_t)sampleFrames); input_events.clear(); if(getNumberOfMidiOutputs()) { // Translate output_events to VST midi events. std::vector vst_output_event_list; vst_output_event_list.resize(output_events.size()); for(std::size_t i = 0; i < output_events.size(); ++i) { vst_output_event_list[i].deltaFrames = output_events[i].getTime(); vst_output_event_list[i].type = kVstMidiType; const char* data = output_events[i].getData(); for(std::size_t j = 0; j < output_events[i].getSize(); ++j) { vst_output_event_list[i].midiData[j] = data[j]; } vst_output_event_list[i].byteSize = output_events[i].getSize(); } if(!vst_output_event_list.empty()) { // Dispatch output events to host VstEvents vst_output_events; vst_output_events.numEvents = vst_output_event_list.size(); vst_output_events.events[0] = (VstEvent*)vst_output_event_list.data(); sendVstEventsToHost(&vst_output_events); } } pos += sampleFrames; } VstInt32 PluginVST::getChunk(void **data, bool isPreset) { std::string state = onStateSave(); char* chunk = (char*)malloc(state.size() + 1); memcpy(chunk, state.data(), state.size()); *data = chunk; return state.size(); } VstInt32 PluginVST::setChunk(void *data, VstInt32 byteSize, bool isPreset) { std::string state; state.append((const char*)data, (std::size_t)byteSize); onStateRestore(state); return 0; } VstInt32 PluginVST::canDo(char* text) { std::string feature = text; // Midi input if((feature == "receiveVstMidiEvent") &&//PlugCanDos::canDoReceiveVstMidiEvent (getNumberOfMidiInputs() > 0)) { return 1; } // Midi output if((feature == "sendVstMidiEvent") && // PlugCanDos::canDoSendVstMidiEvent (getNumberOfMidiOutputs() > 0)) { return 1; } // For FreeWheel functionality. if(feature == "offline") // PlugCanDos::canDoOffline { return 1; } // TODO: For soft-bypass state. //if(feature == "bypass") // PlugCanDos::canDoBypass) //{ // return 1; //} // TODO: For receiving metronome ticks? //if(feature == "receiveVstTimeInfo") // PlugCanDos::canDoReceiveVstTimeInfo) //{ // return 1; //} return 0; } void PluginVST::resizeWindow(std::size_t width, std::size_t height) { if(editor) { editor->rect.top = 0; editor->rect.left = 0; editor->rect.right = width; editor->rect.bottom = height; } } void PluginVST::closeWindow() { } PluginVST::UI::UI(PluginVST& plugin_vst) : AEffEditor(&plugin_vst) , plugin_vst(plugin_vst) { } bool PluginVST::UI::open(void* ptr) { plugin_vst.createWindow(ptr); AEffEditor::open(ptr); is_open = true; return true; } void PluginVST::UI::close() { is_open = false; plugin_vst.onDestroyWindow(); AEffEditor::close(); } bool PluginVST::UI::isOpen() { return is_open; } void PluginVST::UI::idle() { if(is_open) { plugin_vst.onIdle(); } AEffEditor::idle(); } bool PluginVST::UI::getRect(ERect** rect) { *rect = &this->rect; return true; }