diff options
author | Bent Bisballe Nyeng <deva@aasimon.org> | 2016-03-11 12:28:44 +0100 |
---|---|---|
committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2016-03-11 12:28:44 +0100 |
commit | a27d71e88bd8f950e11e7a1a2ba0a4dd88f541f4 (patch) | |
tree | 688bbe1368d31bb7a67f29795a040c1102c34a6e |
Initial import from DrumGizmo branch.
-rw-r--r-- | COPYING | 165 | ||||
-rw-r--r-- | Makefile | 64 | ||||
-rwxr-xr-x | add_file | 64 | ||||
-rw-r--r-- | manifest.ttl | 110 | ||||
-rw-r--r-- | midievent.cc | 71 | ||||
-rw-r--r-- | midievent.h | 56 | ||||
-rw-r--r-- | plugin.h | 145 | ||||
-rw-r--r-- | pluginlv2.cc | 637 | ||||
-rw-r--r-- | pluginlv2.h | 225 | ||||
-rw-r--r-- | plugintest.cc | 334 | ||||
-rw-r--r-- | plugintest.h | 113 | ||||
-rw-r--r-- | pluginvst.cc | 391 | ||||
-rw-r--r-- | pluginvst.h | 208 |
13 files changed, 2583 insertions, 0 deletions
@@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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 <deva@aasimon.org> +# +# 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: <http://usefulinc.com/ns/doap#> . +@prefix foaf: <http://xmlns.com/foaf/0.1/> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix atom: <http://lv2plug.in/ns/ext/atom#> . +@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . +@prefix state: <http://lv2plug.in/ns/ext/state#> . +@prefix pprops: <http://lv2plug.in/ns/ext/port-props#> . + +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +<http://example.org/plugintest#ui> + a ui:X11UI ; + lv2:requiredFeature ui:resize ; + lv2:extensionData ui:resize ; + lv2:requiredFeature ui:idleInterface ; + lv2:extensionData ui:idleInterface ; + ui:binary <plugintest_lv2.so> . + +<http://example.org/plugintest> + a lv2:Plugin ; + lv2:binary <plugintest_lv2.so> ; + a lv2:InstrumentPlugin ; + doap:name "LV2Test" ; + ui:ui <http://example.org/plugintest#ui> ; + doap:maintainer [ + foaf:name "LV2Test" ; + foaf:homepage <http://www.example.org/plugintest> ; + ] ; + doap:license <http://usefulinc.com/doap/licenses/gpl> ; + doap:license <http://opensource.org/licenses/gpl-3.0> ; + lv2:optionalFeature <http://lv2plug.in/ns/ext/uri-map> ; + lv2:optionalFeature <http://lv2plug.in/ns/ext/event> ; + 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 <http://lv2plug.in/ns/lv2core#freeWheeling> ; + lv2:portProperty lv2:toggled ; + lv2:portProperty pprops:hasStrictBounds; + ] , [ + a lv2:OutputPort, lv2:ControlPort ; + lv2:designation <http://lv2plug.in/ns/lv2core#latency>; + 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 <http://lv2plug.in/ns/ext/midi#MidiEvent> ; + lv2:index 2 ; + lv2:symbol "control" ; + lv2:name "Control" + ] , [ + a atom:AtomPort , + lv2:OutputPort; + atom:bufferType atom:Sequence ; + atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ; + 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 <iostream> + +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 <stdint.h> +#include <stdio.h> + +#include <vector> + +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<char> 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 <vector> +#include <string> + +#include <cstdlib> + +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<MidiEvent>& input_events, + std::vector<MidiEvent>& output_events, + const std::vector<const float*>& input_samples, + const std::vector<float*>& 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 <iostream> +#include <assert.h> + +#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 <lv2/lv2plug.in/ns/ext/midi/midi.h> +#include <lv2/lv2plug.in/ns/ext/instance-access/instance-access.h> + +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<uint32_t>(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<uint32_t>(LV2Ports::Latency)) + { + plugin_lv2->latency_port = (float*)data_location; + } + + uint32_t port_offset = static_cast<uint32_t>(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<MidiEvent> 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<MidiEvent> 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: <http://lv2plug.in/ns/lv2core#> .\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 <plugin.h> + +#include <lv2/lv2plug.in/ns/lv2core/lv2.h> +#include <lv2/lv2plug.in/ns/ext/urid/urid.h> +#include <lv2/lv2plug.in/ns/ext/state/state.h> +#include <lv2/lv2plug.in/ns/ext/atom/atom.h> +#include <lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h> +#include <lv2/lv2plug.in/ns/extensions/ui/ui.h> + +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<MidiEvent>& input_events, + std::vector<MidiEvent>& output_events, + const std::vector<const float*>& input_samples, + const std::vector<float*>& 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<LV2_Atom_Sequence*> input_event_ports; + std::vector<LV2_Atom_Sequence*> output_event_ports; + std::vector<const float*> input_audio_ports; + std::vector<float*> 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 <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <iostream> + +#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<MidiEvent>& input_events, + std::vector<MidiEvent>& output_events, + const std::vector<const float*>& input_samples, + const std::vector<float*>& 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 <X11/Xlib.h> +#endif // defined(X11) + +#if defined(WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif // defined(WIN32) + +#ifdef LV2 +#include <pluginlv2.h> +#endif + +#ifdef VST +#include <pluginvst.h> +#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<MidiEvent>& input_events, + std::vector<MidiEvent>& output_events, + const std::vector<const float*>& input_samples, + const std::vector<float*>& 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 <public.sdk/source/vst2.x/audioeffectx.h> +#include <public.sdk/source/vst2.x/aeffeditor.h> + +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<UI>(*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<const float*> input_audio_ports; + for(std::size_t i = 0; i < getNumberOfAudioInputs(); ++i) + { + input_audio_ports.emplace_back(inputs[i]); + } + + std::vector<float*> 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<MidiEvent> 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<VstMidiEvent> 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 <memory> + +#include <plugin.h> + +#include <public.sdk/source/vst2.x/audioeffectx.h> +#include <public.sdk/source/vst2.x/aeffeditor.h> + +#if defined(WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#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<MidiEvent>& input_events, + std::vector<MidiEvent>& output_events, + const std::vector<const float*>& input_samples, + const std::vector<float*>& 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<MidiEvent> 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<UI> editor; +}; + +AudioEffect* createEffectInstance(audioMasterCallback audioMaster); |