zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit c8ee998c2b3db32716837f73cbbdbac21e5fec27
parent 6dac8b077731e44360e8966cd312587d4d67f480
Author: falkTX <falktx@gmail.com>
Date:   Tue,  5 Jan 2016 02:33:41 +0000

Initial (working) ZynAddSubFX LV2/VST plugin

Diffstat:
Msrc/Plugin/CMakeLists.txt | 1+
Asrc/Plugin/ZynAddSubFX/CMakeLists.txt | 31+++++++++++++++++++++++++++++++
Asrc/Plugin/ZynAddSubFX/DistrhoPluginInfo.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/Plugin/ZynAddSubFX/ZynAddSubFX.cpp | 477+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 548 insertions(+), 0 deletions(-)

diff --git a/src/Plugin/CMakeLists.txt b/src/Plugin/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(DynamicFilter) add_subdirectory(Echo) add_subdirectory(Phaser) add_subdirectory(Reverb) +add_subdirectory(ZynAddSubFX) diff --git a/src/Plugin/ZynAddSubFX/CMakeLists.txt b/src/Plugin/ZynAddSubFX/CMakeLists.txt @@ -0,0 +1,31 @@ + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/DPF/distrho .) + +add_library(ZynAddSubFX_lv2 SHARED ${CMAKE_SOURCE_DIR}/src/globals.cpp ${CMAKE_SOURCE_DIR}/src/UI/ConnectionDummy.cpp ${CMAKE_SOURCE_DIR}/DPF/distrho/DistrhoPluginMain.cpp ZynAddSubFX.cpp) +add_library(ZynAddSubFX_vst SHARED ${CMAKE_SOURCE_DIR}/src/globals.cpp ${CMAKE_SOURCE_DIR}/src/UI/ConnectionDummy.cpp ${CMAKE_SOURCE_DIR}/DPF/distrho/DistrhoPluginMain.cpp ZynAddSubFX.cpp) + +set_target_properties(ZynAddSubFX_lv2 PROPERTIES COMPILE_DEFINITIONS "DISTRHO_PLUGIN_TARGET_LV2") +set_target_properties(ZynAddSubFX_lv2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "lv2") +set_target_properties(ZynAddSubFX_lv2 PROPERTIES OUTPUT_NAME "ZynAddSubFX") +set_target_properties(ZynAddSubFX_lv2 PROPERTIES PREFIX "") + +set_target_properties(ZynAddSubFX_vst PROPERTIES COMPILE_DEFINITIONS "DISTRHO_PLUGIN_TARGET_VST") +set_target_properties(ZynAddSubFX_vst PROPERTIES LIBRARY_OUTPUT_DIRECTORY "vst") +set_target_properties(ZynAddSubFX_vst PROPERTIES OUTPUT_NAME "ZynAddSubFX") +set_target_properties(ZynAddSubFX_vst PROPERTIES PREFIX "") + +target_link_libraries(ZynAddSubFX_lv2 zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES}) +target_link_libraries(ZynAddSubFX_vst zynaddsubfx_core ${OS_LIBRARIES} ${LIBLO_LIBRARIES}) + +install(TARGETS ZynAddSubFX_lv2 LIBRARY DESTINATION lib/lv2/ZynAddSubFX.lv2/) +install(TARGETS ZynAddSubFX_vst LIBRARY DESTINATION lib/vst/) + +add_custom_command(TARGET ZynAddSubFX_lv2 POST_BUILD + COMMAND lv2-ttl-generator ./ZynAddSubFX.so + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lv2) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/lv2/manifest.ttl + ${CMAKE_CURRENT_BINARY_DIR}/lv2/presets.ttl + ${CMAKE_CURRENT_BINARY_DIR}/lv2/ZynAddSubFX.ttl + DESTINATION lib/lv2/ZynAddSubFX.lv2/) diff --git a/src/Plugin/ZynAddSubFX/DistrhoPluginInfo.h b/src/Plugin/ZynAddSubFX/DistrhoPluginInfo.h @@ -0,0 +1,39 @@ +/* + ZynAddSubFX - a software synthesizer + + DistrhoPluginInfo.h - DPF information header + Copyright (C) 2015 Filipe Coelho + Author: Filipe Coelho + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + 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 (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED +#define DISTRHO_PLUGIN_INFO_H_INCLUDED + +#define DISTRHO_PLUGIN_BRAND "ZynAddSubFX" +#define DISTRHO_PLUGIN_NAME "ZynAddSubFX" +#define DISTRHO_PLUGIN_URI "http://zynaddsubfx.sourceforge.net" + +#define DISTRHO_PLUGIN_HAS_UI 0 +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 +#define DISTRHO_PLUGIN_IS_SYNTH 1 +#define DISTRHO_PLUGIN_NUM_INPUTS 0 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 +#define DISTRHO_PLUGIN_WANT_PROGRAMS 1 +#define DISTRHO_PLUGIN_WANT_STATE 1 +#define DISTRHO_PLUGIN_WANT_FULL_STATE 1 + +#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/src/Plugin/ZynAddSubFX/ZynAddSubFX.cpp b/src/Plugin/ZynAddSubFX/ZynAddSubFX.cpp @@ -0,0 +1,477 @@ +/* + ZynAddSubFX - a software synthesizer + + ZynAddSubFX.cpp - DPF + ZynAddSubFX Plugin + Copyright (C) 2015-2016 Filipe Coelho + Author: Filipe Coelho + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License + as published by the Free Software Foundation. + + 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 (version 2 or later) for more details. + + You should have received a copy of the GNU General Public License (version 2) + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +// DPF includes +#include "DistrhoPlugin.hpp" + +// ZynAddSubFX includes +#include "Misc/Master.h" +#include "Misc/MiddleWare.h" +#include "Misc/Part.h" +#include "Misc/Util.h" + +// Extra includes +#include "extra/Mutex.hpp" +#include "extra/Thread.hpp" + +/* ------------------------------------------------------------------------------------------------------------ + * ZynAddSubFX plugin class */ + +class ZynAddSubFX : public Plugin, + private Thread +{ +public: + ZynAddSubFX() + : Plugin(0, 1, 1), // 0 parameters, 1 program, 1 state + Thread("ZynMwTick"), + master(nullptr), + middleware(nullptr), + active(false), + defaultState(nullptr) + { + config.init(); + + synth.buffersize = static_cast<int>(getBufferSize()); + synth.samplerate = static_cast<uint>(getSampleRate()); + + if (synth.buffersize > 32) + synth.buffersize = 32; + + synth.alias(); + + _initMaster(); + + defaultState = _getState(); + } + + ~ZynAddSubFX() override + { + _deleteMaster(); + std::free(defaultState); + } + +protected: + /* -------------------------------------------------------------------------------------------------------- + * Information */ + + /** + Get the plugin label. + This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. + */ + const char* getLabel() const noexcept override + { + return "ZynAddSubFX"; + } + + /** + Get an extensive comment/description about the plugin. + */ + const char* getDescription() const noexcept override + { + // TODO + return ""; + } + + /** + Get the plugin author/maker. + */ + const char* getMaker() const noexcept override + { + return "ZynAddSubFX Team"; + } + + /** + Get the plugin homepage. + Optional, returns nothing by default. + */ + const char* getHomePage() const noexcept override + { + return "http://zynaddsubfx.sourceforge.net"; + } + + /** + Get the plugin license (a single line of text or a URL). + */ + const char* getLicense() const noexcept override + { + return "GPL v2"; + } + + /** + Get the plugin version, in hexadecimal. + */ + uint32_t getVersion() const noexcept override + { + // TODO: use config.h or globals.h + return d_version(2, 5, 3); + } + + /** + Get the plugin unique Id. + This value is used by LADSPA, DSSI and VST plugin formats. + */ + int64_t getUniqueId() const noexcept override + { + return d_cconst('Z', 'A', 'S', 'F'); + } + + /* -------------------------------------------------------------------------------------------------------- + * Parameters, empty for now */ + + void initParameter(uint32_t, Parameter&) noexcept override {} + float getParameterValue(uint32_t) const noexcept override { return 0.0f; } + void setParameterValue(uint32_t index, float value) noexcept override {} + + /* -------------------------------------------------------------------------------------------------------- + * Programs */ + + /** + Set the name of the program @a index. + This function will be called once, shortly after the plugin is created. + */ + void initProgramName(uint32_t index, String& programName) override + { + programName = "Default"; + } + + /** + Load a program. + The host may call this function from any context, including realtime processing. + */ + void loadProgram(uint32_t index) override + { + setState(nullptr, defaultState); + } + + /* -------------------------------------------------------------------------------------------------------- + * States */ + + /** + Set the state key and default value of @a index. + This function will be called once, shortly after the plugin is created. + */ + void initState(uint32_t, String& stateKey, String& defaultStateValue) override + { + stateKey = "state"; + defaultStateValue = defaultState; + } + + /** + Get the value of an internal state. + The host may call this function from any non-realtime context. + */ + String getState(const char*) const override + { + return String(_getState(), false); + } + + /** + Change an internal state @a key to @a value. + */ + void setState(const char* key, const char* value) override + { + const MutexLocker cml(mutex); + + master->putalldata(value); + master->applyparameters(); + master->initialize_rt(); + + middleware->updateResources(master); + } + + /* -------------------------------------------------------------------------------------------------------- + * Audio/MIDI Processing */ + + /** + Activate this plugin. + */ + void activate() noexcept override + { + active = true; + } + + /** + Deactivate this plugin. + */ + void deactivate() noexcept override + { + active = false; + } + + /** + Run/process function for plugins with MIDI input. + @note Some parameters might be null if there are no audio inputs/outputs or MIDI events. + */ + void run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) override + { + if (! mutex.tryLock()) + { + //if (! isOffline()) + { + std::memset(outputs[0], 0, frames); + std::memset(outputs[1], 0, frames); + return; + } + + mutex.lock(); + } + + uint32_t framesOffset = 0; + + for (uint32_t i=0; i<midiEventCount; ++i) + { + const MidiEvent& midiEvent(midiEvents[i]); + + if (midiEvent.frame >= frames) + continue; + if (midiEvent.size > MidiEvent::kDataSize) + continue; + if (midiEvent.data[0] < 0x80 || midiEvent.data[0] >= 0xF0) + continue; + + if (midiEvent.frame > framesOffset) + { + master->GetAudioOutSamples(midiEvent.frame-framesOffset, synth.samplerate, outputs[0]+framesOffset, + outputs[1]+framesOffset); + framesOffset = midiEvent.frame; + } + + const uint8_t status = midiEvent.data[0] & 0xF0; + const char channel = midiEvent.data[0] & 0x0F; + + switch (status) + { + case 0x80: { + const char note = static_cast<char>(midiEvent.data[1]); + + master->noteOff(channel, note); + } break; + + case 0x90: { + const char note = static_cast<char>(midiEvent.data[1]); + const char velo = static_cast<char>(midiEvent.data[2]); + + master->noteOn(channel, note, velo); + } break; + + case 0xA0: { + const char note = static_cast<char>(midiEvent.data[1]); + const char pressure = static_cast<char>(midiEvent.data[2]); + + master->polyphonicAftertouch(channel, note, pressure); + } break; + + case 0xB0: { + const int control = midiEvent.data[1]; + const int value = midiEvent.data[2]; + + // skip controls which we map to parameters + //if (getIndexFromZynControl(midiEvent.data[1]) != kParamCount) + // continue; + + master->setController(channel, control, value); + } break; + + case 0xE0: { + const uint8_t lsb = midiEvent.data[1]; + const uint8_t msb = midiEvent.data[2]; + const int value = ((msb << 7) | lsb) - 8192; + + master->setController(channel, C_pitchwheel, value); + } break; + } + } + + if (frames > framesOffset) + master->GetAudioOutSamples(frames-framesOffset, synth.samplerate, outputs[0]+framesOffset, + outputs[1]+framesOffset); + + mutex.unlock(); + } + + /* -------------------------------------------------------------------------------------------------------- + * Callbacks */ + + /** + Callback to inform the plugin about a buffer size change. + This function will only be called when the plugin is deactivated. + @note This value is only a hint! + Hosts might call run() with a higher or lower number of frames. + */ + void bufferSizeChanged(uint32_t newBufferSize) override + { + char* const state(_getState()); + + _deleteMaster(); + + synth.buffersize = static_cast<int>(newBufferSize); + + if (synth.buffersize > 32) + synth.buffersize = 32; + + synth.alias(); + + _initMaster(); + + setState(nullptr, state); + std::free(state); + } + + /** + Callback to inform the plugin about a sample rate change. + This function will only be called when the plugin is deactivated. + */ + void sampleRateChanged(double newSampleRate) override + { + char* const state(_getState()); + + _deleteMaster(); + + synth.samplerate = static_cast<uint>(newSampleRate); + synth.alias(); + + _initMaster(); + + setState(nullptr, state); + std::free(state); + } + +private: + Config config; + Master* master; + MiddleWare* middleware; + SYNTH_T synth; + + bool active; + Mutex mutex; + char* defaultState; + + char* _getState() const + { + char* data = nullptr; + + if (active) + { + middleware->doReadOnlyOp([this, &data]{ + master->getalldata(&data); + }); + } + else + { + master->getalldata(&data); + } + + return data; + } + + void _initMaster() + { + middleware = new MiddleWare(std::move(synth), &config); + middleware->setUiCallback(__uiCallback, this); + middleware->setIdleCallback(__idleCallback, this); + _masterChangedCallback(middleware->spawnMaster()); + startThread(); + } + + void _deleteMaster() + { + stopThread(1000); + + master = nullptr; + delete middleware; + middleware = nullptr; + } + + void _masterChangedCallback(Master* m) + { + master = m; + master->setMasterChangedCallback(__masterChangedCallback, this); + } + + static void __masterChangedCallback(void* ptr, Master* m) + { + ((ZynAddSubFX*)ptr)->_masterChangedCallback(m); + } + + void _uiCallback(const char* const) + { + } + + static void __uiCallback(void* ptr, const char* msg) + { + ((ZynAddSubFX*)ptr)->_uiCallback(msg); + } + + void _idleCallback() + { + // this can be used to give host idle time + // for now LV2 doesn't support this, and only some VST hosts support it + } + + static void __idleCallback(void* ptr) + { + ((ZynAddSubFX*)ptr)->_idleCallback(); + } + + void run() noexcept override + { + for (; ! shouldThreadExit();) + { + try { + middleware->tick(); + } catch(...) {} + + d_msleep(1); + } + } + + DISTRHO_DECLARE_NON_COPY_CLASS(ZynAddSubFX) +}; + +/* ------------------------------------------------------------------------------------------------------------ + * Create plugin, entry point */ + +START_NAMESPACE_DISTRHO + +Plugin* createPlugin() +{ + return new ZynAddSubFX(); +} + +END_NAMESPACE_DISTRHO + +/* ------------------------------------------------------------------------------------------------------------ + * Dummy variables and functions for linking purposes */ + +class WavFile; +namespace Nio { + void masterSwap(Master*){} + bool setSource(std::string){return true;} + bool setSink(std::string){return true;} + std::set<std::string> getSources(void){return std::set<std::string>();} + std::set<std::string> getSinks(void){return std::set<std::string>();} + std::string getSource(void){return "";} + std::string getSink(void){return "";} + void waveNew(WavFile*){} + void waveStart(){} + void waveStop(){} +}