/* * Lv2Proc.h - Lv2 processor class * * Copyright (c) 2019-2020 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program (see COPYING); if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef LV2PROC_H #define LV2PROC_H #include "lmmsconfig.h" #ifdef LMMS_HAVE_LV2 #include #include #include #include "Lv2Basics.h" #include "Lv2Features.h" #include "LinkedModelGroups.h" #include "MidiEvent.h" #include "MidiTime.h" #include "Plugin.h" #include "PluginIssue.h" #include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" // forward declare port structs/enums namespace Lv2Ports { struct Audio; struct PortBase; struct AtomSeq; enum class Type; enum class Flow; enum class Vis; } //! Class representing one Lv2 processor, i.e. one Lv2 handle //! For Mono effects, 1 Lv2ControlBase references 2 Lv2Proc class Lv2Proc : public LinkedModelGroup { public: static Plugin::PluginTypes check(const LilvPlugin* plugin, std::vector &issues, bool printIssues = false); /* ctor/dtor */ Lv2Proc(const LilvPlugin* plugin, Model *parent); ~Lv2Proc() override; //! Must be checked after ctor or reload bool isValid() const { return m_valid; } /* port access */ struct StereoPortRef { //! mono port or left port in case of stereo Lv2Ports::Audio* m_left = nullptr; //! unused, or right port in case of stereo Lv2Ports::Audio* m_right = nullptr; }; StereoPortRef& inPorts() { return m_inPorts; } const StereoPortRef& inPorts() const { return m_inPorts; } StereoPortRef& outPorts() { return m_outPorts; } const StereoPortRef& outPorts() const { return m_outPorts; } template void foreach_port(const Functor& ftor) { for (std::unique_ptr& port : m_ports) { ftor(port.get()); } } template void foreach_port(const Functor& ftor) const { for (const std::unique_ptr& port : m_ports) { ftor(port.get()); } } //! Debug function to print ports to stdout void dumpPorts(); /* utils for the run thread */ //! Copy values from the LMMS core (connected models, MIDI events, ...) into //! the respective ports void copyModelsFromCore(); //! Bring values from all ports to the LMMS core void copyModelsToCore(); /** * Copy buffer passed by the core into our ports * @param buf buffer of sample frames, each sample frame is something like * a `float[ * ]` array. * @param firstChan The offset for @p buf where we have to read our * first channel. * This marks the first sample in each sample frame where we read from. * If we are the 2nd of 2 mono procs, this can be greater than 0. * @param num Number of channels we must read from @param buf (starting at * @p offset) */ void copyBuffersFromCore(const sampleFrame *buf, unsigned firstChan, unsigned num, fpp_t frames); /** * Copy our ports into buffers passed by the core * @param buf buffer of sample frames, each sample frame is something like * a `float[ * ]` array. * @param firstChan The offset for @p buf where we have to write our * first channel. * This marks the first sample in each sample frame where we write to. * If we are the 2nd of 2 mono procs, this can be greater than 0. * @param num Number of channels we must write to @param buf (starting at * @p offset) */ void copyBuffersToCore(sampleFrame *buf, unsigned firstChan, unsigned num, fpp_t frames) const; //! Run the Lv2 plugin instance for @param frames frames void run(fpp_t frames); void handleMidiInputEvent(const class MidiEvent &event, const MidiTime &time, f_cnt_t offset); /* misc */ class AutomatableModel *modelAtPort(const QString &uri); // unused currently std::size_t controlCount() const { return LinkedModelGroup::modelNum(); } bool hasNoteInput() const; protected: /* load and save */ //! Create ports and instance, connect ports, activate plugin void initPlugin(); //! Deactivate instance void shutdownPlugin(); private: bool m_valid = true; const LilvPlugin* m_plugin; LilvInstance* m_instance; Lv2Features m_features; // full list of ports std::vector> m_ports; // quick reference to specific, unique ports StereoPortRef m_inPorts, m_outPorts; Lv2Ports::AtomSeq *m_midiIn = nullptr, *m_midiOut = nullptr; // MIDI // many things here may be moved into the `Instrument` class constexpr const static std::size_t m_maxMidiInputEvents = 1024; //! spinlock for the MIDI ringbuffer (for MIDI events going to the plugin) std::atomic_flag m_ringLock = ATOMIC_FLAG_INIT; //! MIDI ringbuffer (for MIDI events going to the plugin) ringbuffer_t m_midiInputBuf; //! MIDI ringbuffer reader ringbuffer_reader_t m_midiInputReader; // other static std::size_t minimumEvbufSize() { return 1 << 15; /* ardour uses this*/ } //! models for the controls, sorted by port symbols std::map m_connectedModels; void initPluginSpecificFeatures(); //! load a file in the plugin, but don't do anything in LMMS void loadFileInternal(const QString &file); //! allocate m_ports, fill all with metadata, and assign meaning of ports void createPorts(); //! fill m_ports[portNum] with metadata void createPort(std::size_t portNum); //! connect m_ports[portNum] with Lv2 void connectPort(std::size_t num); void dumpPort(std::size_t num); static bool portIsSideChain(const LilvPlugin* plugin, const LilvPort *port); static bool portIsOptional(const LilvPlugin* plugin, const LilvPort *port); static AutoLilvNode uri(const char* uriStr); }; #endif // LMMS_HAVE_LV2 #endif // LV2PROC_H