From a27d71e88bd8f950e11e7a1a2ba0a4dd88f541f4 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 11 Mar 2016 12:28:44 +0100 Subject: Initial import from DrumGizmo branch. --- COPYING | 165 +++++++++++++++ Makefile | 64 ++++++ add_file | 64 ++++++ manifest.ttl | 110 ++++++++++ midievent.cc | 71 +++++++ midievent.h | 56 ++++++ plugin.h | 145 +++++++++++++ pluginlv2.cc | 637 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pluginlv2.h | 225 +++++++++++++++++++++ plugintest.cc | 334 ++++++++++++++++++++++++++++++ plugintest.h | 113 +++++++++++ pluginvst.cc | 391 +++++++++++++++++++++++++++++++++++ pluginvst.h | 208 +++++++++++++++++++ 13 files changed, 2583 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100755 add_file create mode 100644 manifest.ttl create mode 100644 midievent.cc create mode 100644 midievent.h create mode 100644 plugin.h create mode 100644 pluginlv2.cc create mode 100644 pluginlv2.h create mode 100644 plugintest.cc create mode 100644 plugintest.h create mode 100644 pluginvst.cc create mode 100644 pluginvst.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a630927 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +PLUGIN_NAME=plugintest +PLUGIN_URI=http://example.org/plugintest + +CXXFLAGS = -DX11 -I. -fPIC -std=c++11 -Wall -shared -Wl,--no-undefined + +LV2_CXXFLAGS = -DLV2 -g -Werror -DLV2_PLUGIN_URI=\"$(PLUGIN_URI)\" +LV2_SRC = \ + pluginlv2.cc \ + midievent.cc \ + plugintest.cc + + +VST_PATH=/path/to/vstsdk2.4 +VST_SRC_BASE=$(VST_PATH)/public.sdk/source/vst2.x/ +VST_CXXFLAGS=-DVST -I$(VST_PATH) +VST_SRC = \ + ${VST_SRC_BASE}/audioeffectx.cpp \ + ${VST_SRC_BASE}/audioeffect.cpp \ + ${VST_SRC_BASE}/vstplugmain.cpp \ + pluginvst.cc \ + midievent.cc \ + plugintest.cc + +## LV2 targets + +$(PLUGIN_NAME)_lv2.so: $(LV2_SRC) + g++ $(CXXFLAGS) $(LV2_CXXFLAGS) $(LV2_SRC) -lX11 -o$(PLUGIN_NAME)_lv2.so + +install-lv2: $(PLUGIN_NAME)_lv2.so + mkdir -p lib/lv2/$(PLUGIN_NAME) + cp manifest.ttl $(PLUGIN_NAME)_lv2.so lib/lv2/$(PLUGIN_NAME) + +test-lv2: install-lv2 + LV2_PATH="$(PWD)/lib/lv2" lv2_inspect $(PLUGIN_URI) + carla-discovery-native lv2 $(PWD)/lib/lv2/$(PLUGIN_NAME) + +clean-lv2: + rm -f $(PLUGIN_NAME)_lv2.so + +## VST targets + +$(PLUGIN_NAME)_vst.so: $(VST_SRC) + [ "$VST_PATH" == "" ] && (echo "Run make with 'VST_PATH=/path/to/vstsdk2.4 make'"; false) + g++ $(CXXFLAGS) $(VST_CXXFLAGS) $(VST_SRC) -lX11 -o$(PLUGIN_NAME)_vst.so + +install-vst: $(PLUGIN_NAME)_vst.so + mkdir -p lib/vst/$(PLUGIN_NAME) + cp $(PLUGIN_NAME)_vst.so lib/vst/$(PLUGIN_NAME) + +test-vst: install-vst + carla-discovery-native vst lib/vst/$(PLUGIN_NAME)/$(PLUGIN_NAME)_vst.so + +clean-vst: + rm -f $(PLUGIN_NAME)_vst.so + +## Common targets + +install: install-lv2 install-vst + +test: test-lv2 test-vst + +all: $(PLUGIN_NAME)_lv2.so $(PLUGIN_NAME)_vst.so + +clean: clean-lv2 clean-vst \ No newline at end of file diff --git a/add_file b/add_file new file mode 100755 index 0000000..8043794 --- /dev/null +++ b/add_file @@ -0,0 +1,64 @@ +#!/bin/bash +PROJECT="PluginGizmo" + +function allfile() { + if [ "$USER" == "deva" ] + then + NAME="Bent Bisballe Nyeng"; EMAIL="deva@aasimon.org" + fi + + echo "/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */" > $1; + echo "/***************************************************************************" >> $1; + echo " * $1" >> $1; + echo " *" >> $1 ; + echo " * `date`" >> $1; + echo " * Copyright "`date +%Y | xargs`" $NAME" >> $1; + echo " * $EMAIL" >> $1; + echo " ****************************************************************************/" >> $1; + echo "" >> $1; + echo "/*" >> $1; + echo " * This file is part of $PROJECT." >> $1; + echo " *" >> $1; + echo " * $PROJECT is free software; you can redistribute it and/or modify" >> $1; + echo " * it under the terms of the GNU Lesser General Public License as published by" >> $1; + echo " * the Free Software Foundation; either version 3 of the License, or" >> $1; + echo " * (at your option) any later version." >> $1; + echo " *" >> $1; + echo " * $PROJECT is distributed in the hope that it will be useful," >> $1; + echo " * but WITHOUT ANY WARRANTY; without even the implied warranty of" >> $1; + echo " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" >> $1; + echo " * GNU Lesser General Public License for more details." >> $1; + echo " *" >> $1; + echo " * You should have received a copy of the GNU Lesser General Public License" >> $1; + echo " * along with $PROJECT; if not, write to the Free Software" >> $1; + echo " * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA." >> $1; + echo " */" >> $1; +} + +function ccfile() { + local hf=`echo -n $1 | cut -d'.' -f1`.h; + hfile $hf; + + allfile $1; + echo -n '#include "' >> $1; + echo -n $hf >> $1; + echo '"' >> $1; + echo '' >> $1; +} + +function hfile() { + allfile $1; + local hn=`echo $1 | tr 'a-z.' 'A-Z_'` + local pr=`echo $PROJECT | tr 'a-z.' 'A-Z_'` + echo "#pragma once" >> $1; +} + +if [ "$#" = "1" ]; then +if [ "CC" = `echo $1 | cut -d'.' -f2 | tr 'a-z' 'A-Z'` ]; then + ccfile $1; +fi; +if [ "H" = `echo $1 | cut -d'.' -f2 | tr 'a-z' 'A-Z'` ]; then + hfile $1; +fi; +else echo "Usage: $0 filename"; +fi; diff --git a/manifest.ttl b/manifest.ttl new file mode 100644 index 0000000..61304ed --- /dev/null +++ b/manifest.ttl @@ -0,0 +1,110 @@ +# LV2 Plugin +# Copyright 2016 Bent Bisballe Nyeng +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix atom: . +@prefix ui: . +@prefix state: . +@prefix pprops: . + +@prefix lv2: . +@prefix rdfs: . + + + a ui:X11UI ; + lv2:requiredFeature ui:resize ; + lv2:extensionData ui:resize ; + lv2:requiredFeature ui:idleInterface ; + lv2:extensionData ui:idleInterface ; + ui:binary . + + + a lv2:Plugin ; + lv2:binary ; + a lv2:InstrumentPlugin ; + doap:name "LV2Test" ; + ui:ui ; + doap:maintainer [ + foaf:name "LV2Test" ; + foaf:homepage ; + ] ; + doap:license ; + doap:license ; + lv2:optionalFeature ; + lv2:optionalFeature ; + lv2:extensionData state:interface ; + lv2:port [ + a lv2:InputPort, lv2:ControlPort ; + lv2:index 0 ; + lv2:symbol "lv2_freewheel" ; + lv2:name "Freewheel" ; + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + lv2:designation ; + lv2:portProperty lv2:toggled ; + lv2:portProperty pprops:hasStrictBounds; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:designation ; + lv2:index 1; + lv2:symbol "latency"; + lv2:name "Latency"; + lv2:minimum 0; + lv2:maximum 192000; + lv2:portProperty lv2:reportsLatency, lv2:integer; + ] , [ + a atom:AtomPort , + lv2:InputPort; + atom:bufferType atom:Sequence ; + atom:supports ; + lv2:index 2 ; + lv2:symbol "control" ; + lv2:name "Control" + ] , [ + a atom:AtomPort , + lv2:OutputPort; + atom:bufferType atom:Sequence ; + atom:supports ; + lv2:index 3 ; + lv2:symbol "control" ; + lv2:name "Control" + ] , [ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 4 ; + lv2:symbol "in1" ; + lv2:name "In1" + ] , [ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 5 ; + lv2:symbol "in2" ; + lv2:name "In2" + ] , [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 6 ; + lv2:symbol "out1" ; + lv2:name "Out1" + ], [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 7 ; + lv2:symbol "out2" ; + lv2:name "Out2" + ] . diff --git a/midievent.cc b/midievent.cc new file mode 100644 index 0000000..9b3dfb5 --- /dev/null +++ b/midievent.cc @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * midievent.cc + * + * Sun Feb 7 15:09:01 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 "midievent.h" + +#include + +MidiEvent::MidiEvent(int64_t time, const char* data, std::size_t size) + : time(time) +{ + std::cout << __PRETTY_FUNCTION__ << + " data: " << (void*)data << + " size: " << size << + std::endl; + + this->data.resize(size); + for(std::size_t i = 0; i < size; ++i) + { + this->data[i] = data[i]; + } + + if ((data[0] & 0xF0) == 0x80) { // note off + type = MidiEventType::NoteOff; + key = data[1]; + velocity = data[2]; + } + + if ((data[0] & 0xF0) == 0x90) { // note on + type = MidiEventType::NoteOn; + key = data[1]; + velocity = data[2]; + } +} + +int64_t MidiEvent::getTime() const +{ + return time; +} + +const char* MidiEvent::getData() const +{ + return data.data(); +} + +std::size_t MidiEvent::getSize() const +{ + return data.size(); +} diff --git a/midievent.h b/midievent.h new file mode 100644 index 0000000..046a01a --- /dev/null +++ b/midievent.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * midievent.h + * + * Sun Feb 7 15:09:01 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. + */ +#pragma once + +#include +#include + +#include + +enum class MidiEventType { + Unknown, + NoteOn, + NoteOff, +}; + +class MidiEvent { +public: + MidiEvent() = default; + MidiEvent(int64_t time, const char* data, std::size_t size); + + int64_t getTime() const; + const char* getData() const; + std::size_t getSize() const; + + MidiEventType type{MidiEventType::Unknown}; + int key{0}; + int velocity{0}; + +private: + int64_t time; + std::vector data; +}; diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..0c58771 --- /dev/null +++ b/plugin.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * plugin.h + * + * Sun Feb 7 14:11: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. + */ +#pragma once + +#include +#include + +#include + +class MidiEvent; + +//! Abstract base-class for plugin implementations. +class Plugin { +public: + //! Implement this to create a new plugin instance. + static Plugin* create(); + + //! Get current free-wheel mode. + virtual bool getFreeWheel() const = 0; + + //! This method is called by the host when the free-wheel mode changes. + virtual void onFreeWheelChange(bool freewheel) = 0; + + + //! Call this to get current samplerate. + virtual float getSamplerate() = 0; + + //! This method is called by the host when the samplerate changes. + virtual void onSamplerateChange(float samplerate) = 0; + + + //! Call this to get current frame-size. + virtual std::size_t getFramesize() = 0; + + //! This method is called by the host when the frame-size changes. + virtual void onFramesizeChange(std::size_t framesize) = 0; + + + //! Call this to get current active state + virtual bool getActive() = 0; + + //! This method is called by the host when the active state changes. + virtual void onActiveChange(bool active) = 0; + + + //! This method is called by the host to get the current state for storing. + virtual std::string onStateSave() = 0; + + //! This method is called by the host when a new state has been loaded. + virtual void onStateRestore(const std::string& config) = 0; + + + //! This is method is called by the host to get the current latency. + //! \param The latency in samples. + virtual float getLatency() = 0; + + //! Call this method to signal a latency change to the host. + //! \param latency The latency in samples. + virtual void setLatency(float latency) = 0; + + + //! Called by the the host to get the number of midi input channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfMidiInputs() = 0; + + //! Called by the the host to get the number of midi output channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfMidiOutputs() = 0; + + //! Called by the the host to get the number of audio input channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfAudioInputs() = 0; + + //! Called by the the host to get the number of audio output channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfAudioOutputs() = 0; + + + //! Get unique plugin id. + virtual std::string getId() = 0; + + //! Process callback. + virtual void process(std::size_t pos, + const std::vector& input_events, + std::vector& output_events, + const std::vector& input_samples, + const std::vector& output_samples, + std::size_t count) = 0; + + + // + // GUI (optional) + // + + //! Return true if a GUI implementation is to be used. + virtual bool hasGUI() + { + return false; + } + + //! Create new window. + virtual void createWindow(void *parent) {} + + //! Destroy window. + virtual void onDestroyWindow() {} + + //! Show window. + virtual void onShowWindow() {} + + //! Hide window. + virtual void onHideWindow() {} + + //! Called regularly by host; process ui events. + virtual void onIdle() {} + + //! Signal new window size to host. + virtual void resizeWindow(std::size_t width, std::size_t height) = 0; + + //! Signal close window event to the host. + virtual void closeWindow() = 0; +}; diff --git a/pluginlv2.cc b/pluginlv2.cc new file mode 100644 index 0000000..d351104 --- /dev/null +++ b/pluginlv2.cc @@ -0,0 +1,637 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * pluginlv2.cc + * + * Sun Feb 7 15:15:24 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 "pluginlv2.h" + +#include +#include + +#include "midievent.h" + +#define LV2_PLUGIN_URI__atom LV2_PLUGIN_URI "/atom#" +#define LV2_PLUGIN_URI__instance LV2_PLUGIN_URI "#plugin-instance" +#define LV2_PLUGIN_URI__ui LV2_PLUGIN_URI "#ui" + +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include +#include + +bool PluginLV2::getFreeWheel() const +{ + return free_wheel; +} + +float PluginLV2::getSamplerate() +{ + return sample_rate; +} + +std::size_t PluginLV2::getFramesize() +{ + return frame_size; +} + +bool PluginLV2::getActive() +{ + return active; +} + +float PluginLV2::getLatency() +{ + if(latency_port) + { + return *latency_port; + } + + return 0.0f; +} + +void PluginLV2::setLatency(float latency) +{ + if(latency_port) + { + *latency_port = latency; + } +} + +LV2_Handle PluginLV2::instantiate(const struct _LV2_Descriptor* descriptor, + double sample_rate, + const char* bundle_path, + const LV2_Feature *const *features) +{ + PluginLV2* plugin_lv2 = createEffectInstance(); + + plugin_lv2->sample_rate = sample_rate; + + plugin_lv2->input_event_ports.resize(plugin_lv2->getNumberOfMidiInputs(), + nullptr); + plugin_lv2->output_event_ports.resize(plugin_lv2->getNumberOfMidiOutputs(), + nullptr); + + plugin_lv2->input_audio_ports.resize(plugin_lv2->getNumberOfAudioInputs()); + plugin_lv2->output_audio_ports.resize(plugin_lv2->getNumberOfAudioOutputs()); + + for(auto& port : plugin_lv2->output_audio_ports) + { + port = nullptr; + } + + for(auto& port : plugin_lv2->input_audio_ports) + { + port = nullptr; + } + + while(*features != nullptr) + { + std::string uri = (*features)->URI; + void* data = (*features)->data; + + if(uri == LV2_URID__map) + { + plugin_lv2->map = (LV2_URID_Map*)data; + } + + ++features; + } + + // Only reported on creation. + plugin_lv2->onSamplerateChange(sample_rate); + + return (LV2_Handle)plugin_lv2; +} + +void PluginLV2::connectPort(LV2_Handle instance, uint32_t port, + void *data_location) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + if(port == static_cast(LV2Ports::FreeWheel)) + { + plugin_lv2->free_wheel_port = (float*)data_location; + if(plugin_lv2->free_wheel_port) + { + plugin_lv2->free_wheel = (*plugin_lv2->free_wheel_port != 0.0f); + // Signal first time. + plugin_lv2->onFreeWheelChange(plugin_lv2->free_wheel); + } + } + + if(port == static_cast(LV2Ports::Latency)) + { + plugin_lv2->latency_port = (float*)data_location; + } + + uint32_t port_offset = static_cast(LV2Ports::PortOffset); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfMidiInputs()))) + { + int port_index = port - port_offset; + plugin_lv2->input_event_ports[port_index] = + (LV2_Atom_Sequence*)data_location; + } + + port_offset += plugin_lv2->getNumberOfMidiInputs(); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfMidiOutputs()))) + { + int port_index = port - port_offset; + plugin_lv2->output_event_ports[port_index] = + (LV2_Atom_Sequence*)data_location; + } + + port_offset += plugin_lv2->getNumberOfMidiOutputs(); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfAudioInputs()))) + { + int port_index = port - port_offset; + plugin_lv2->input_audio_ports[port_index] = (float*)data_location; + } + + port_offset += plugin_lv2->getNumberOfAudioInputs(); + + if((port >= port_offset) && + (port < (port_offset + plugin_lv2->getNumberOfAudioOutputs()))) + { + int port_index = port - port_offset; + plugin_lv2->output_audio_ports[port_index] = (float*)data_location; + } +} + +class Sequence { +public: + Sequence(LV2_URID_Map& map, void* buffer, std::size_t buffer_size); + void clear(); + void addMidiEvent(std::size_t pos, const char* data, std::size_t size); + void* data(); + +private: + void *buffer; + std::size_t buffer_size; + LV2_Atom_Sequence *seq; + LV2_URID_Map& map; +}; + +Sequence::Sequence(LV2_URID_Map& map, void* buffer, std::size_t buffer_size) + : map(map) +{ + this->buffer = buffer; + this->buffer_size = buffer_size; + + seq = (LV2_Atom_Sequence*)buffer; + + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); + seq->atom.type = map.map(map.handle, LV2_ATOM__Sequence); + seq->body.unit = 0; + seq->body.pad = 0; +} + +// Keep this to support atom extension from lv2 < 1.10 +static inline void _lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) +{ + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); +} + +void Sequence::clear() +{ + _lv2_atom_sequence_clear(seq); +} + +// Keep this to support atom extension from lv2 < 1.10 +static inline LV2_Atom_Event* +_lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event) +{ + const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; + + if(capacity - seq->atom.size < total_size) + { + return nullptr; + } + + LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size); + memcpy(e, event, total_size); + + seq->atom.size += lv2_atom_pad_size(total_size); + + return e; +} + +void Sequence::addMidiEvent(std::size_t pos, const char* data, std::size_t size) +{ + typedef struct { + LV2_Atom_Event event; + uint8_t msg[6]; + } MIDINoteEvent; + + + MIDINoteEvent ev; + ev.event.time.frames = pos; + ev.event.body.type = map.map(map.handle, LV2_MIDI__MidiEvent); + ev.event.body.size = size;//sizeof(LV2_Atom_Event) + size; + + assert(size <= sizeof(ev.msg)); // Assert that we have room for the message + + memcpy(ev.msg, data, size + sizeof(LV2_Atom_Event)); + + _lv2_atom_sequence_append_event(seq, this->buffer_size, &ev.event); +} + +void* Sequence::data() +{ + return buffer; +} + +void PluginLV2::run(LV2_Handle instance, uint32_t sample_count) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + // Handle free-wheel state + if(plugin_lv2->free_wheel_port != nullptr) + { + bool new_free_wheel = *plugin_lv2->free_wheel_port != 0.0f; + if(new_free_wheel != plugin_lv2->free_wheel) + { + plugin_lv2->free_wheel = new_free_wheel; + plugin_lv2->onFreeWheelChange(plugin_lv2->free_wheel); + } + } + + // Handle frame size + if(plugin_lv2->frame_size != sample_count) + { + plugin_lv2->frame_size = sample_count; + plugin_lv2->onFramesizeChange(plugin_lv2->frame_size); + } + + // Convert input lv2 events to input events. + std::vector input_events; + for(std::size_t port = 0; port < plugin_lv2->getNumberOfMidiInputs(); ++port) + { + if(plugin_lv2->input_event_ports[port] == nullptr) + { + continue; // Not yet connected. + } + + auto& event_port = plugin_lv2->input_event_ports[port]; + + LV2_Atom_Event* ev = lv2_atom_sequence_begin(&event_port->body); + while(!lv2_atom_sequence_is_end(&event_port->body, + event_port->atom.size, + ev)) + { + if(ev->body.type != plugin_lv2->map->map(plugin_lv2->map->handle, + LV2_MIDI__MidiEvent)) + { + continue; // not a midi event. + } + + const char* data = (char*)(ev + 1); + input_events.emplace_back(ev->time.frames, data, ev->body.size); + + ev = lv2_atom_sequence_next(ev); + } + } + + std::vector output_events; + + // Process events and audio + plugin_lv2->process(plugin_lv2->pos, + input_events, + output_events, + plugin_lv2->input_audio_ports, + plugin_lv2->output_audio_ports, + sample_count); + + // Convert output events to lv2 events + if(plugin_lv2->getNumberOfMidiOutputs() > 0) + { + if(plugin_lv2->map != nullptr) + { + if(plugin_lv2->output_event_ports[0] != nullptr) // Not yet connected? + { + auto& event_port = plugin_lv2->output_event_ports[0]; // TODO: Split? + Sequence seq(*plugin_lv2->map, + &event_port->body + 1, + event_port->atom.size); + for(auto midi_event : output_events) + { + seq.addMidiEvent(midi_event.getTime(), + midi_event.getData(), + midi_event.getSize()); + } + } + } + } + + plugin_lv2->pos += sample_count; +} + +void PluginLV2::activate(LV2_Handle instance) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + plugin_lv2->active = true; + plugin_lv2->onActiveChange(plugin_lv2->active); +} + +void PluginLV2::deactivate(LV2_Handle instance) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + plugin_lv2->active = false; + plugin_lv2->onActiveChange(plugin_lv2->active); +} + +void PluginLV2::cleanup(LV2_Handle instance) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + delete plugin_lv2; +} + +// +// State handling +// + +LV2_State_Status PluginLV2::save(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + if(plugin_lv2->map == nullptr) + { + // Missing urid feature? + return LV2_STATE_ERR_NO_FEATURE; + } + + std::string config = plugin_lv2->onStateSave(); + + store(handle, + plugin_lv2->map->map(plugin_lv2->map->handle, + LV2_PLUGIN_URI__atom "config"), + config.data(), + config.length(), + plugin_lv2->map->map(plugin_lv2->map->handle, LV2_ATOM__Chunk), + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + return LV2_STATE_SUCCESS; +} + +LV2_State_Status PluginLV2::restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + if(plugin_lv2->map == nullptr) + { + // Missing urid feature? + return LV2_STATE_ERR_NO_FEATURE; + } + + std::size_t size; + uint32_t type; + + const char* data = + (const char*)retrieve(handle, + plugin_lv2->map->map(plugin_lv2->map->handle, + LV2_PLUGIN_URI__atom "config"), + &size, &type, &flags); + + if(data && size) + { + std::string config; + + config.append(data, size); + plugin_lv2->onStateRestore(config); + } + + return LV2_STATE_SUCCESS; +} + +static LV2_State_Interface persist = { + PluginLV2::save, + PluginLV2::restore +}; + +const void* PluginLV2::extensionData(const char *uri) +{ + if(!strcmp(uri, LV2_STATE__interface)) + { + return &persist; + } + + return nullptr; +} + + +// +// GUI +// +LV2UI_Handle PluginLV2::uiInstantiate(const struct _LV2UI_Descriptor*descriptor, + const char * plugin_uri, + const char * bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature * const * features) +{ + LV2_Handle instance = nullptr; + void* parent = nullptr; + LV2UI_Resize* resize = nullptr; + + while(*features != nullptr) { + std::string uri = (*features)->URI; + void *data = (*features)->data; + + if(uri == LV2_INSTANCE_ACCESS_URI) + { + instance = (LV2_Handle)data; + } + + if(uri == LV2_UI__parent) + { + parent = data; + } + + if(uri == LV2_UI__resize) + { + resize = (LV2UI_Resize*)data; + } + + features++; + } + + if(instance == nullptr) + { + return nullptr; + } + + PluginLV2* plugin_lv2 = (PluginLV2*)instance; + + // Do we have a GUI? + if(plugin_lv2->hasGUI() == false) + { + return nullptr; + } + + plugin_lv2->resize = resize; + + plugin_lv2->createWindow(parent); + plugin_lv2->resize->ui_resize(plugin_lv2->resize->handle, 100, 100); + + return plugin_lv2; +} + +void PluginLV2::resizeWindow(std::size_t width, std::size_t height) +{ + if(resize) + { + resize->ui_resize(resize->handle, width, height); + } +} + +void PluginLV2::closeWindow() +{ +} + +static const LV2_Descriptor descriptor = { + LV2_PLUGIN_URI, + PluginLV2::instantiate, + PluginLV2::connectPort, + PluginLV2::activate, + PluginLV2::run, + PluginLV2::deactivate, + PluginLV2::cleanup, + PluginLV2::extensionData +}; + +void PluginLV2::uiCleanup(LV2UI_Handle handle) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)handle; + plugin_lv2->onDestroyWindow () ; + +} + +int PluginLV2::uiIdle(LV2UI_Handle handle) +{ + PluginLV2* plugin_lv2 = (PluginLV2*)handle; + plugin_lv2->onIdle(); + return 0; +} + +static const LV2UI_Idle_Interface idle_iface = { + PluginLV2::uiIdle +}; + +const void* PluginLV2::uiExtensionData(const char* uri) +{ + if(!strcmp(uri, LV2_UI__idleInterface)) + { + return &idle_iface; + } + + return NULL; +} + +static LV2UI_Descriptor ui_descriptor = { + LV2_PLUGIN_URI__ui, + PluginLV2::uiInstantiate, + PluginLV2::uiCleanup, + nullptr,//PluginLV2::ui_port_event, + PluginLV2::uiExtensionData +}; + +#ifdef __cplusplus +extern "C" { +#endif + +LV2_SYMBOL_EXPORT +const LV2_Descriptor* lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return nullptr; + } +} + +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index) +{ + switch(index) { + case 0: + return &ui_descriptor; + default: + return nullptr; + } +} + +// +// DynManifest experiments failed, but the code is kept here for nostalgic +// reasons :-) +// +// +//int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle* handle, +// const LV2_Feature* const* features) +//{ +// *handle = createEffectInstance(); +// return 0; +//} +// +//int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle, FILE* fp) +//{ +//// PluginLV2* plugin_lv2 = (PluginLV2*)handle; +// +// fprintf(fp, "@prefix lv2: .\n"); +// fprintf(fp, "<" LV2_PLUGIN_URI "> a lv2:Plugin .\n"); +// return 0; +//} +// +//int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle, FILE* fp, +// const char* uri) +//{ +// //PluginLV2* plugin_lv2 = (PluginLV2*)handle; +// printf("%s '%s'\n", __PRETTY_FUNCTION__, uri); +// return 0; +//} +// +//void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle) +//{ +// PluginLV2* plugin_lv2 = (PluginLV2*)handle; +// delete plugin_lv2; +//} + +#ifdef __cplusplus +} +#endif diff --git a/pluginlv2.h b/pluginlv2.h new file mode 100644 index 0000000..61fef02 --- /dev/null +++ b/pluginlv2.h @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * pluginlv2.h + * + * Sun Feb 7 15:15:23 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. + */ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +enum class LV2Ports +{ + FreeWheel = 0, + Latency = 1, + PortOffset = 2, +}; + +class PluginLV2 + : public Plugin +{ +public: + virtual ~PluginLV2() = default; + + //! Get current free-wheel mode. + bool getFreeWheel() const override; + + //! This method is called by the host when the free-wheel mode changes. + virtual void onFreeWheelChange(bool freewheel) override = 0; + + + //! Call this to get current samplerate. + float getSamplerate() override; + + //! This method is called by the host when the samplerate changes. + virtual void onSamplerateChange(float samplerate) = 0; + + //! Call this to get current frame-size. + std::size_t getFramesize() override; + + //! This method is called by the host when the frame-size changes. + virtual void onFramesizeChange(std::size_t framesize) override = 0; + + + //! Call this to get current active state + bool getActive() override; + + //! This method is called by the host when the active state changes. + virtual void onActiveChange(bool active) override = 0; + + + //! This method is called by the host to get the current state for storing. + virtual std::string onStateSave() override = 0; + + //! This method is called by the host when a new state has been loaded. + virtual void onStateRestore(const std::string& config) override = 0; + + + //! This is method is called by the host to get the current latency. + //! \param The latency in samples. + float getLatency() override; + + //! Call this method to signal a latency change to the host. + //! \param latency The latency in samples. + void setLatency(float latency) override; + + + //! Called by the the host to get the number of audio input channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfAudioInputs() override = 0; + + //! Called by the the host to get the number of audio output channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfAudioOutputs() override = 0; + + + //! Called by the the host to get the number of midi input channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfMidiInputs() override = 0; + + //! Called by the the host to get the number of midi output channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfMidiOutputs() override = 0; + + + //! Get unique plugin id. + std::string getId() override = 0; + + virtual void process(std::size_t pos, + const std::vector& input_events, + std::vector& output_events, + const std::vector& input_samples, + const std::vector& output_samples, + std::size_t count) = 0; + + // + // GUI (optional) + // + + //! Return true if a GUI implementation is to be used. + virtual bool hasGUI() override + { + return false; + } + + //! Create new window. + virtual void createWindow(void *parent) override {} + + //! Destroy window. + virtual void onDestroyWindow() override {} + + //! Show window. + virtual void onShowWindow() override {} + + //! Hide window. + virtual void onHideWindow() override {} + + //! Called regularly by host; process ui events. + virtual void onIdle() override {} + + //! Signal new window size to host. + void resizeWindow(std::size_t width, std::size_t height) override; + + //! Signal close window event to the host. + void closeWindow() override; + +public: + static LV2_Handle instantiate(const struct _LV2_Descriptor* descriptor, + double sample_rate, + const char* bundle_path, + const LV2_Feature* const * features); + + static void connectPort(LV2_Handle instance, uint32_t port, + void *data_location); + + static void run(LV2_Handle instance, uint32_t sample_count); + + static void activate(LV2_Handle instance); + + static void deactivate(LV2_Handle instance); + + static void cleanup(LV2_Handle instance); + + static const void* extensionData(const char *uri); + + static LV2_State_Status save(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); + + static LV2_State_Status restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); + + static LV2UI_Handle uiInstantiate(const struct _LV2UI_Descriptor * descriptor, + const char * plugin_uri, + const char * bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget * widget, + const LV2_Feature * const * features); + + static void uiCleanup(LV2UI_Handle handle); + + static int uiIdle(LV2UI_Handle handle); + + static const void* uiExtensionData(const char* uri); + +private: + float* free_wheel_port{nullptr}; + bool free_wheel{false}; + + float sample_rate{0}; + + float* latency_port{nullptr}; + + std::size_t frame_size{0}; + + std::size_t pos{0}; + + std::vector input_event_ports; + std::vector output_event_ports; + std::vector input_audio_ports; + std::vector output_audio_ports; + + LV2_URID_Map* map{nullptr}; + + bool active{false}; + + // + // GUI + // + LV2UI_Resize* resize{nullptr}; +}; + +PluginLV2* createEffectInstance(); diff --git a/plugintest.cc b/plugintest.cc new file mode 100644 index 0000000..93debb2 --- /dev/null +++ b/plugintest.cc @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * pluginlv2test.cc + * + * Mon Feb 8 07:21:09 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 "plugintest.h" + +#include "midievent.h" + +#include +#include +#include +#include + +#include + +#ifdef LV2 +// Entry point for lv2 plugin instantiation. +PluginLV2* createEffectInstance() +{ + return new PluginTest(); +} +#endif + +#ifdef VST +// Entry point for vst plugin instantiation. +AudioEffect* createEffectInstance(audioMasterCallback audioMaster) +{ + return new PluginTest(audioMaster); +} + +PluginTest::PluginTest(audioMasterCallback audioMaster) + : PluginVST(audioMaster) +{ +} +#endif + +void PluginTest::onFreeWheelChange(bool freewheel) +{ +} + +void PluginTest::onSamplerateChange(float samplerate) +{ +} + +void PluginTest::onFramesizeChange(size_t framesize) +{ +} + +void PluginTest::onActiveChange(bool active) +{ +} + +std::string PluginTest::onStateSave() +{ + return ""; +} + +void PluginTest::onStateRestore(const std::string& config) +{ +} + +size_t PluginTest::getNumberOfMidiInputs() +{ + return 1; +} + +size_t PluginTest::getNumberOfMidiOutputs() +{ + return 1; +} + +size_t PluginTest::getNumberOfAudioInputs() +{ + return 2; +} + +size_t PluginTest::getNumberOfAudioOutputs() +{ + return 2; +} + +std::string PluginTest::getId() +{ + return "PluginTest"; +} + +void PluginTest::process(size_t pos, + const std::vector& input_events, + std::vector& output_events, + const std::vector& input_samples, + const std::vector& output_samples, + size_t count) +{ + setLatency(44100); + + float sample_rate = getSamplerate(); + static float frequency = 0.0f; + static float amplitude = 0.0f; + + for(auto& event : input_events) + { + if(event.type == MidiEventType::NoteOn) + { + float note = event.key; + frequency = 110.0f * pow(2, note/12.0); + amplitude = (float)event.velocity / 127.0f; + } + + if(event.type == MidiEventType::NoteOff) + { + frequency = 0.0f; + amplitude = 0.0f; + } + } + + for(float* ch : output_samples) + { + float p = pos; + for(size_t s = 0; s < count; ++s) + { + const float pi = 3.141592653f; + float value = sin(p * 2.0f * pi / sample_rate * frequency) * amplitude; + + //value = value * 10.0; + + ch[s] = value; + + p += 1.0f; + } + } +} + +bool PluginTest::hasGUI() +{ + return true; +} + +static int n = 0; +static const char message[] = "Hello, world!"; + +#if defined(WIN32) +LRESULT CALLBACK PluginTest::wndProc(HWND hwnd, UINT iMsg, + WPARAM wParam, LPARAM lParam) +{ + auto self = (PluginTest*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + PAINTSTRUCT ps; + HDC hdc; + + // Switch according to what type of message we have received + + switch(iMsg) { + case WM_PAINT: + // We receive WM_PAINT every time window is updated + hdc = BeginPaint(hwnd, &ps); + TextOut(hdc, 10, n % 100, message, 13); + ++n; + Rectangle(hdc, self->mouseX, self->mouseY, + self->mouseX + 10, self->mouseY + 10); + EndPaint(hwnd, &ps); + break; + + case WM_MOUSEMOVE: + self->mouseX = (short)LOWORD(lParam); + self->mouseY = (short)HIWORD(lParam); + break; + } + + // Send any messages we don't handle to default window procedure + return DefWindowProc(hwnd, iMsg, wParam, lParam); +} +#endif // defined(WIN32) + +void PluginTest::createWindow(void *parent) +{ +#if defined(X11) + xDisplay = XOpenDisplay(nullptr); + + xScreen = DefaultScreen(xDisplay); + + xWindow = XCreateSimpleWindow(xDisplay, (Window)parent, 10, 10, 100, 100, 1, + BlackPixel(xDisplay, xScreen), + WhitePixel(xDisplay, xScreen)); + + XSelectInput(xDisplay, xWindow, + ExposureMask | KeyPressMask | PointerMotionMask); +#endif // defined(X11) + +#if defined(WIN32) + static char szAppName[] = "PluginTest"; + WNDCLASSEX wndclass = {}; + + hInstance = GetModuleHandle(nullptr); + + wndclass.cbSize = sizeof(wndclass); + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = PluginTest::wndProc; + wndclass.hInstance = hInstance; + wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wndclass.lpszClassName = szAppName; + wndclass.lpszMenuName = NULL; + wndclass.cbWndExtra = sizeof(PluginTest*); // Size of data. + + // Register a new window class with Windows + RegisterClassEx(&wndclass); + + HWND hWndParent = (HWND)parent; + + // Create a window based on our new class + hwnd = CreateWindow(szAppName, "PluginTest", + ((parent ? WS_CHILD : WS_OVERLAPPEDWINDOW) | WS_VISIBLE ), + 10, 10, 100, 100, + hWndParent, NULL, hInstance, NULL); + + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this); + +#endif // defined(WIN32) + + resizeWindow(100, 100); + onShowWindow(); +} + +void PluginTest::onDestroyWindow() +{ +#if defined(X11) + XDestroyWindow(xDisplay, xWindow); + XCloseDisplay(xDisplay); +#endif // defined(X11) + +#if defined(WIN32) + DestroyWindow(hwnd); +// UnregisterClass(m_className, hInstance); +#endif // defined(WIN32) +} + +void PluginTest::onShowWindow() +{ +#if defined(X11) + XMapWindow(xDisplay, xWindow); +#endif // defined(X11) + +#if defined(WIN32) + // Show and update our window + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); +#endif // defined(WIN32) +} + +void PluginTest::onHideWindow() +{ +#if defined(X11) + XUnmapWindow(xDisplay, xWindow); +#endif // defined(X11) + +#if defined(WIN32) + ShowWindow(hwnd, SW_HIDE); +#endif // defined(WIN32) + +} + +void PluginTest::onIdle() +{ +#if defined(X11) + XEvent xEvent; + while(XPending(xDisplay) > 0) + { + XNextEvent(xDisplay, &xEvent); + switch(xEvent.type) { + case Expose: + break; + + case MotionNotify: + mouseX = xEvent.xmotion.x; + mouseY = xEvent.xmotion.y; + break; + + default: + break; + } + } + + XClearWindow(xDisplay, xWindow); + XDrawRectangle(xDisplay, xWindow, DefaultGC(xDisplay, xScreen), + mouseX, mouseY, 10, 10); + XDrawString(xDisplay, xWindow, DefaultGC(xDisplay, xScreen), + 10, n % 100, message, strlen(message)); +#endif // defined(X11) + +#if defined(WIN32) + MSG msg; + + while(PeekMessage(&msg, hwnd, 0, 0, PM_NOREMOVE)) + { + GetMessage(&msg, hwnd, 0, 0); + TranslateMessage(&msg); // for certain keyboard messages + DispatchMessage(&msg); // send message to WndProc + } + + InvalidateRect(hwnd, 0, TRUE); +#endif // defined(WIN32) + + ++n; +} + +void PluginTest::resizeWindow(size_t width, size_t height) +{ +} + +void PluginTest::closeWindow() +{ +} diff --git a/plugintest.h b/plugintest.h new file mode 100644 index 0000000..2da5c73 --- /dev/null +++ b/plugintest.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * pluginlv2test.h + * + * Mon Feb 8 07:21:09 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. + */ +#pragma once + +#if defined(X11) +#include +#endif // defined(X11) + +#if defined(WIN32) +#define WIN32_LEAN_AND_MEAN +#include +#endif // defined(WIN32) + +#ifdef LV2 +#include +#endif + +#ifdef VST +#include +#endif + +class PluginTest +#ifdef LV2 + : public PluginLV2 +#endif +#ifdef VST + : public PluginVST +#endif +{ +public: +#ifdef VST + PluginTest(audioMasterCallback audioMaster); +#endif + + void onFreeWheelChange(bool freewheel) override; + + void onFramesizeChange(size_t framesize) override; + + void onSamplerateChange(float samplerate) override; + + void onActiveChange(bool active) override; + + std::string onStateSave() override; + void onStateRestore(const std::string& config) override; + + size_t getNumberOfMidiInputs() override; + size_t getNumberOfMidiOutputs() override; + size_t getNumberOfAudioInputs() override; + size_t getNumberOfAudioOutputs() override; + + std::string getId() override; + + void process(size_t pos, + const std::vector& input_events, + std::vector& output_events, + const std::vector& input_samples, + const std::vector& output_samples, + size_t count) override; + + // + // GUI + // + bool hasGUI() override; + void createWindow(void *parent) override; + void onDestroyWindow() override; + void onShowWindow() override; + void onHideWindow() override; + void onIdle() override; + void resizeWindow(size_t width, size_t height) override; + void closeWindow() override; + +private: +#if defined(X11) + Display* xDisplay{nullptr}; + Window xWindow{0}; + int xScreen{0}; +#endif // defined(X11) + +#if defined(WIN32) + HWND hwnd{0}; + HMODULE hInstance{0}; + + static LRESULT CALLBACK wndProc(HWND hwnd, UINT iMsg, + WPARAM wParam, LPARAM lParam); +#endif // defined(WIN32) + + int mouseX{0}; + int mouseY{0}; +}; diff --git a/pluginvst.cc b/pluginvst.cc new file mode 100644 index 0000000..700a19d --- /dev/null +++ b/pluginvst.cc @@ -0,0 +1,391 @@ +/* -*- 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(current_latency); + AudioEffectX::ioChanged(); + current_latency = update_latency; + } +} + +// +// VST AudioEffectX implementation: +// +PluginVST::PluginVST(audioMasterCallback audioMaster) + : AudioEffectX(audioMaster, 0, 0) +{ +} + +PluginVST::~PluginVST() +{ +} + +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; +} + +void PluginVST::open() +{ + // Called when plug-in is initialized. + + // 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); + + if(hasGUI()) + { + editor = std::make_shared(*this); + setEditor(editor.get()); + } +} + +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); +} + +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); +} + +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) +{ + // 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; +} diff --git a/pluginvst.h b/pluginvst.h new file mode 100644 index 0000000..8e7334e --- /dev/null +++ b/pluginvst.h @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * pluginvst.h + * + * Mon Feb 8 19:24:39 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. + */ +#pragma once + +#include + +#include + +#include +#include + +#if defined(WIN32) +#define WIN32_LEAN_AND_MEAN +#include +#endif // defined(WIN32) + +class PluginVST + : public Plugin + , public AudioEffectX +{ +public: + //! Get current free-wheel mode. + bool getFreeWheel() const override; + + //! This method is called by the host when the free-wheel mode changes. + virtual void onFreeWheelChange(bool freewheel) override = 0; + + + //! Call this to get current samplerate. + float getSamplerate() override; + + //! This method is called by the host when the free-wheel mode changes. + virtual void onSamplerateChange(float samplerate) override = 0; + + + //! Call this to get current frame-size. + std::size_t getFramesize() override; + + //! This method is called by the host when the frame-size changes. + virtual void onFramesizeChange(std::size_t framesize) override = 0; + + + //! Call this to get current active state + bool getActive() override; + + //! This method is called by the host when the active state changes. + virtual void onActiveChange(bool active) override = 0; + + + //! This method is called by the host to get the current state for storing. + virtual std::string onStateSave() override = 0; + + //! This method is called by the host when a new state has been loaded. + virtual void onStateRestore(const std::string& config) override = 0; + + + //! This is method is called by the host to get the current latency. + //! \param The latency in samples. + float getLatency() override; + + //! Call this method to signal a latency change to the host. + //! \param latency The latency in samples. + void setLatency(float latency) override; + + + //! Called by the the host to get the number of audio input channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfAudioInputs() override = 0; + + //! Called by the the host to get the number of audio output channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfAudioOutputs() override = 0; + + + //! Called by the the host to get the number of midi input channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfMidiInputs() override = 0; + + //! Called by the the host to get the number of midi output channels. + //! This must remain constant during the lifespan of the plugin instance. + virtual std::size_t getNumberOfMidiOutputs() override = 0; + + + //! Get unique plugin id. + std::string getId() override = 0; + + virtual void process(std::size_t pos, + const std::vector& input_events, + std::vector& output_events, + const std::vector& input_samples, + const std::vector& output_samples, + std::size_t count) = 0; + + // + // GUI + // + //! Return true if a GUI implementation is to be used. + virtual bool hasGUI() override + { + return false; + } + + //! Create new window. + virtual void createWindow(void *parent) override {} + + //! Destroy window. + virtual void onDestroyWindow() override {} + + //! Show window. + virtual void onShowWindow() override {} + + //! Hide window. + virtual void onHideWindow() override {} + + //! Called regularly by host; process ui events. + virtual void onIdle() override {} + + //! Signal new window size to host. + void resizeWindow(std::size_t width, std::size_t height) override; + + //! Signal close window event to the host. + void closeWindow() override; + +protected: + bool active{false}; + + void updateLatency(); + float current_latency{0.0f}; + float update_latency{0.0f}; + + bool free_wheel{true}; + + std::vector input_events; + + std::size_t pos{0}; + +public: + PluginVST(audioMasterCallback audioMaster); + virtual ~PluginVST(); + + // From AudioEffect: + void open() override; + void close() override; + void suspend() override; + void resume() override; + + // Callbacks: + void setSampleRate(float sampleRate) override; + void setBlockSize(VstInt32 blockSize) override; + + VstInt32 getChunk(void **data, bool isPreset) override; + VstInt32 setChunk(void *data, VstInt32 byteSize, bool isPreset) override; + + // From AudioEffectX: + VstInt32 canDo(char* text) override; + + void processReplacing(float** inputs, float** outputs, + VstInt32 sampleFrames) override; + VstInt32 processEvents(VstEvents *events) override; + + // UI + class UI : + public AEffEditor + { + public: + UI(PluginVST& plugin_vst); + + bool open(void* ptr) override; + void close() override; + bool isOpen() override; + void idle() override; + bool getRect(ERect** rect) override; + + PluginVST& plugin_vst; + bool is_open{false}; + + ERect rect{0,0,100,100}; + }; + +private: + std::shared_ptr editor; +}; + +AudioEffect* createEffectInstance(audioMasterCallback audioMaster); -- cgit v1.2.3