@@ -1,12 +0,0 @@ | |||
/* (Auto-generated binary data file). */ | |||
#ifndef BINARY_DPF_RESOURCES_HPP_INCLUDED | |||
#define BINARY_DPF_RESOURCES_HPP_INCLUDED | |||
namespace dpf_resources | |||
{ | |||
extern const char* dejavusans_ttf; | |||
const unsigned int dejavusans_ttf_size = 741536; | |||
}; | |||
#endif |
@@ -1317,16 +1317,6 @@ bool Window::handlePluginSpecial(const bool press, const Key key) | |||
return pData->handlePluginSpecial(press, key); | |||
} | |||
bool Window::handlePluginKeyboard(const bool press, const uint key) | |||
{ | |||
return pData->handlePluginKeyboard(press, key); | |||
} | |||
bool Window::handlePluginSpecial(const bool press, const Key key) | |||
{ | |||
return pData->handlePluginSpecial(press, key); | |||
} | |||
// ----------------------------------------------------------------------- | |||
StandaloneWindow::StandaloneWindow() | |||
@@ -1,97 +0,0 @@ | |||
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. | |||
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) | |||
Bitstream Vera Fonts Copyright | |||
------------------------------ | |||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is | |||
a trademark of Bitstream, Inc. | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of the fonts accompanying this license ("Fonts") and associated | |||
documentation files (the "Font Software"), to reproduce and distribute the | |||
Font Software, including without limitation the rights to use, copy, merge, | |||
publish, distribute, and/or sell copies of the Font Software, and to permit | |||
persons to whom the Font Software is furnished to do so, subject to the | |||
following conditions: | |||
The above copyright and trademark notices and this permission notice shall | |||
be included in all copies of one or more of the Font Software typefaces. | |||
The Font Software may be modified, altered, or added to, and in particular | |||
the designs of glyphs or characters in the Fonts may be modified and | |||
additional glyphs or characters may be added to the Fonts, only if the fonts | |||
are renamed to names not containing either the words "Bitstream" or the word | |||
"Vera". | |||
This License becomes null and void to the extent applicable to Fonts or Font | |||
Software that has been modified and is distributed under the "Bitstream | |||
Vera" names. | |||
The Font Software may be sold as part of a larger software package but no | |||
copy of one or more of the Font Software typefaces may be sold by itself. | |||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, | |||
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME | |||
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING | |||
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, | |||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | |||
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE | |||
FONT SOFTWARE. | |||
Except as contained in this notice, the names of Gnome, the Gnome | |||
Foundation, and Bitstream Inc., shall not be used in advertising or | |||
otherwise to promote the sale, use or other dealings in this Font Software | |||
without prior written authorization from the Gnome Foundation or Bitstream | |||
Inc., respectively. For further information, contact: fonts at gnome dot | |||
org. | |||
Arev Fonts Copyright | |||
------------------------------ | |||
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. | |||
Permission is hereby granted, free of charge, to any person obtaining | |||
a copy of the fonts accompanying this license ("Fonts") and | |||
associated documentation files (the "Font Software"), to reproduce | |||
and distribute the modifications to the Bitstream Vera Font Software, | |||
including without limitation the rights to use, copy, merge, publish, | |||
distribute, and/or sell copies of the Font Software, and to permit | |||
persons to whom the Font Software is furnished to do so, subject to | |||
the following conditions: | |||
The above copyright and trademark notices and this permission notice | |||
shall be included in all copies of one or more of the Font Software | |||
typefaces. | |||
The Font Software may be modified, altered, or added to, and in | |||
particular the designs of glyphs or characters in the Fonts may be | |||
modified and additional glyphs or characters may be added to the | |||
Fonts, only if the fonts are renamed to names not containing either | |||
the words "Tavmjong Bah" or the word "Arev". | |||
This License becomes null and void to the extent applicable to Fonts | |||
or Font Software that has been modified and is distributed under the | |||
"Tavmjong Bah Arev" names. | |||
The Font Software may be sold as part of a larger software package but | |||
no copy of one or more of the Font Software typefaces may be sold by | |||
itself. | |||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT | |||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL | |||
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL | |||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM | |||
OTHER DEALINGS IN THE FONT SOFTWARE. | |||
Except as contained in this notice, the name of Tavmjong Bah shall not | |||
be used in advertising or otherwise to promote the sale, use or other | |||
dealings in this Font Software without prior written authorization | |||
from Tavmjong Bah. For further information, contact: tavmjong @ free | |||
. fr. |
@@ -1,175 +0,0 @@ | |||
/* libSOFD - Simple Open File Dialog [for X11 without toolkit] | |||
* | |||
* Copyright (C) 2014 Robin Gareus <robin@gareus.org> | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#ifndef LIBSOFD_H | |||
#define LIBSOFD_H | |||
#include <X11/Xlib.h> | |||
/////////////////////////////////////////////////////////////////////////////// | |||
/* public API */ | |||
/** open a file select dialog | |||
* @param dpy X Display connection | |||
* @param parent (optional) if not NULL, become transient for given window | |||
* @param x if >0 set explict initial width of the window | |||
* @param y if >0 set explict initial height of the window | |||
* @return 0 on success | |||
*/ | |||
int x_fib_show (Display *dpy, Window parent, int x, int y); | |||
/** force close the dialog. | |||
* This is normally not needed, the dialog closes itself | |||
* when a file is selected or the user cancels selection. | |||
* @param dpy X Display connection | |||
*/ | |||
void x_fib_close (Display *dpy); | |||
/** non-blocking X11 event handler. | |||
* It is safe to run this function even if the dialog is | |||
* closed or was not initialized. | |||
* | |||
* @param dpy X Display connection | |||
* @param event the XEvent to process | |||
* @return status | |||
* 0: the event was not for this window, or file-dialog still | |||
* active, or the dialog window is not displayed. | |||
* >0: file was selected, dialog closed | |||
* <0: file selection was cancelled. | |||
*/ | |||
int x_fib_handle_events (Display *dpy, XEvent *event); | |||
/** last status of the dialog | |||
* @return >0: file was selected, <0: canceled or inactive. 0: active | |||
*/ | |||
int x_fib_status (); | |||
/** query the selected filename | |||
* @return NULL if none set, or allocated string to be free()ed by the called | |||
*/ | |||
char *x_fib_filename (); | |||
/** customize/configure the dialog before calling \ref x_fib_show | |||
* changes only have any effect if the dialog is not visible. | |||
* @param k key to change | |||
* 0: set current dir to display (must end with slash) | |||
* 1: set title of dialog window | |||
* 2: specify a custom X11 font to use | |||
* 3: specify a custom 'places' file to include | |||
* (following gtk-bookmark convention) | |||
* @param v value | |||
* @return 0 on success. | |||
*/ | |||
int x_fib_configure (int k, const char *v); | |||
/** customize/configure the dialog before calling \ref x_fib_show | |||
* changes only have any effect if the dialog is not visible. | |||
* | |||
* @param k button to change: | |||
* 1: show hidden files | |||
* 2: show places | |||
* 3: show filter/list all (automatically hidden if there is no | |||
* filter function) | |||
* @param v <0 to hide the button >=0 show button, | |||
* 0: set button-state to not-checked | |||
* 1: set button-state to checked | |||
* >1: retain current state | |||
* @return 0 on success. | |||
*/ | |||
int x_fib_cfg_buttons (int k, int v); | |||
/** set custom callback to filter file-names. | |||
* NULL will disable the filter and hide the 'show all' button. | |||
* changes only have any effect if the dialog is not visible. | |||
* | |||
* @param cb callback function to check file | |||
* the callback function is called with the file name (basename only) | |||
* and is expected to return 1 if the file passes the filter | |||
* and 0 if the file should not be listed by default. | |||
* @return 0 on success. | |||
*/ | |||
int x_fib_cfg_filter_callback (int (*cb)(const char*)); | |||
/* 'recently used' API. x-platform | |||
* NOTE: all functions use a static cache and are not reentrant. | |||
* It is expected that none of these functions are called in | |||
* parallel from different threads. | |||
*/ | |||
/** release static resources of 'recently used files' | |||
*/ | |||
void x_fib_free_recent (); | |||
/** add an entry to the recently used list | |||
* | |||
* The dialog does not add files automatically on open, | |||
* if the application succeeds to open a selected file, | |||
* this function should be called. | |||
* | |||
* @param path complete path to file | |||
* @param atime time of last use, 0: NOW | |||
* @return -1 on error, number of current entries otherwise | |||
*/ | |||
int x_fib_add_recent (const char *path, time_t atime); | |||
/** get a platform specific path to a good location for | |||
* saving the recently used file list. | |||
* (follows XDG_DATA_HOME on Unix, and CSIDL_LOCAL_APPDATA spec) | |||
* | |||
* @param application-name to use to include in file | |||
* @return pointer to static path or NULL | |||
*/ | |||
const char *x_fib_recent_file(const char *appname); | |||
/** save the current list of recently used files to the given filename | |||
* (the format is one file per line, filename URL encoded and space separated | |||
* with last-used timestamp) | |||
* | |||
* This function tries to creates the containing directory if it does | |||
* not exist. | |||
* | |||
* @param fn file to save the list to | |||
* @return 0: on success | |||
*/ | |||
int x_fib_save_recent (const char *fn); | |||
/** load a recently used file list. | |||
* | |||
* @param fn file to load the list from | |||
* @return 0: on success | |||
*/ | |||
int x_fib_load_recent (const char *fn); | |||
/** get number of entries in the current list | |||
* @return number of entries in the recently used list | |||
*/ | |||
unsigned int x_fib_recent_count (); | |||
/** get recently used entry at given position | |||
* | |||
* @param i entry to query | |||
* @return pointer to static string | |||
*/ | |||
const char *x_fib_recent_at (unsigned int i); | |||
#endif // LIBSOFD_H |
@@ -1,593 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program 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. | |||
* | |||
* 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 Lesser General Public License for more details. | |||
* | |||
* For a full copy of the license see the LGPL.txt file | |||
*/ | |||
#include "DistrhoPluginInternal.hpp" | |||
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI | |||
# undef DISTRHO_PLUGIN_HAS_UI | |||
# define DISTRHO_PLUGIN_HAS_UI 0 | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
# include "DistrhoUIInternal.hpp" | |||
#else | |||
# include "../extra/Sleep.hpp" | |||
#endif | |||
#include "jack/jack.h" | |||
#include "jack/midiport.h" | |||
#include "jack/transport.h" | |||
#ifndef DISTRHO_OS_WINDOWS | |||
# include <signal.h> | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
START_NAMESPACE_DISTRHO | |||
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE | |||
static const setStateFunc setStateCallback = nullptr; | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
static volatile bool gCloseSignalReceived = false; | |||
#ifdef DISTRHO_OS_WINDOWS | |||
static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept | |||
{ | |||
if (dwCtrlType == CTRL_C_EVENT) | |||
{ | |||
gCloseSignalReceived = true; | |||
return TRUE; | |||
} | |||
return FALSE; | |||
} | |||
static void initSignalHandler() | |||
{ | |||
SetConsoleCtrlHandler(winSignalHandler, TRUE); | |||
} | |||
#else | |||
static void closeSignalHandler(int) noexcept | |||
{ | |||
gCloseSignalReceived = true; | |||
} | |||
static void initSignalHandler() | |||
{ | |||
struct sigaction sig; | |||
memset(&sig, 0, sizeof(sig)); | |||
sig.sa_handler = closeSignalHandler; | |||
sig.sa_flags = SA_RESTART; | |||
sigemptyset(&sig.sa_mask); | |||
sigaction(SIGINT, &sig, nullptr); | |||
sigaction(SIGTERM, &sig, nullptr); | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
class PluginJack : public IdleCallback | |||
#else | |||
class PluginJack | |||
#endif | |||
{ | |||
public: | |||
PluginJack(jack_client_t* const client) | |||
: fPlugin(), | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()), | |||
#endif | |||
fClient(client) | |||
{ | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
char strBuf[0xff+1]; | |||
strBuf[0xff] = '\0'; | |||
# if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
{ | |||
std::snprintf(strBuf, 0xff, "in%i", i+1); | |||
fPortAudioIns[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
{ | |||
std::snprintf(strBuf, 0xff, "out%i", i+1); | |||
fPortAudioOuts[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||
} | |||
# endif | |||
#endif | |||
fPortEventsIn = jack_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
if (fPlugin.getProgramCount() > 0) | |||
{ | |||
fPlugin.loadProgram(0); | |||
# if DISTRHO_PLUGIN_HAS_UI | |||
fUI.programLoaded(0); | |||
# endif | |||
} | |||
# if DISTRHO_PLUGIN_HAS_UI | |||
fProgramChanged = -1; | |||
# endif | |||
#endif | |||
if (const uint32_t count = fPlugin.getParameterCount()) | |||
{ | |||
fLastOutputValues = new float[count]; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fParametersChanged = new bool[count]; | |||
std::memset(fParametersChanged, 0, sizeof(bool)*count); | |||
#endif | |||
for (uint32_t i=0; i < count; ++i) | |||
{ | |||
if (fPlugin.isParameterOutput(i)) | |||
{ | |||
fLastOutputValues[i] = fPlugin.getParameterValue(i); | |||
} | |||
else | |||
{ | |||
fLastOutputValues[i] = 0.0f; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fUI.parameterChanged(i, fPlugin.getParameterValue(i)); | |||
#endif | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
fLastOutputValues = nullptr; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fParametersChanged = nullptr; | |||
#endif | |||
} | |||
jack_set_buffer_size_callback(fClient, jackBufferSizeCallback, this); | |||
jack_set_sample_rate_callback(fClient, jackSampleRateCallback, this); | |||
jack_set_process_callback(fClient, jackProcessCallback, this); | |||
jack_on_shutdown(fClient, jackShutdownCallback, this); | |||
fPlugin.activate(); | |||
jack_activate(fClient); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
if (const char* const name = jack_get_client_name(fClient)) | |||
fUI.setWindowTitle(name); | |||
else | |||
fUI.setWindowTitle(fPlugin.getName()); | |||
fUI.exec(this); | |||
#else | |||
while (! gCloseSignalReceived) | |||
d_sleep(1); | |||
#endif | |||
} | |||
~PluginJack() | |||
{ | |||
if (fClient != nullptr) | |||
jack_deactivate(fClient); | |||
if (fLastOutputValues != nullptr) | |||
{ | |||
delete[] fLastOutputValues; | |||
fLastOutputValues = nullptr; | |||
} | |||
fPlugin.deactivate(); | |||
if (fClient == nullptr) | |||
return; | |||
jack_port_unregister(fClient, fPortEventsIn); | |||
fPortEventsIn = nullptr; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
{ | |||
jack_port_unregister(fClient, fPortAudioIns[i]); | |||
fPortAudioIns[i] = nullptr; | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
{ | |||
jack_port_unregister(fClient, fPortAudioOuts[i]); | |||
fPortAudioOuts[i] = nullptr; | |||
} | |||
#endif | |||
jack_client_close(fClient); | |||
} | |||
// ------------------------------------------------------------------- | |||
protected: | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
void idleCallback() override | |||
{ | |||
if (gCloseSignalReceived) | |||
return fUI.quit(); | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
if (fProgramChanged >= 0) | |||
{ | |||
fUI.programLoaded(fProgramChanged); | |||
fProgramChanged = -1; | |||
} | |||
# endif | |||
for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
{ | |||
if (fPlugin.isParameterOutput(i)) | |||
{ | |||
const float value = fPlugin.getParameterValue(i); | |||
if (d_isEqual(fLastOutputValues[i], value)) | |||
continue; | |||
fLastOutputValues[i] = value; | |||
fUI.parameterChanged(i, value); | |||
} | |||
else if (fParametersChanged[i]) | |||
{ | |||
fParametersChanged[i] = false; | |||
fUI.parameterChanged(i, fPlugin.getParameterValue(i)); | |||
} | |||
} | |||
fUI.exec_idle(); | |||
} | |||
#endif | |||
void jackBufferSize(const jack_nframes_t nframes) | |||
{ | |||
fPlugin.setBufferSize(nframes, true); | |||
} | |||
void jackSampleRate(const jack_nframes_t nframes) | |||
{ | |||
fPlugin.setSampleRate(nframes, true); | |||
} | |||
void jackProcess(const jack_nframes_t nframes) | |||
{ | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
audioIns[i] = (const float*)jack_port_get_buffer(fPortAudioIns[i], nframes); | |||
#else | |||
static const float** audioIns = nullptr; | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
audioOuts[i] = (float*)jack_port_get_buffer(fPortAudioOuts[i], nframes); | |||
#else | |||
static float** audioOuts = nullptr; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
jack_position_t pos; | |||
fTimePosition.playing = (jack_transport_query(fClient, &pos) == JackTransportRolling); | |||
if (pos.unique_1 == pos.unique_2) | |||
{ | |||
fTimePosition.frame = pos.frame; | |||
if (pos.valid & JackTransportBBT) | |||
{ | |||
fTimePosition.bbt.valid = true; | |||
fTimePosition.bbt.bar = pos.bar; | |||
fTimePosition.bbt.beat = pos.beat; | |||
fTimePosition.bbt.tick = pos.tick; | |||
fTimePosition.bbt.barStartTick = pos.bar_start_tick; | |||
fTimePosition.bbt.beatsPerBar = pos.beats_per_bar; | |||
fTimePosition.bbt.beatType = pos.beat_type; | |||
fTimePosition.bbt.ticksPerBeat = pos.ticks_per_beat; | |||
fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute; | |||
} | |||
else | |||
fTimePosition.bbt.valid = false; | |||
} | |||
else | |||
{ | |||
fTimePosition.bbt.valid = false; | |||
fTimePosition.frame = 0; | |||
} | |||
fPlugin.setTimePosition(fTimePosition); | |||
#endif | |||
void* const midiBuf = jack_port_get_buffer(fPortEventsIn, nframes); | |||
if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) | |||
{ | |||
#if DISTRHO_PLUGIN_IS_SYNTH | |||
uint32_t midiEventCount = 0; | |||
MidiEvent midiEvents[eventCount]; | |||
#endif | |||
jack_midi_event_t jevent; | |||
for (uint32_t i=0; i < eventCount; ++i) | |||
{ | |||
if (jack_midi_event_get(&jevent, midiBuf, i) != 0) | |||
break; | |||
// Check if message is control change on channel 1 | |||
if (jevent.buffer[0] == 0xB0 && jevent.size == 3) | |||
{ | |||
const uint8_t control = jevent.buffer[1]; | |||
const uint8_t value = jevent.buffer[2]; | |||
/* NOTE: This is not optimal, we're iterating all parameters on every CC message. | |||
Since the JACK standalone is more of a test tool, this will do for now. */ | |||
for (uint32_t j=0, paramCount=fPlugin.getParameterCount(); j < paramCount; ++j) | |||
{ | |||
if (fPlugin.isParameterOutput(j)) | |||
continue; | |||
if (fPlugin.getParameterMidiCC(j) != control) | |||
continue; | |||
const float scaled = static_cast<float>(value)/127.0f; | |||
const float fvalue = fPlugin.getParameterRanges(j).getUnnormalizedValue(scaled); | |||
fPlugin.setParameterValue(j, fvalue); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fParametersChanged[j] = true; | |||
#endif | |||
break; | |||
} | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
// Check if message is program change on channel 1 | |||
else if (jevent.buffer[0] == 0xC0 && jevent.size == 2) | |||
{ | |||
const uint8_t program = jevent.buffer[1]; | |||
if (program < fPlugin.getProgramCount()) | |||
{ | |||
fPlugin.loadProgram(program); | |||
# if DISTRHO_PLUGIN_HAS_UI | |||
fProgramChanged = program; | |||
# endif | |||
} | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_IS_SYNTH | |||
MidiEvent& midiEvent(midiEvents[midiEventCount++]); | |||
midiEvent.frame = jevent.time; | |||
midiEvent.size = jevent.size; | |||
if (midiEvent.size > MidiEvent::kDataSize) | |||
midiEvent.dataExt = jevent.buffer; | |||
else | |||
std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size); | |||
#endif | |||
} | |||
#if DISTRHO_PLUGIN_IS_SYNTH | |||
fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount); | |||
#endif | |||
} | |||
#if DISTRHO_PLUGIN_IS_SYNTH | |||
else | |||
{ | |||
fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0); | |||
} | |||
#else | |||
fPlugin.run(audioIns, audioOuts, nframes); | |||
#endif | |||
} | |||
void jackShutdown() | |||
{ | |||
d_stderr("jack has shutdown, quitting now..."); | |||
fClient = nullptr; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fUI.quit(); | |||
#endif | |||
} | |||
// ------------------------------------------------------------------- | |||
void setParameterValue(const uint32_t index, const float value) | |||
{ | |||
fPlugin.setParameterValue(index, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void setState(const char* const key, const char* const value) | |||
{ | |||
fPlugin.setState(key, value); | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
void setSize(const uint width, const uint height) | |||
{ | |||
fUI.setWindowSize(width, height); | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
private: | |||
PluginExporter fPlugin; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
UIExporter fUI; | |||
#endif | |||
jack_client_t* fClient; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
jack_port_t* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
#endif | |||
jack_port_t* fPortEventsIn; | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
TimePosition fTimePosition; | |||
#endif | |||
// Temporary data | |||
float* fLastOutputValues; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
// Store DSP changes to send to UI | |||
bool* fParametersChanged; | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
int fProgramChanged; | |||
# endif | |||
#endif | |||
// ------------------------------------------------------------------- | |||
// Callbacks | |||
#define uiPtr ((PluginJack*)ptr) | |||
static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr) | |||
{ | |||
uiPtr->jackBufferSize(nframes); | |||
return 0; | |||
} | |||
static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr) | |||
{ | |||
uiPtr->jackSampleRate(nframes); | |||
return 0; | |||
} | |||
static int jackProcessCallback(jack_nframes_t nframes, void* ptr) | |||
{ | |||
uiPtr->jackProcess(nframes); | |||
return 0; | |||
} | |||
static void jackShutdownCallback(void* ptr) | |||
{ | |||
uiPtr->jackShutdown(); | |||
} | |||
static void setParameterValueCallback(void* ptr, uint32_t index, float value) | |||
{ | |||
uiPtr->setParameterValue(index, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
static void setStateCallback(void* ptr, const char* key, const char* value) | |||
{ | |||
uiPtr->setState(key, value); | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
static void setSizeCallback(void* ptr, uint width, uint height) | |||
{ | |||
uiPtr->setSize(width, height); | |||
} | |||
#endif | |||
#undef uiPtr | |||
}; | |||
END_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
int main() | |||
{ | |||
USE_NAMESPACE_DISTRHO; | |||
jack_status_t status = jack_status_t(0x0); | |||
jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); | |||
if (client == nullptr) | |||
{ | |||
String errorString; | |||
if (status & JackFailure) | |||
errorString += "Overall operation failed;\n"; | |||
if (status & JackInvalidOption) | |||
errorString += "The operation contained an invalid or unsupported option;\n"; | |||
if (status & JackNameNotUnique) | |||
errorString += "The desired client name was not unique;\n"; | |||
if (status & JackServerStarted) | |||
errorString += "The JACK server was started as a result of this operation;\n"; | |||
if (status & JackServerFailed) | |||
errorString += "Unable to connect to the JACK server;\n"; | |||
if (status & JackServerError) | |||
errorString += "Communication error with the JACK server;\n"; | |||
if (status & JackNoSuchClient) | |||
errorString += "Requested client does not exist;\n"; | |||
if (status & JackLoadFailure) | |||
errorString += "Unable to load internal client;\n"; | |||
if (status & JackInitFailure) | |||
errorString += "Unable to initialize client;\n"; | |||
if (status & JackShmFailure) | |||
errorString += "Unable to access shared memory;\n"; | |||
if (status & JackVersionError) | |||
errorString += "Client's protocol version does not match;\n"; | |||
if (status & JackBackendError) | |||
errorString += "Backend Error;\n"; | |||
if (status & JackClientZombie) | |||
errorString += "Client is being shutdown against its will;\n"; | |||
if (errorString.isNotEmpty()) | |||
{ | |||
errorString[errorString.length()-2] = '.'; | |||
d_stderr("Failed to create jack client, reason was:\n%s", errorString.buffer()); | |||
} | |||
else | |||
d_stderr("Failed to create jack client, cannot continue!"); | |||
return 1; | |||
} | |||
USE_NAMESPACE_DISTRHO; | |||
initSignalHandler(); | |||
d_lastBufferSize = jack_get_buffer_size(client); | |||
d_lastSampleRate = jack_get_sample_rate(client); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
d_lastUiSampleRate = d_lastSampleRate; | |||
#endif | |||
const PluginJack p(client); | |||
return 0; | |||
} | |||
// ----------------------------------------------------------------------- |
@@ -1,735 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#include "DistrhoPluginInternal.hpp" | |||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
# error Cannot use MIDI Output with LADSPA or DSSI | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
# include "dssi/dssi.h" | |||
#else | |||
# include "ladspa/ladspa.h" | |||
# if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
# error Cannot use MIDI with LADSPA | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
# warning LADSPA cannot handle states | |||
# endif | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
# warning LADSPA/DSSI does not support TimePos | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
class PluginLadspaDssi | |||
{ | |||
public: | |||
PluginLadspaDssi() | |||
: fPortControls(nullptr), | |||
fLastControlValues(nullptr) | |||
{ | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
fPortAudioIns[i] = nullptr; | |||
#else | |||
fPortAudioIns = nullptr; | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
fPortAudioOuts[i] = nullptr; | |||
#else | |||
fPortAudioOuts = nullptr; | |||
#endif | |||
if (const uint32_t count = fPlugin.getParameterCount()) | |||
{ | |||
fPortControls = new LADSPA_Data*[count]; | |||
fLastControlValues = new LADSPA_Data[count]; | |||
for (uint32_t i=0; i < count; ++i) | |||
{ | |||
fPortControls[i] = nullptr; | |||
fLastControlValues[i] = fPlugin.getParameterValue(i); | |||
} | |||
} | |||
else | |||
{ | |||
fPortControls = nullptr; | |||
fLastControlValues = nullptr; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
fPortLatency = nullptr; | |||
#endif | |||
} | |||
~PluginLadspaDssi() noexcept | |||
{ | |||
if (fPortControls != nullptr) | |||
{ | |||
delete[] fPortControls; | |||
fPortControls = nullptr; | |||
} | |||
if (fLastControlValues != nullptr) | |||
{ | |||
delete[] fLastControlValues; | |||
fLastControlValues = nullptr; | |||
} | |||
} | |||
// ------------------------------------------------------------------- | |||
void ladspa_activate() | |||
{ | |||
fPlugin.activate(); | |||
} | |||
void ladspa_deactivate() | |||
{ | |||
fPlugin.deactivate(); | |||
} | |||
// ------------------------------------------------------------------- | |||
void ladspa_connect_port(const ulong port, LADSPA_Data* const dataLocation) noexcept | |||
{ | |||
ulong index = 0; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
{ | |||
if (port == index++) | |||
{ | |||
fPortAudioIns[i] = dataLocation; | |||
return; | |||
} | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
{ | |||
if (port == index++) | |||
{ | |||
fPortAudioOuts[i] = dataLocation; | |||
return; | |||
} | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
if (port == index++) | |||
{ | |||
fPortLatency = dataLocation; | |||
return; | |||
} | |||
#endif | |||
for (ulong i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
{ | |||
if (port == index++) | |||
{ | |||
fPortControls[i] = dataLocation; | |||
return; | |||
} | |||
} | |||
} | |||
// ------------------------------------------------------------------- | |||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
void ladspa_run(const ulong sampleCount) | |||
{ | |||
dssi_run_synth(sampleCount, nullptr, 0); | |||
} | |||
void dssi_run_synth(const ulong sampleCount, snd_seq_event_t* const events, const ulong eventCount) | |||
#else | |||
void ladspa_run(const ulong sampleCount) | |||
#endif | |||
{ | |||
// pre-roll | |||
if (sampleCount == 0) | |||
return updateParameterOutputs(); | |||
// Check for updated parameters | |||
float curValue; | |||
for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
{ | |||
if (fPortControls[i] == nullptr) | |||
continue; | |||
curValue = *fPortControls[i]; | |||
if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) | |||
{ | |||
fLastControlValues[i] = curValue; | |||
fPlugin.setParameterValue(i, curValue); | |||
} | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
// Get MIDI Events | |||
uint32_t midiEventCount = 0; | |||
MidiEvent midiEvents[eventCount]; | |||
for (uint32_t i=0, j; i < eventCount; ++i) | |||
{ | |||
const snd_seq_event_t& seqEvent(events[i]); | |||
// FIXME | |||
if (seqEvent.data.note.channel > 0xF || seqEvent.data.control.channel > 0xF) | |||
continue; | |||
switch (seqEvent.type) | |||
{ | |||
case SND_SEQ_EVENT_NOTEOFF: | |||
j = midiEventCount++; | |||
midiEvents[j].frame = seqEvent.time.tick; | |||
midiEvents[j].size = 3; | |||
midiEvents[j].data[0] = 0x80 + seqEvent.data.note.channel; | |||
midiEvents[j].data[1] = seqEvent.data.note.note; | |||
midiEvents[j].data[2] = 0; | |||
midiEvents[j].data[3] = 0; | |||
break; | |||
case SND_SEQ_EVENT_NOTEON: | |||
j = midiEventCount++; | |||
midiEvents[j].frame = seqEvent.time.tick; | |||
midiEvents[j].size = 3; | |||
midiEvents[j].data[0] = 0x90 + seqEvent.data.note.channel; | |||
midiEvents[j].data[1] = seqEvent.data.note.note; | |||
midiEvents[j].data[2] = seqEvent.data.note.velocity; | |||
midiEvents[j].data[3] = 0; | |||
break; | |||
case SND_SEQ_EVENT_KEYPRESS: | |||
j = midiEventCount++; | |||
midiEvents[j].frame = seqEvent.time.tick; | |||
midiEvents[j].size = 3; | |||
midiEvents[j].data[0] = 0xA0 + seqEvent.data.note.channel; | |||
midiEvents[j].data[1] = seqEvent.data.note.note; | |||
midiEvents[j].data[2] = seqEvent.data.note.velocity; | |||
midiEvents[j].data[3] = 0; | |||
break; | |||
case SND_SEQ_EVENT_CONTROLLER: | |||
j = midiEventCount++; | |||
midiEvents[j].frame = seqEvent.time.tick; | |||
midiEvents[j].size = 3; | |||
midiEvents[j].data[0] = 0xB0 + seqEvent.data.control.channel; | |||
midiEvents[j].data[1] = seqEvent.data.control.param; | |||
midiEvents[j].data[2] = seqEvent.data.control.value; | |||
midiEvents[j].data[3] = 0; | |||
break; | |||
case SND_SEQ_EVENT_CHANPRESS: | |||
j = midiEventCount++; | |||
midiEvents[j].frame = seqEvent.time.tick; | |||
midiEvents[j].size = 2; | |||
midiEvents[j].data[0] = 0xD0 + seqEvent.data.control.channel; | |||
midiEvents[j].data[1] = seqEvent.data.control.value; | |||
midiEvents[j].data[2] = 0; | |||
midiEvents[j].data[3] = 0; | |||
break; | |||
case SND_SEQ_EVENT_PITCHBEND: | |||
j = midiEventCount++; | |||
midiEvents[j].frame = seqEvent.time.tick; | |||
midiEvents[j].size = 3; | |||
midiEvents[j].data[0] = 0xE0 + seqEvent.data.control.channel; | |||
uint16_t tempvalue = seqEvent.data.control.value + 8192; | |||
midiEvents[j].data[1] = tempvalue & 0x7F; | |||
midiEvents[j].data[2] = tempvalue >> 7; | |||
midiEvents[j].data[3] = 0; | |||
break; | |||
} | |||
} | |||
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, midiEvents, midiEventCount); | |||
#else | |||
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | |||
#endif | |||
updateParameterOutputs(); | |||
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
return; // unused | |||
(void)events; (void)eventCount; | |||
#endif | |||
} | |||
// ------------------------------------------------------------------- | |||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
char* dssi_configure(const char* const key, const char* const value) | |||
{ | |||
if (std::strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, std::strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) | |||
return nullptr; | |||
if (std::strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, std::strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) | |||
return nullptr; | |||
fPlugin.setState(key, value); | |||
return nullptr; | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
const DSSI_Program_Descriptor* dssi_get_program(const ulong index) | |||
{ | |||
if (index >= fPlugin.getProgramCount()) | |||
return nullptr; | |||
static DSSI_Program_Descriptor desc; | |||
desc.Bank = index / 128; | |||
desc.Program = index % 128; | |||
desc.Name = fPlugin.getProgramName(index); | |||
return &desc; | |||
} | |||
void dssi_select_program(const ulong bank, const ulong program) | |||
{ | |||
const ulong realProgram(bank * 128 + program); | |||
DISTRHO_SAFE_ASSERT_RETURN(realProgram < fPlugin.getProgramCount(),); | |||
fPlugin.loadProgram(realProgram); | |||
// Update control inputs | |||
for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
{ | |||
if (fPlugin.isParameterOutput(i)) | |||
continue; | |||
fLastControlValues[i] = fPlugin.getParameterValue(i); | |||
if (fPortControls[i] != nullptr) | |||
*fPortControls[i] = fLastControlValues[i]; | |||
} | |||
} | |||
# endif | |||
int dssi_get_midi_controller_for_port(const ulong port) noexcept | |||
{ | |||
const uint32_t parameterOffset = fPlugin.getParameterOffset(); | |||
if (port > parameterOffset) | |||
return DSSI_NONE; | |||
const uint8_t midiCC = fPlugin.getParameterMidiCC(port-parameterOffset); | |||
if (midiCC == 0 || midiCC == 32 || midiCC >= 0x78) | |||
return DSSI_NONE; | |||
return DSSI_CC(midiCC); | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
private: | |||
PluginExporter fPlugin; | |||
// LADSPA ports | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
const LADSPA_Data* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |||
#else | |||
const LADSPA_Data** fPortAudioIns; | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
LADSPA_Data* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |||
#else | |||
LADSPA_Data** fPortAudioOuts; | |||
#endif | |||
LADSPA_Data** fPortControls; | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
LADSPA_Data* fPortLatency; | |||
#endif | |||
// Temporary data | |||
LADSPA_Data* fLastControlValues; | |||
// ------------------------------------------------------------------- | |||
void updateParameterOutputs() | |||
{ | |||
for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
{ | |||
if (! fPlugin.isParameterOutput(i)) | |||
continue; | |||
fLastControlValues[i] = fPlugin.getParameterValue(i); | |||
if (fPortControls[i] != nullptr) | |||
*fPortControls[i] = fLastControlValues[i]; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
if (fPortLatency != nullptr) | |||
*fPortLatency = fPlugin.getLatency(); | |||
#endif | |||
} | |||
}; | |||
// ----------------------------------------------------------------------- | |||
static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, ulong sampleRate) | |||
{ | |||
if (d_lastBufferSize == 0) | |||
d_lastBufferSize = 2048; | |||
d_lastSampleRate = sampleRate; | |||
return new PluginLadspaDssi(); | |||
} | |||
#define instancePtr ((PluginLadspaDssi*)instance) | |||
static void ladspa_connect_port(LADSPA_Handle instance, ulong port, LADSPA_Data* dataLocation) | |||
{ | |||
instancePtr->ladspa_connect_port(port, dataLocation); | |||
} | |||
static void ladspa_activate(LADSPA_Handle instance) | |||
{ | |||
instancePtr->ladspa_activate(); | |||
} | |||
static void ladspa_run(LADSPA_Handle instance, ulong sampleCount) | |||
{ | |||
instancePtr->ladspa_run(sampleCount); | |||
} | |||
static void ladspa_deactivate(LADSPA_Handle instance) | |||
{ | |||
instancePtr->ladspa_deactivate(); | |||
} | |||
static void ladspa_cleanup(LADSPA_Handle instance) | |||
{ | |||
delete instancePtr; | |||
} | |||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value) | |||
{ | |||
return instancePtr->dssi_configure(key, value); | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, ulong index) | |||
{ | |||
return instancePtr->dssi_get_program(index); | |||
} | |||
static void dssi_select_program(LADSPA_Handle instance, ulong bank, ulong program) | |||
{ | |||
instancePtr->dssi_select_program(bank, program); | |||
} | |||
# endif | |||
static int dssi_get_midi_controller_for_port(LADSPA_Handle instance, ulong port) | |||
{ | |||
return instancePtr->dssi_get_midi_controller_for_port(port); | |||
} | |||
# if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
static void dssi_run_synth(LADSPA_Handle instance, ulong sampleCount, snd_seq_event_t* events, ulong eventCount) | |||
{ | |||
instancePtr->dssi_run_synth(sampleCount, events, eventCount); | |||
} | |||
# endif | |||
#endif | |||
#undef instancePtr | |||
// ----------------------------------------------------------------------- | |||
static LADSPA_Descriptor sLadspaDescriptor = { | |||
/* UniqueID */ 0, | |||
/* Label */ nullptr, | |||
#if DISTRHO_PLUGIN_IS_RT_SAFE | |||
/* Properties */ LADSPA_PROPERTY_HARD_RT_CAPABLE, | |||
#else | |||
/* Properties */ 0x0, | |||
#endif | |||
/* Name */ nullptr, | |||
/* Maker */ nullptr, | |||
/* Copyright */ nullptr, | |||
/* PortCount */ 0, | |||
/* PortDescriptors */ nullptr, | |||
/* PortNames */ nullptr, | |||
/* PortRangeHints */ nullptr, | |||
/* ImplementationData */ nullptr, | |||
ladspa_instantiate, | |||
ladspa_connect_port, | |||
ladspa_activate, | |||
ladspa_run, | |||
/* run_adding */ nullptr, | |||
/* set_run_adding_gain */ nullptr, | |||
ladspa_deactivate, | |||
ladspa_cleanup | |||
}; | |||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
static DSSI_Descriptor sDssiDescriptor = { | |||
1, | |||
&sLadspaDescriptor, | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
dssi_configure, | |||
# else | |||
/* configure */ nullptr, | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
dssi_get_program, | |||
dssi_select_program, | |||
# else | |||
/* get_program */ nullptr, | |||
/* select_program */ nullptr, | |||
# endif | |||
dssi_get_midi_controller_for_port, | |||
# if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
dssi_run_synth, | |||
# else | |||
/* run_synth */ nullptr, | |||
# endif | |||
/* run_synth_adding */ nullptr, | |||
/* run_multiple_synths */ nullptr, | |||
/* run_multiple_synths_adding */ nullptr, | |||
nullptr, nullptr | |||
}; | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
class DescriptorInitializer | |||
{ | |||
public: | |||
DescriptorInitializer() | |||
{ | |||
// Create dummy plugin to get data from | |||
d_lastBufferSize = 512; | |||
d_lastSampleRate = 44100.0; | |||
PluginExporter plugin; | |||
d_lastBufferSize = 0; | |||
d_lastSampleRate = 0.0; | |||
// Get port count, init | |||
ulong port = 0; | |||
ulong portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.getParameterCount(); | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
portCount += 1; | |||
#endif | |||
const char** const portNames = new const char*[portCount]; | |||
LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor[portCount]; | |||
LADSPA_PortRangeHint* portRangeHints = new LADSPA_PortRangeHint [portCount]; | |||
// Set ports | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++port) | |||
{ | |||
const AudioPort& aport(plugin.getAudioPort(true, i)); | |||
portNames[port] = strdup(aport.name); | |||
portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT; | |||
portRangeHints[port].HintDescriptor = 0x0; | |||
portRangeHints[port].LowerBound = 0.0f; | |||
portRangeHints[port].UpperBound = 1.0f; | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++port) | |||
{ | |||
const AudioPort& aport(plugin.getAudioPort(false, i)); | |||
portNames[port] = strdup(aport.name); | |||
portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT; | |||
portRangeHints[port].HintDescriptor = 0x0; | |||
portRangeHints[port].LowerBound = 0.0f; | |||
portRangeHints[port].UpperBound = 1.0f; | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
// Set latency port | |||
portNames[port] = strdup("_latency"); | |||
portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; | |||
portRangeHints[port].HintDescriptor = LADSPA_HINT_SAMPLE_RATE|LADSPA_HINT_INTEGER; | |||
portRangeHints[port].LowerBound = 0.0f; | |||
portRangeHints[port].UpperBound = 1.0f; | |||
++port; | |||
#endif | |||
for (ulong i=0, count=plugin.getParameterCount(); i < count; ++i, ++port) | |||
{ | |||
portNames[port] = strdup((const char*)plugin.getParameterName(i)); | |||
portDescriptors[port] = LADSPA_PORT_CONTROL; | |||
if (plugin.isParameterOutput(i)) | |||
portDescriptors[port] |= LADSPA_PORT_OUTPUT; | |||
else | |||
portDescriptors[port] |= LADSPA_PORT_INPUT; | |||
{ | |||
const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |||
const float defValue(ranges.def); | |||
portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; | |||
portRangeHints[port].LowerBound = ranges.min; | |||
portRangeHints[port].UpperBound = ranges.max; | |||
if (defValue == 0.0f) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; | |||
else if (defValue == 1.0f) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1; | |||
else if (defValue == 100.0f) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100; | |||
else if (defValue == 440.0f) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440; | |||
else if (ranges.min == defValue) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM; | |||
else if (ranges.max == defValue) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM; | |||
else | |||
{ | |||
const float middleValue = ranges.min/2.0f + ranges.max/2.0f; | |||
const float middleLow = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | |||
const float middleHigh = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f; | |||
if (defValue < middleLow) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW; | |||
else if (defValue > middleHigh) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH; | |||
else | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE; | |||
} | |||
} | |||
{ | |||
const uint32_t hints(plugin.getParameterHints(i)); | |||
if (hints & kParameterIsBoolean) | |||
{ | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; | |||
} | |||
else | |||
{ | |||
if (hints & kParameterIsInteger) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER; | |||
if (hints & kParameterIsLogarithmic) | |||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_LOGARITHMIC; | |||
} | |||
} | |||
} | |||
// Set data | |||
sLadspaDescriptor.UniqueID = plugin.getUniqueId(); | |||
sLadspaDescriptor.Label = strdup(plugin.getLabel()); | |||
sLadspaDescriptor.Name = strdup(plugin.getName()); | |||
sLadspaDescriptor.Maker = strdup(plugin.getMaker()); | |||
sLadspaDescriptor.Copyright = strdup(plugin.getLicense()); | |||
sLadspaDescriptor.PortCount = portCount; | |||
sLadspaDescriptor.PortNames = portNames; | |||
sLadspaDescriptor.PortDescriptors = portDescriptors; | |||
sLadspaDescriptor.PortRangeHints = portRangeHints; | |||
} | |||
~DescriptorInitializer() | |||
{ | |||
if (sLadspaDescriptor.Label != nullptr) | |||
{ | |||
std::free((void*)sLadspaDescriptor.Label); | |||
sLadspaDescriptor.Label = nullptr; | |||
} | |||
if (sLadspaDescriptor.Name != nullptr) | |||
{ | |||
std::free((void*)sLadspaDescriptor.Name); | |||
sLadspaDescriptor.Name = nullptr; | |||
} | |||
if (sLadspaDescriptor.Maker != nullptr) | |||
{ | |||
std::free((void*)sLadspaDescriptor.Maker); | |||
sLadspaDescriptor.Maker = nullptr; | |||
} | |||
if (sLadspaDescriptor.Copyright != nullptr) | |||
{ | |||
std::free((void*)sLadspaDescriptor.Copyright); | |||
sLadspaDescriptor.Copyright = nullptr; | |||
} | |||
if (sLadspaDescriptor.PortDescriptors != nullptr) | |||
{ | |||
delete[] sLadspaDescriptor.PortDescriptors; | |||
sLadspaDescriptor.PortDescriptors = nullptr; | |||
} | |||
if (sLadspaDescriptor.PortRangeHints != nullptr) | |||
{ | |||
delete[] sLadspaDescriptor.PortRangeHints; | |||
sLadspaDescriptor.PortRangeHints = nullptr; | |||
} | |||
if (sLadspaDescriptor.PortNames != nullptr) | |||
{ | |||
for (ulong i=0; i < sLadspaDescriptor.PortCount; ++i) | |||
{ | |||
if (sLadspaDescriptor.PortNames[i] != nullptr) | |||
std::free((void*)sLadspaDescriptor.PortNames[i]); | |||
} | |||
delete[] sLadspaDescriptor.PortNames; | |||
sLadspaDescriptor.PortNames = nullptr; | |||
} | |||
} | |||
}; | |||
static DescriptorInitializer sDescInit; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
DISTRHO_PLUGIN_EXPORT | |||
const LADSPA_Descriptor* ladspa_descriptor(ulong index) | |||
{ | |||
USE_NAMESPACE_DISTRHO | |||
return (index == 0) ? &sLadspaDescriptor : nullptr; | |||
} | |||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | |||
DISTRHO_PLUGIN_EXPORT | |||
const DSSI_Descriptor* dssi_descriptor(ulong index) | |||
{ | |||
USE_NAMESPACE_DISTRHO | |||
return (index == 0) ? &sDssiDescriptor : nullptr; | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- |
@@ -1,720 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2017 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#include "DistrhoPluginInternal.hpp" | |||
#include "lv2/atom.h" | |||
#include "lv2/buf-size.h" | |||
#include "lv2/data-access.h" | |||
#include "lv2/instance-access.h" | |||
#include "lv2/midi.h" | |||
#include "lv2/options.h" | |||
#include "lv2/port-props.h" | |||
#include "lv2/presets.h" | |||
#include "lv2/resize-port.h" | |||
#include "lv2/state.h" | |||
#include "lv2/time.h" | |||
#include "lv2/ui.h" | |||
#include "lv2/units.h" | |||
#include "lv2/urid.h" | |||
#include "lv2/worker.h" | |||
#include "lv2/lv2_kxstudio_properties.h" | |||
#include "lv2/lv2_programs.h" | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
# include "mod-license.h" | |||
#endif | |||
#include <fstream> | |||
#include <iostream> | |||
#ifndef DISTRHO_PLUGIN_URI | |||
# error DISTRHO_PLUGIN_URI undefined! | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | |||
# define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_USES_MODGUI | |||
# define DISTRHO_PLUGIN_USES_MODGUI 0 | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
# if DISTRHO_OS_HAIKU | |||
# define DISTRHO_LV2_UI_TYPE "BeUI" | |||
# elif DISTRHO_OS_MAC | |||
# define DISTRHO_LV2_UI_TYPE "CocoaUI" | |||
# elif DISTRHO_OS_WINDOWS | |||
# define DISTRHO_LV2_UI_TYPE "WindowsUI" | |||
# else | |||
# define DISTRHO_LV2_UI_TYPE "X11UI" | |||
# endif | |||
#else | |||
# define DISTRHO_LV2_UI_TYPE "UI" | |||
#endif | |||
#define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
#define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI)) | |||
// ----------------------------------------------------------------------- | |||
DISTRHO_PLUGIN_EXPORT | |||
void lv2_generate_ttl(const char* const basename) | |||
{ | |||
USE_NAMESPACE_DISTRHO | |||
// Dummy plugin to get data from | |||
d_lastBufferSize = 512; | |||
d_lastSampleRate = 44100.0; | |||
PluginExporter plugin; | |||
d_lastBufferSize = 0; | |||
d_lastSampleRate = 0.0; | |||
String pluginDLL(basename); | |||
String pluginTTL(pluginDLL + ".ttl"); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
String pluginUI(pluginDLL); | |||
# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
pluginUI.truncate(pluginDLL.rfind("_dsp")); | |||
pluginUI += "_ui"; | |||
const String uiTTL(pluginUI + ".ttl"); | |||
# endif | |||
#endif | |||
// --------------------------------------------- | |||
{ | |||
std::cout << "Writing manifest.ttl..."; std::cout.flush(); | |||
std::fstream manifestFile("manifest.ttl", std::ios::out); | |||
String manifestString; | |||
manifestString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
manifestString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
manifestString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
manifestString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
#endif | |||
manifestString += "\n"; | |||
manifestString += "<" DISTRHO_PLUGIN_URI ">\n"; | |||
manifestString += " a lv2:Plugin ;\n"; | |||
manifestString += " lv2:binary <" + pluginDLL + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||
#if DISTRHO_PLUGIN_USES_MODGUI | |||
manifestString += " rdfs:seeAlso <" + pluginTTL + "> ,\n"; | |||
manifestString += " <modgui.ttl> .\n"; | |||
#else | |||
manifestString += " rdfs:seeAlso <" + pluginTTL + "> .\n"; | |||
#endif | |||
manifestString += "\n"; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
manifestString += "<" DISTRHO_UI_URI ">\n"; | |||
manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n"; | |||
manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n"; | |||
# if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
manifestString += "\n"; | |||
manifestString += " lv2:extensionData ui:idleInterface ,\n"; | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
manifestString += " ui:showInterface ,\n"; | |||
manifestString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||
# else | |||
manifestString += " ui:showInterface ;\n"; | |||
# endif | |||
manifestString += "\n"; | |||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
manifestString += " ui:resize ,\n"; | |||
manifestString += " ui:touch ;\n"; | |||
manifestString += "\n"; | |||
# endif | |||
manifestString += " lv2:requiredFeature <" LV2_DATA_ACCESS_URI "> ,\n"; | |||
manifestString += " <" LV2_INSTANCE_ACCESS_URI "> ,\n"; | |||
manifestString += " <" LV2_OPTIONS__options "> ,\n"; | |||
manifestString += " <" LV2_URID__map "> .\n"; | |||
# else | |||
manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n"; | |||
# endif | |||
manifestString += "\n"; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#"); | |||
char strBuf[0xff+1]; | |||
strBuf[0xff] = '\0'; | |||
String presetString; | |||
// Presets | |||
for (uint32_t i = 0; i < plugin.getProgramCount(); ++i) | |||
{ | |||
std::snprintf(strBuf, 0xff, "%03i", i+1); | |||
presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\n"; | |||
presetString += " a pset:Preset ;\n"; | |||
presetString += " lv2:appliesTo <" DISTRHO_PLUGIN_URI "> ;\n"; | |||
presetString += " rdfs:label \"" + plugin.getProgramName(i) + "\" ;\n"; | |||
presetString += " rdfs:seeAlso <presets.ttl> .\n"; | |||
presetString += "\n"; | |||
manifestString += presetString; | |||
} | |||
#endif | |||
manifestFile << manifestString << std::endl; | |||
manifestFile.close(); | |||
std::cout << " done!" << std::endl; | |||
} | |||
// --------------------------------------------- | |||
{ | |||
std::cout << "Writing " << pluginTTL << "..."; std::cout.flush(); | |||
std::fstream pluginFile(pluginTTL, std::ios::out); | |||
String pluginString; | |||
// header | |||
#if DISTRHO_LV2_USE_EVENTS_IN | |||
pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
#endif | |||
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||
pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
#ifdef DISTRHO_PLUGIN_BRAND | |||
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | |||
#endif | |||
pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
#endif | |||
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |||
pluginString += "\n"; | |||
// plugin | |||
pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | |||
#ifdef DISTRHO_PLUGIN_LV2_CATEGORY | |||
pluginString += " a " DISTRHO_PLUGIN_LV2_CATEGORY ", lv2:Plugin ;\n"; | |||
#elif DISTRHO_PLUGIN_IS_SYNTH | |||
pluginString += " a lv2:InstrumentPlugin, lv2:Plugin ;\n"; | |||
#else | |||
pluginString += " a lv2:Plugin ;\n"; | |||
#endif | |||
pluginString += "\n"; | |||
// extensionData | |||
pluginString += " lv2:extensionData <" LV2_STATE__interface "> "; | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
pluginString += ",\n <" LV2_OPTIONS__interface "> "; | |||
pluginString += ",\n <" LV2_WORKER__interface "> "; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
pluginString += ",\n <" LV2_PROGRAMS__Interface "> "; | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
pluginString += ",\n <" MOD_LICENSE__interface "> "; | |||
#endif | |||
pluginString += ";\n\n"; | |||
// optionalFeatures | |||
#if DISTRHO_PLUGIN_IS_RT_SAFE | |||
pluginString += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ,\n"; | |||
pluginString += " <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||
#else | |||
pluginString += " lv2:optionalFeature <" LV2_BUF_SIZE__boundedBlockLength "> ;\n"; | |||
#endif | |||
pluginString += "\n"; | |||
// requiredFeatures | |||
pluginString += " lv2:requiredFeature <" LV2_OPTIONS__options "> "; | |||
pluginString += ",\n <" LV2_URID__map "> "; | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
pluginString += ",\n <" LV2_WORKER__schedule "> "; | |||
#endif | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
pluginString += ",\n <" MOD_LICENSE__feature "> "; | |||
#endif | |||
pluginString += ";\n\n"; | |||
// UI | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | |||
pluginString += "\n"; | |||
#endif | |||
{ | |||
uint32_t portIndex = 0; | |||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++portIndex) | |||
{ | |||
const AudioPort& port(plugin.getAudioPort(true, i)); | |||
if (i == 0) | |||
pluginString += " lv2:port [\n"; | |||
else | |||
pluginString += " [\n"; | |||
if (port.hints & kAudioPortIsCV) | |||
pluginString += " a lv2:InputPort, lv2:CVPort ;\n"; | |||
else | |||
pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; | |||
pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||
pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n"; | |||
pluginString += " lv2:name \"" + port.name + "\" ;\n"; | |||
if (port.hints & kAudioPortIsSidechain) | |||
pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |||
if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) | |||
pluginString += " ] ;\n"; | |||
else | |||
pluginString += " ] ,\n"; | |||
} | |||
pluginString += "\n"; | |||
#endif | |||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++portIndex) | |||
{ | |||
const AudioPort& port(plugin.getAudioPort(false, i)); | |||
if (i == 0) | |||
pluginString += " lv2:port [\n"; | |||
else | |||
pluginString += " [\n"; | |||
if (port.hints & kAudioPortIsCV) | |||
pluginString += " a lv2:OutputPort, lv2:CVPort ;\n"; | |||
else | |||
pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; | |||
pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||
pluginString += " lv2:symbol \"lv2_" + port.symbol + "\" ;\n"; | |||
pluginString += " lv2:name \"" + port.name + "\" ;\n"; | |||
if (port.hints & kAudioPortIsSidechain) | |||
pluginString += " lv2:portProperty lv2:isSideChain;\n"; | |||
if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) | |||
pluginString += " ] ;\n"; | |||
else | |||
pluginString += " ] ,\n"; | |||
} | |||
pluginString += "\n"; | |||
#endif | |||
#if DISTRHO_LV2_USE_EVENTS_IN | |||
pluginString += " lv2:port [\n"; | |||
pluginString += " a lv2:InputPort, atom:AtomPort ;\n"; | |||
pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||
pluginString += " lv2:name \"Events Input\" ;\n"; | |||
pluginString += " lv2:symbol \"lv2_events_in\" ;\n"; | |||
pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |||
pluginString += " atom:bufferType atom:Sequence ;\n"; | |||
# if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||
pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_TIMEPOS | |||
pluginString += " atom:supports <" LV2_TIME__Position "> ;\n"; | |||
# endif | |||
pluginString += " ] ;\n\n"; | |||
++portIndex; | |||
#endif | |||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||
pluginString += " lv2:port [\n"; | |||
pluginString += " a lv2:OutputPort, atom:AtomPort ;\n"; | |||
pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||
pluginString += " lv2:name \"Events Output\" ;\n"; | |||
pluginString += " lv2:symbol \"lv2_events_out\" ;\n"; | |||
pluginString += " rsz:minimumSize " + String(DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE) + " ;\n"; | |||
pluginString += " atom:bufferType atom:Sequence ;\n"; | |||
# if (DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI) | |||
pluginString += " atom:supports <" LV2_ATOM__String "> ;\n"; | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
pluginString += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | |||
# endif | |||
pluginString += " ] ;\n\n"; | |||
++portIndex; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
pluginString += " lv2:port [\n"; | |||
pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |||
pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||
pluginString += " lv2:name \"Latency\" ;\n"; | |||
pluginString += " lv2:symbol \"lv2_latency\" ;\n"; | |||
pluginString += " lv2:designation lv2:latency ;\n"; | |||
pluginString += " lv2:portProperty lv2:reportsLatency, lv2:integer, <" LV2_PORT_PROPS__notOnGUI "> ;\n"; | |||
pluginString += " ] ;\n\n"; | |||
++portIndex; | |||
#endif | |||
for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i, ++portIndex) | |||
{ | |||
if (i == 0) | |||
pluginString += " lv2:port [\n"; | |||
else | |||
pluginString += " [\n"; | |||
if (plugin.isParameterOutput(i)) | |||
pluginString += " a lv2:OutputPort, lv2:ControlPort ;\n"; | |||
else | |||
pluginString += " a lv2:InputPort, lv2:ControlPort ;\n"; | |||
pluginString += " lv2:index " + String(portIndex) + " ;\n"; | |||
bool designated = false; | |||
// designation | |||
if (! plugin.isParameterOutput(i)) | |||
{ | |||
switch (plugin.getParameterDesignation(i)) | |||
{ | |||
case kParameterDesignationNull: | |||
break; | |||
case kParameterDesignationBypass: | |||
designated = true; | |||
pluginString += " lv2:name \"Enabled\" ;\n"; | |||
pluginString += " lv2:symbol \"lv2_enabled\" ;\n"; | |||
pluginString += " lv2:default 1 ;\n"; | |||
pluginString += " lv2:minimum 0 ;\n"; | |||
pluginString += " lv2:maximum 1 ;\n"; | |||
pluginString += " lv2:portProperty lv2:toggled , lv2:integer ;\n"; | |||
pluginString += " lv2:designation lv2:enabled ;\n"; | |||
break; | |||
} | |||
} | |||
// name and symbol | |||
if (! designated) | |||
{ | |||
pluginString += " lv2:name \"" + plugin.getParameterName(i) + "\" ;\n"; | |||
String symbol(plugin.getParameterSymbol(i)); | |||
if (symbol.isEmpty()) | |||
symbol = "lv2_port_" + String(portIndex-1); | |||
pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | |||
} | |||
// ranges | |||
if (! designated) | |||
{ | |||
const ParameterRanges& ranges(plugin.getParameterRanges(i)); | |||
if (plugin.getParameterHints(i) & kParameterIsInteger) | |||
{ | |||
if (! plugin.isParameterOutput(i)) | |||
pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n"; | |||
pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | |||
pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | |||
} | |||
else | |||
{ | |||
if (! plugin.isParameterOutput(i)) | |||
pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n"; | |||
pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | |||
pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; | |||
} | |||
} | |||
// unit | |||
if (! designated) | |||
{ | |||
const String& unit(plugin.getParameterUnit(i)); | |||
if (! unit.isEmpty()) | |||
{ | |||
if (unit == "db" || unit == "dB") | |||
{ | |||
pluginString += " unit:unit unit:db ;\n"; | |||
} | |||
else if (unit == "hz" || unit == "Hz") | |||
{ | |||
pluginString += " unit:unit unit:hz ;\n"; | |||
} | |||
else if (unit == "khz" || unit == "kHz") | |||
{ | |||
pluginString += " unit:unit unit:khz ;\n"; | |||
} | |||
else if (unit == "mhz" || unit == "mHz") | |||
{ | |||
pluginString += " unit:unit unit:mhz ;\n"; | |||
} | |||
else if (unit == "ms") | |||
{ | |||
pluginString += " unit:unit unit:ms ;\n"; | |||
} | |||
else if (unit == "s") | |||
{ | |||
pluginString += " unit:unit unit:s ;\n"; | |||
} | |||
else if (unit == "%") | |||
{ | |||
pluginString += " unit:unit unit:pc ;\n"; | |||
} | |||
else | |||
{ | |||
pluginString += " unit:unit [\n"; | |||
pluginString += " rdfs:label \"" + unit + "\" ;\n"; | |||
pluginString += " unit:symbol \"" + unit + "\" ;\n"; | |||
pluginString += " unit:render \"%f " + unit + "\" ;\n"; | |||
pluginString += " ] ;\n"; | |||
} | |||
} | |||
} | |||
// hints | |||
if (! designated) | |||
{ | |||
const uint32_t hints(plugin.getParameterHints(i)); | |||
if (hints & kParameterIsBoolean) | |||
pluginString += " lv2:portProperty lv2:toggled ;\n"; | |||
if (hints & kParameterIsInteger) | |||
pluginString += " lv2:portProperty lv2:integer ;\n"; | |||
if (hints & kParameterIsLogarithmic) | |||
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; | |||
if ((hints & kParameterIsAutomable) == 0 && ! plugin.isParameterOutput(i)) | |||
{ | |||
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; | |||
pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; | |||
} | |||
} | |||
if (i+1 == count) | |||
pluginString += " ] ;\n\n"; | |||
else | |||
pluginString += " ] ,\n"; | |||
} | |||
} | |||
// comment | |||
{ | |||
const String comment(plugin.getDescription()); | |||
if (comment.isNotEmpty()) | |||
pluginString += " rdfs:comment \"\"\"\n" + comment + "\n\"\"\" ;\n\n"; | |||
} | |||
#ifdef DISTRHO_PLUGIN_BRAND | |||
// MOD | |||
pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; | |||
pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n"; | |||
#endif | |||
// name | |||
pluginString += " doap:name \"" + String(plugin.getName()) + "\" ;\n"; | |||
// license | |||
{ | |||
const String license(plugin.getLicense()); | |||
if (license.contains("://")) | |||
pluginString += " doap:license <" + license + "> ;\n\n"; | |||
else | |||
pluginString += " doap:license \"" + license + "\" ;\n\n"; | |||
} | |||
// developer | |||
{ | |||
const String homepage(plugin.getHomePage()); | |||
pluginString += " doap:maintainer [\n"; | |||
pluginString += " foaf:name \"" + String(plugin.getMaker()) + "\" ;\n"; | |||
if (homepage.isNotEmpty()) | |||
pluginString += " foaf:homepage <" + homepage + "> ;\n"; | |||
pluginString += " ] ;\n\n"; | |||
} | |||
{ | |||
const uint32_t version(plugin.getVersion()); | |||
const uint32_t majorVersion = (version & 0xFF0000) >> 16; | |||
const uint32_t microVersion = (version & 0x00FF00) >> 8; | |||
/* */ uint32_t minorVersion = (version & 0x0000FF) >> 0; | |||
// NOTE: LV2 ignores 'major' version and says 0 for minor is pre-release/unstable. | |||
if (majorVersion > 0) | |||
minorVersion += 2; | |||
pluginString += " lv2:microVersion " + String(microVersion) + " ;\n"; | |||
pluginString += " lv2:minorVersion " + String(minorVersion) + " .\n"; | |||
} | |||
pluginFile << pluginString << std::endl; | |||
pluginFile.close(); | |||
std::cout << " done!" << std::endl; | |||
} | |||
// --------------------------------------------- | |||
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
{ | |||
std::cout << "Writing " << uiTTL << "..."; std::cout.flush(); | |||
std::fstream uiFile(uiTTL, std::ios::out); | |||
String uiString; | |||
uiString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
uiString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
uiString += "\n"; | |||
uiString += "<" DISTRHO_UI_URI ">\n"; | |||
uiString += " lv2:extensionData ui:idleInterface ,\n"; | |||
# if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
uiString += " ui:showInterface ,\n"; | |||
uiString += " <" LV2_PROGRAMS__Interface "> ;\n"; | |||
# else | |||
uiString += " ui:showInterface ;\n"; | |||
# endif | |||
uiString += "\n"; | |||
# if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
uiString += " lv2:optionalFeature ui:noUserResize ,\n"; | |||
uiString += " ui:resize ,\n"; | |||
uiString += " ui:touch ;\n"; | |||
uiString += "\n"; | |||
# endif | |||
uiString += " lv2:requiredFeature <" LV2_OPTIONS__options "> ,\n"; | |||
uiString += " <" LV2_URID__map "> .\n"; | |||
uiFile << uiString << std::endl; | |||
uiFile.close(); | |||
std::cout << " done!" << std::endl; | |||
} | |||
#endif | |||
// --------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
{ | |||
std::cout << "Writing presets.ttl..."; std::cout.flush(); | |||
std::fstream presetsFile("presets.ttl", std::ios::out); | |||
String presetsString; | |||
presetsString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
presetsString += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; | |||
# if DISTRHO_PLUGIN_WANT_STATE | |||
presetsString += "@prefix state: <" LV2_STATE_PREFIX "> .\n"; | |||
# endif | |||
presetsString += "\n"; | |||
const uint32_t numParameters = plugin.getParameterCount(); | |||
const uint32_t numPrograms = plugin.getProgramCount(); | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
const uint32_t numStates = plugin.getStateCount(); | |||
# endif | |||
const String presetSeparator(std::strstr(DISTRHO_PLUGIN_URI, "#") != nullptr ? ":" : "#"); | |||
char strBuf[0xff+1]; | |||
strBuf[0xff] = '\0'; | |||
String presetString; | |||
for (uint32_t i=0; i<numPrograms; ++i) | |||
{ | |||
std::snprintf(strBuf, 0xff, "%03i", i+1); | |||
plugin.loadProgram(i); | |||
presetString = "<" DISTRHO_PLUGIN_URI + presetSeparator + "preset" + strBuf + ">\n"; | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
if (numParameters == 0 && numStates == 0) | |||
#else | |||
if (numParameters == 0) | |||
#endif | |||
{ | |||
presetString += " ."; | |||
presetsString += presetString; | |||
continue; | |||
} | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
presetString += " state:state [\n"; | |||
for (uint32_t j=0; j<numStates; ++j) | |||
{ | |||
const String key = plugin.getStateKey(j); | |||
const String value = plugin.getState(key); | |||
presetString += " <urn:distrho:" + key + ">"; | |||
if (value.length() < 10) | |||
presetString += " \"" + value + "\" ;\n"; | |||
else | |||
presetString += "\n\"\"\"\n" + value + "\n\"\"\" ;\n"; | |||
} | |||
if (numParameters > 0) | |||
presetString += " ] ;\n\n"; | |||
else | |||
presetString += " ] .\n\n"; | |||
# endif | |||
bool firstParameter = true; | |||
for (uint32_t j=0; j <numParameters; ++j) | |||
{ | |||
if (plugin.isParameterOutput(j)) | |||
continue; | |||
if (firstParameter) | |||
{ | |||
presetString += " lv2:port [\n"; | |||
firstParameter = false; | |||
} | |||
else | |||
{ | |||
presetString += " [\n"; | |||
} | |||
presetString += " lv2:symbol \"" + plugin.getParameterSymbol(j) + "\" ;\n"; | |||
if (plugin.getParameterHints(j) & kParameterIsInteger) | |||
presetString += " pset:value " + String(int(plugin.getParameterValue(j))) + " ;\n"; | |||
else | |||
presetString += " pset:value " + String(plugin.getParameterValue(j)) + " ;\n"; | |||
if (j+1 == numParameters || plugin.isParameterOutput(j+1)) | |||
presetString += " ] .\n\n"; | |||
else | |||
presetString += " ] ,\n"; | |||
} | |||
presetsString += presetString; | |||
} | |||
presetsFile << presetsString << std::endl; | |||
presetsFile.close(); | |||
std::cout << " done!" << std::endl; | |||
} | |||
#endif | |||
} |
@@ -1,511 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#include "DistrhoUIInternal.hpp" | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
# error DSSI UIs do not support direct access! | |||
#endif | |||
#include "../extra/Sleep.hpp" | |||
#include <lo/lo.h> | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
struct OscData { | |||
lo_address addr; | |||
const char* path; | |||
lo_server server; | |||
OscData() | |||
: addr(nullptr), | |||
path(nullptr), | |||
server(nullptr) {} | |||
void idle() const | |||
{ | |||
if (server == nullptr) | |||
return; | |||
while (lo_server_recv_noblock(server, 0) != 0) {} | |||
} | |||
void send_configure(const char* const key, const char* const value) const | |||
{ | |||
char targetPath[std::strlen(path)+11]; | |||
std::strcpy(targetPath, path); | |||
std::strcat(targetPath, "/configure"); | |||
lo_send(addr, targetPath, "ss", key, value); | |||
} | |||
void send_control(const int32_t index, const float value) const | |||
{ | |||
char targetPath[std::strlen(path)+9]; | |||
std::strcpy(targetPath, path); | |||
std::strcat(targetPath, "/control"); | |||
lo_send(addr, targetPath, "if", index, value); | |||
} | |||
void send_midi(uchar data[4]) const | |||
{ | |||
char targetPath[std::strlen(path)+6]; | |||
std::strcpy(targetPath, path); | |||
std::strcat(targetPath, "/midi"); | |||
lo_send(addr, targetPath, "m", data); | |||
} | |||
void send_update(const char* const url) const | |||
{ | |||
char targetPath[std::strlen(path)+8]; | |||
std::strcpy(targetPath, path); | |||
std::strcat(targetPath, "/update"); | |||
lo_send(addr, targetPath, "s", url); | |||
} | |||
void send_exiting() const | |||
{ | |||
char targetPath[std::strlen(path)+9]; | |||
std::strcpy(targetPath, path); | |||
std::strcat(targetPath, "/exiting"); | |||
lo_send(addr, targetPath, ""); | |||
} | |||
}; | |||
// ----------------------------------------------------------------------- | |||
class UIDssi | |||
{ | |||
public: | |||
UIDssi(const OscData& oscData, const char* const uiTitle) | |||
: fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), | |||
fHostClosed(false), | |||
fOscData(oscData) | |||
{ | |||
fUI.setWindowTitle(uiTitle); | |||
} | |||
~UIDssi() | |||
{ | |||
if (fOscData.server != nullptr && ! fHostClosed) | |||
fOscData.send_exiting(); | |||
} | |||
void exec() | |||
{ | |||
for (;;) | |||
{ | |||
fOscData.idle(); | |||
if (fHostClosed || ! fUI.idle()) | |||
break; | |||
d_msleep(30); | |||
} | |||
} | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
void dssiui_configure(const char* key, const char* value) | |||
{ | |||
fUI.stateChanged(key, value); | |||
} | |||
#endif | |||
void dssiui_control(ulong index, float value) | |||
{ | |||
fUI.parameterChanged(index, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void dssiui_program(ulong bank, ulong program) | |||
{ | |||
fUI.programLoaded(bank * 128 + program); | |||
} | |||
#endif | |||
void dssiui_samplerate(const double sampleRate) | |||
{ | |||
fUI.setSampleRate(sampleRate, true); | |||
} | |||
void dssiui_show() | |||
{ | |||
fUI.setWindowVisible(true); | |||
} | |||
void dssiui_hide() | |||
{ | |||
fUI.setWindowVisible(false); | |||
} | |||
void dssiui_quit() | |||
{ | |||
fHostClosed = true; | |||
fUI.quit(); | |||
} | |||
// ------------------------------------------------------------------- | |||
protected: | |||
void setParameterValue(const uint32_t rindex, const float value) | |||
{ | |||
if (fOscData.server == nullptr) | |||
return; | |||
fOscData.send_control(rindex, value); | |||
} | |||
void setState(const char* const key, const char* const value) | |||
{ | |||
if (fOscData.server == nullptr) | |||
return; | |||
fOscData.send_configure(key, value); | |||
} | |||
void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
{ | |||
if (fOscData.server == nullptr) | |||
return; | |||
if (channel > 0xF) | |||
return; | |||
uint8_t mdata[4] = { 0, channel, note, velocity }; | |||
mdata[1] += (velocity != 0) ? 0x90 : 0x80; | |||
fOscData.send_midi(mdata); | |||
} | |||
void setSize(const uint width, const uint height) | |||
{ | |||
fUI.setWindowSize(width, height); | |||
} | |||
private: | |||
UIExporter fUI; | |||
bool fHostClosed; | |||
const OscData& fOscData; | |||
// ------------------------------------------------------------------- | |||
// Callbacks | |||
#define uiPtr ((UIDssi*)ptr) | |||
static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||
{ | |||
uiPtr->setParameterValue(rindex, value); | |||
} | |||
static void setStateCallback(void* ptr, const char* key, const char* value) | |||
{ | |||
uiPtr->setState(key, value); | |||
} | |||
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
{ | |||
uiPtr->sendNote(channel, note, velocity); | |||
} | |||
static void setSizeCallback(void* ptr, uint width, uint height) | |||
{ | |||
uiPtr->setSize(width, height); | |||
} | |||
#undef uiPtr | |||
}; | |||
// ----------------------------------------------------------------------- | |||
static OscData gOscData; | |||
static const char* gUiTitle = nullptr; | |||
static UIDssi* globalUI = nullptr; | |||
static void initUiIfNeeded() | |||
{ | |||
if (globalUI != nullptr) | |||
return; | |||
if (d_lastUiSampleRate == 0.0) | |||
d_lastUiSampleRate = 44100.0; | |||
globalUI = new UIDssi(gOscData, gUiTitle); | |||
} | |||
// ----------------------------------------------------------------------- | |||
int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*) | |||
{ | |||
d_debug("osc_debug_handler(\"%s\")", path); | |||
return 0; | |||
#ifndef DEBUG | |||
// unused | |||
(void)path; | |||
#endif | |||
} | |||
void osc_error_handler(int num, const char* msg, const char* path) | |||
{ | |||
d_stderr("osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
int osc_configure_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||
{ | |||
const char* const key = &argv[0]->s; | |||
const char* const value = &argv[1]->s; | |||
d_debug("osc_configure_handler(\"%s\", \"%s\")", key, value); | |||
initUiIfNeeded(); | |||
globalUI->dssiui_configure(key, value); | |||
return 0; | |||
} | |||
#endif | |||
int osc_control_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||
{ | |||
const int32_t rindex = argv[0]->i; | |||
const float value = argv[1]->f; | |||
d_debug("osc_control_handler(%i, %f)", rindex, value); | |||
int32_t index = rindex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS; | |||
// latency | |||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||
index -= 1; | |||
#endif | |||
if (index < 0) | |||
return 0; | |||
initUiIfNeeded(); | |||
globalUI->dssiui_control(index, value); | |||
return 0; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||
{ | |||
const int32_t bank = argv[0]->i; | |||
const int32_t program = argv[1]->f; | |||
d_debug("osc_program_handler(%i, %i)", bank, program); | |||
initUiIfNeeded(); | |||
globalUI->dssiui_program(bank, program); | |||
return 0; | |||
} | |||
#endif | |||
int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*) | |||
{ | |||
const int32_t sampleRate = argv[0]->i; | |||
d_debug("osc_sample_rate_handler(%i)", sampleRate); | |||
d_lastUiSampleRate = sampleRate; | |||
if (globalUI != nullptr) | |||
globalUI->dssiui_samplerate(sampleRate); | |||
return 0; | |||
} | |||
int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||
{ | |||
d_debug("osc_show_handler()"); | |||
initUiIfNeeded(); | |||
globalUI->dssiui_show(); | |||
return 0; | |||
} | |||
int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||
{ | |||
d_debug("osc_hide_handler()"); | |||
if (globalUI != nullptr) | |||
globalUI->dssiui_hide(); | |||
return 0; | |||
} | |||
int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*) | |||
{ | |||
d_debug("osc_quit_handler()"); | |||
if (globalUI != nullptr) | |||
globalUI->dssiui_quit(); | |||
return 0; | |||
} | |||
END_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
int main(int argc, char* argv[]) | |||
{ | |||
USE_NAMESPACE_DISTRHO | |||
// dummy test mode | |||
if (argc == 1) | |||
{ | |||
gUiTitle = "DSSI UI Test"; | |||
initUiIfNeeded(); | |||
globalUI->dssiui_show(); | |||
globalUI->exec(); | |||
delete globalUI; | |||
globalUI = nullptr; | |||
return 0; | |||
} | |||
if (argc != 5) | |||
{ | |||
fprintf(stderr, "Usage: %s <osc-url> <plugin-dll> <plugin-label> <instance-name>\n", argv[0]); | |||
return 1; | |||
} | |||
const char* oscUrl = argv[1]; | |||
const char* uiTitle = argv[4]; | |||
char* const oscHost = lo_url_get_hostname(oscUrl); | |||
char* const oscPort = lo_url_get_port(oscUrl); | |||
char* const oscPath = lo_url_get_path(oscUrl); | |||
size_t oscPathSize = strlen(oscPath); | |||
lo_address oscAddr = lo_address_new(oscHost, oscPort); | |||
lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, osc_error_handler); | |||
char* const oscServerPath = lo_server_get_url(oscServer); | |||
char pluginPath[strlen(oscServerPath)+oscPathSize]; | |||
strcpy(pluginPath, oscServerPath); | |||
strcat(pluginPath, oscPath+1); | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
char oscPathConfigure[oscPathSize+11]; | |||
strcpy(oscPathConfigure, oscPath); | |||
strcat(oscPathConfigure, "/configure"); | |||
lo_server_add_method(oscServer, oscPathConfigure, "ss", osc_configure_handler, nullptr); | |||
#endif | |||
char oscPathControl[oscPathSize+9]; | |||
strcpy(oscPathControl, oscPath); | |||
strcat(oscPathControl, "/control"); | |||
lo_server_add_method(oscServer, oscPathControl, "if", osc_control_handler, nullptr); | |||
d_stdout("oscServerPath: \"%s\"", oscServerPath); | |||
d_stdout("pluginPath: \"%s\"", pluginPath); | |||
d_stdout("oscPathControl: \"%s\"", oscPathControl); | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
char oscPathProgram[oscPathSize+9]; | |||
strcpy(oscPathProgram, oscPath); | |||
strcat(oscPathProgram, "/program"); | |||
lo_server_add_method(oscServer, oscPathProgram, "ii", osc_program_handler, nullptr); | |||
#endif | |||
char oscPathSampleRate[oscPathSize+13]; | |||
strcpy(oscPathSampleRate, oscPath); | |||
strcat(oscPathSampleRate, "/sample-rate"); | |||
lo_server_add_method(oscServer, oscPathSampleRate, "i", osc_sample_rate_handler, nullptr); | |||
char oscPathShow[oscPathSize+6]; | |||
strcpy(oscPathShow, oscPath); | |||
strcat(oscPathShow, "/show"); | |||
lo_server_add_method(oscServer, oscPathShow, "", osc_show_handler, nullptr); | |||
char oscPathHide[oscPathSize+6]; | |||
strcpy(oscPathHide, oscPath); | |||
strcat(oscPathHide, "/hide"); | |||
lo_server_add_method(oscServer, oscPathHide, "", osc_hide_handler, nullptr); | |||
char oscPathQuit[oscPathSize+6]; | |||
strcpy(oscPathQuit, oscPath); | |||
strcat(oscPathQuit, "/quit"); | |||
lo_server_add_method(oscServer, oscPathQuit, "", osc_quit_handler, nullptr); | |||
lo_server_add_method(oscServer, nullptr, nullptr, osc_debug_handler, nullptr); | |||
gUiTitle = uiTitle; | |||
gOscData.addr = oscAddr; | |||
gOscData.path = oscPath; | |||
gOscData.server = oscServer; | |||
gOscData.send_update(pluginPath); | |||
// wait for init | |||
for (int i=0; i < 100; ++i) | |||
{ | |||
lo_server_recv(oscServer); | |||
if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) | |||
break; | |||
d_msleep(50); | |||
} | |||
int ret = 1; | |||
if (d_lastUiSampleRate != 0.0 || globalUI != nullptr) | |||
{ | |||
initUiIfNeeded(); | |||
globalUI->exec(); | |||
delete globalUI; | |||
globalUI = nullptr; | |||
ret = 0; | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
lo_server_del_method(oscServer, oscPathConfigure, "ss"); | |||
#endif | |||
lo_server_del_method(oscServer, oscPathControl, "if"); | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
lo_server_del_method(oscServer, oscPathProgram, "ii"); | |||
#endif | |||
lo_server_del_method(oscServer, oscPathSampleRate, "i"); | |||
lo_server_del_method(oscServer, oscPathShow, ""); | |||
lo_server_del_method(oscServer, oscPathHide, ""); | |||
lo_server_del_method(oscServer, oscPathQuit, ""); | |||
lo_server_del_method(oscServer, nullptr, nullptr); | |||
std::free(oscServerPath); | |||
std::free(oscHost); | |||
std::free(oscPort); | |||
std::free(oscPath); | |||
lo_address_free(oscAddr); | |||
lo_server_free(oscServer); | |||
return ret; | |||
} |
@@ -1,542 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#include "DistrhoUIInternal.hpp" | |||
#include "../extra/String.hpp" | |||
#include "lv2/atom.h" | |||
#include "lv2/atom-util.h" | |||
#include "lv2/data-access.h" | |||
#include "lv2/instance-access.h" | |||
#include "lv2/options.h" | |||
#include "lv2/parameters.h" | |||
#include "lv2/ui.h" | |||
#include "lv2/urid.h" | |||
#include "lv2/lv2_kxstudio_properties.h" | |||
#include "lv2/lv2_programs.h" | |||
#ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX | |||
# define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:" | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
class UiLv2 | |||
{ | |||
public: | |||
UiLv2(const char* const bundlePath, const intptr_t winId, | |||
const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch, | |||
const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc, | |||
LV2UI_Widget* const widget, void* const dspPtr) | |||
: fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, dspPtr, bundlePath), | |||
fUridMap(uridMap), | |||
fUiResize(uiResz), | |||
fUiTouch(uiTouch), | |||
fController(controller), | |||
fWriteFunction(writeFunc), | |||
fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | |||
fKeyValueURID(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
fWinIdWasNull(winId == 0) | |||
{ | |||
if (fUiResize != nullptr && winId != 0) | |||
fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight()); | |||
if (widget != nullptr) | |||
*widget = (LV2UI_Widget)fUI.getWindowId(); | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
// tell the DSP we're ready to receive msgs | |||
setState("__dpf_ui_data__", ""); | |||
#endif | |||
if (winId != 0) | |||
return; | |||
// if winId == 0 then options must not be null | |||
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | |||
const LV2_URID uridWindowTitle(uridMap->map(uridMap->handle, LV2_UI__windowTitle)); | |||
const LV2_URID uridTransientWinId(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId)); | |||
bool hasTitle = false; | |||
for (int i=0; options[i].key != 0; ++i) | |||
{ | |||
if (options[i].key == uridTransientWinId) | |||
{ | |||
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Long)) | |||
{ | |||
if (const int64_t transientWinId = *(const int64_t*)options[i].value) | |||
fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId)); | |||
} | |||
else | |||
d_stderr("Host provides transientWinId but has wrong value type"); | |||
} | |||
else if (options[i].key == uridWindowTitle) | |||
{ | |||
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__String)) | |||
{ | |||
if (const char* const windowTitle = (const char*)options[i].value) | |||
{ | |||
hasTitle = true; | |||
fUI.setWindowTitle(windowTitle); | |||
} | |||
} | |||
else | |||
d_stderr("Host provides windowTitle but has wrong value type"); | |||
} | |||
} | |||
if (! hasTitle) | |||
fUI.setWindowTitle(DISTRHO_PLUGIN_NAME); | |||
} | |||
// ------------------------------------------------------------------- | |||
void lv2ui_port_event(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer) | |||
{ | |||
if (format == 0) | |||
{ | |||
const uint32_t parameterOffset(fUI.getParameterOffset()); | |||
if (rindex < parameterOffset) | |||
return; | |||
DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | |||
const float value(*(const float*)buffer); | |||
fUI.parameterChanged(rindex-parameterOffset, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
else if (format == fEventTransferURID) | |||
{ | |||
const LV2_Atom* const atom((const LV2_Atom*)buffer); | |||
DISTRHO_SAFE_ASSERT_RETURN(atom->type == fKeyValueURID,); | |||
const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom); | |||
const char* const value = key+(std::strlen(key)+1); | |||
fUI.stateChanged(key, value); | |||
} | |||
#endif | |||
} | |||
// ------------------------------------------------------------------- | |||
int lv2ui_idle() | |||
{ | |||
if (fWinIdWasNull) | |||
return (fUI.idle() && fUI.isVisible()) ? 0 : 1; | |||
return fUI.idle() ? 0 : 1; | |||
} | |||
int lv2ui_show() | |||
{ | |||
return fUI.setWindowVisible(true) ? 0 : 1; | |||
} | |||
int lv2ui_hide() | |||
{ | |||
return fUI.setWindowVisible(false) ? 0 : 1; | |||
} | |||
int lv2ui_resize(uint width, uint height) | |||
{ | |||
fUI.setWindowSize(width, height, true); | |||
return 0; | |||
} | |||
// ------------------------------------------------------------------- | |||
uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) | |||
{ | |||
// currently unused | |||
return LV2_OPTIONS_ERR_UNKNOWN; | |||
} | |||
uint32_t lv2_set_options(const LV2_Options_Option* const options) | |||
{ | |||
for (int i=0; options[i].key != 0; ++i) | |||
{ | |||
if (options[i].key == fUridMap->map(fUridMap->handle, LV2_PARAMETERS__sampleRate)) | |||
{ | |||
if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Float)) | |||
{ | |||
const float sampleRate(*(const float*)options[i].value); | |||
fUI.setSampleRate(sampleRate); | |||
continue; | |||
} | |||
else | |||
{ | |||
d_stderr("Host changed UI sample-rate but with wrong value type"); | |||
continue; | |||
} | |||
} | |||
} | |||
return LV2_OPTIONS_SUCCESS; | |||
} | |||
// ------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void lv2ui_select_program(const uint32_t bank, const uint32_t program) | |||
{ | |||
const uint32_t realProgram(bank * 128 + program); | |||
fUI.programLoaded(realProgram); | |||
} | |||
#endif | |||
// ------------------------------------------------------------------- | |||
protected: | |||
void editParameterValue(const uint32_t rindex, const bool started) | |||
{ | |||
if (fUiTouch != nullptr && fUiTouch->touch != nullptr) | |||
fUiTouch->touch(fUiTouch->handle, rindex, started); | |||
} | |||
void setParameterValue(const uint32_t rindex, const float value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
fWriteFunction(fController, rindex, sizeof(float), 0, &value); | |||
} | |||
void setState(const char* const key, const char* const value) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||
// join key and value | |||
String tmpStr; | |||
tmpStr += key; | |||
tmpStr += "\xff"; | |||
tmpStr += value; | |||
tmpStr[std::strlen(key)] = '\0'; | |||
// set msg size (key + separator + value + null terminator) | |||
const size_t msgSize(tmpStr.length()+1); | |||
// reserve atom space | |||
const size_t atomSize(sizeof(LV2_Atom) + msgSize); | |||
char atomBuf[atomSize]; | |||
std::memset(atomBuf, 0, atomSize); | |||
// set atom info | |||
LV2_Atom* const atom((LV2_Atom*)atomBuf); | |||
atom->size = msgSize; | |||
atom->type = fKeyValueURID; | |||
// set atom data | |||
std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize); | |||
// send to DSP side | |||
fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | |||
} | |||
void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) | |||
{ | |||
} | |||
void setSize(const uint width, const uint height) | |||
{ | |||
fUI.setWindowSize(width, height); | |||
if (fUiResize != nullptr && ! fWinIdWasNull) | |||
fUiResize->ui_resize(fUiResize->handle, width, height); | |||
} | |||
private: | |||
UIExporter fUI; | |||
// LV2 features | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2UI_Resize* const fUiResize; | |||
const LV2UI_Touch* const fUiTouch; | |||
// LV2 UI stuff | |||
const LV2UI_Controller fController; | |||
const LV2UI_Write_Function fWriteFunction; | |||
// Need to save this | |||
const LV2_URID fEventTransferURID; | |||
const LV2_URID fKeyValueURID; | |||
// using ui:showInterface if true | |||
bool fWinIdWasNull; | |||
// ------------------------------------------------------------------- | |||
// Callbacks | |||
#define uiPtr ((UiLv2*)ptr) | |||
static void editParameterCallback(void* ptr, uint32_t rindex, bool started) | |||
{ | |||
uiPtr->editParameterValue(rindex, started); | |||
} | |||
static void setParameterCallback(void* ptr, uint32_t rindex, float value) | |||
{ | |||
uiPtr->setParameterValue(rindex, value); | |||
} | |||
static void setStateCallback(void* ptr, const char* key, const char* value) | |||
{ | |||
uiPtr->setState(key, value); | |||
} | |||
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
{ | |||
uiPtr->sendNote(channel, note, velocity); | |||
} | |||
static void setSizeCallback(void* ptr, uint width, uint height) | |||
{ | |||
uiPtr->setSize(width, height); | |||
} | |||
#undef uiPtr | |||
}; | |||
// ----------------------------------------------------------------------- | |||
static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char* bundlePath, | |||
LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) | |||
{ | |||
if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0) | |||
{ | |||
d_stderr("Invalid plugin URI"); | |||
return nullptr; | |||
} | |||
const LV2_Options_Option* options = nullptr; | |||
const LV2_URID_Map* uridMap = nullptr; | |||
const LV2UI_Resize* uiResize = nullptr; | |||
const LV2UI_Touch* uiTouch = nullptr; | |||
void* parentId = nullptr; | |||
void* instance = nullptr; | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
struct LV2_DirectAccess_Interface { | |||
void* (*get_instance_pointer)(LV2_Handle handle); | |||
}; | |||
const LV2_Extension_Data_Feature* extData = nullptr; | |||
#endif | |||
for (int i=0; features[i] != nullptr; ++i) | |||
{ | |||
if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |||
options = (const LV2_Options_Option*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | |||
uridMap = (const LV2_URID_Map*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0) | |||
uiResize = (const LV2UI_Resize*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) | |||
parentId = features[i]->data; | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0) | |||
extData = (const LV2_Extension_Data_Feature*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0) | |||
instance = features[i]->data; | |||
#endif | |||
} | |||
if (options == nullptr && parentId == nullptr) | |||
{ | |||
d_stderr("Options feature missing (needed for show-interface), cannot continue!"); | |||
return nullptr; | |||
} | |||
if (uridMap == nullptr) | |||
{ | |||
d_stderr("URID Map feature missing, cannot continue!"); | |||
return nullptr; | |||
} | |||
if (parentId == nullptr) | |||
{ | |||
d_stdout("Parent Window Id missing, host should be using ui:showInterface..."); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
if (extData == nullptr || instance == nullptr) | |||
{ | |||
d_stderr("Data or instance access missing, cannot continue!"); | |||
return nullptr; | |||
} | |||
if (const LV2_DirectAccess_Interface* const directAccess = (const LV2_DirectAccess_Interface*)extData->data_access(DISTRHO_PLUGIN_LV2_STATE_PREFIX "direct-access")) | |||
instance = directAccess->get_instance_pointer(instance); | |||
else | |||
instance = nullptr; | |||
if (instance == nullptr) | |||
{ | |||
d_stderr("Failed to get direct access, cannot continue!"); | |||
return nullptr; | |||
} | |||
#endif | |||
const intptr_t winId((intptr_t)parentId); | |||
if (options != nullptr) | |||
{ | |||
const LV2_URID uridSampleRate(uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate)); | |||
for (int i=0; options[i].key != 0; ++i) | |||
{ | |||
if (options[i].key == uridSampleRate) | |||
{ | |||
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Float)) | |||
d_lastUiSampleRate = *(const float*)options[i].value; | |||
else | |||
d_stderr("Host provides UI sample-rate but has wrong value type"); | |||
break; | |||
} | |||
} | |||
} | |||
if (d_lastUiSampleRate < 1.0) | |||
{ | |||
d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)"); | |||
d_lastUiSampleRate = 44100.0; | |||
} | |||
return new UiLv2(bundlePath, winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, widget, instance); | |||
} | |||
#define uiPtr ((UiLv2*)ui) | |||
static void lv2ui_cleanup(LV2UI_Handle ui) | |||
{ | |||
delete uiPtr; | |||
} | |||
static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) | |||
{ | |||
uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); | |||
} | |||
// ----------------------------------------------------------------------- | |||
static int lv2ui_idle(LV2UI_Handle ui) | |||
{ | |||
return uiPtr->lv2ui_idle(); | |||
} | |||
static int lv2ui_show(LV2UI_Handle ui) | |||
{ | |||
return uiPtr->lv2ui_show(); | |||
} | |||
static int lv2ui_hide(LV2UI_Handle ui) | |||
{ | |||
return uiPtr->lv2ui_hide(); | |||
} | |||
static int lv2ui_resize(LV2UI_Handle ui, int width, int height) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, 1); | |||
DISTRHO_SAFE_ASSERT_RETURN(width > 0, 1); | |||
DISTRHO_SAFE_ASSERT_RETURN(height > 0, 1); | |||
return 1; // This needs more testing | |||
//return uiPtr->lv2ui_resize(width, height); | |||
} | |||
// ----------------------------------------------------------------------- | |||
static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options) | |||
{ | |||
return uiPtr->lv2_get_options(options); | |||
} | |||
static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* options) | |||
{ | |||
return uiPtr->lv2_set_options(options); | |||
} | |||
// ----------------------------------------------------------------------- | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program) | |||
{ | |||
uiPtr->lv2ui_select_program(bank, program); | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
static const void* lv2ui_extension_data(const char* uri) | |||
{ | |||
static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; | |||
static const LV2UI_Idle_Interface uiIdle = { lv2ui_idle }; | |||
static const LV2UI_Show_Interface uiShow = { lv2ui_show, lv2ui_hide }; | |||
static const LV2UI_Resize uiResz = { nullptr, lv2ui_resize }; | |||
if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) | |||
return &options; | |||
if (std::strcmp(uri, LV2_UI__idleInterface) == 0) | |||
return &uiIdle; | |||
if (std::strcmp(uri, LV2_UI__showInterface) == 0) | |||
return &uiShow; | |||
if (std::strcmp(uri, LV2_UI__resize) == 0) | |||
return &uiResz; | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program }; | |||
if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) | |||
return &uiPrograms; | |||
#endif | |||
return nullptr; | |||
} | |||
#undef instancePtr | |||
// ----------------------------------------------------------------------- | |||
static const LV2UI_Descriptor sLv2UiDescriptor = { | |||
DISTRHO_UI_URI, | |||
lv2ui_instantiate, | |||
lv2ui_cleanup, | |||
lv2ui_port_event, | |||
lv2ui_extension_data | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
DISTRHO_PLUGIN_EXPORT | |||
const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) | |||
{ | |||
USE_NAMESPACE_DISTRHO | |||
return (index == 0) ? &sLv2UiDescriptor : nullptr; | |||
} | |||
// ----------------------------------------------------------------------- |