67 changes: 33 additions & 34 deletions dpf/distrho/src/DistrhoPluginCLAP.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand Down Expand Up @@ -211,7 +211,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
#endif
const bool isFloating)
: fPlugin(plugin),
fPluinEventQueue(eventQueue),
fPluginEventQueue(eventQueue),
fEventQueue(eventQueue->fEventQueue),
fCachedParameters(eventQueue->fCachedParameters),
#if DISTRHO_PLUGIN_WANT_PROGRAMS
Expand Down Expand Up @@ -382,7 +382,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
*width = minimumWidth;
if (minimumHeight > *height)
*height = minimumHeight;

return true;
}

Expand Down Expand Up @@ -534,7 +534,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
private:
// Plugin and UI
PluginExporter& fPlugin;
ClapEventQueue* const fPluinEventQueue;
ClapEventQueue* const fPluginEventQueue;
ClapEventQueue::Queue& fEventQueue;
ClapEventQueue::CachedParameters& fCachedParameters;
#if DISTRHO_PLUGIN_WANT_PROGRAMS
Expand Down Expand Up @@ -578,7 +578,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
setStateCallback,
sendNoteCallback,
setSizeCallback,
fileRequestCallback,
nullptr, // TODO fileRequestCallback,
d_nextBundlePath,
fPlugin.getInstancePointer(),
fScaleFactor);
Expand Down Expand Up @@ -682,7 +682,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
#if DISTRHO_PLUGIN_WANT_STATE
void setState(const char* const key, const char* const value)
{
fPluinEventQueue->setStateFromUI(key, value);
fPluginEventQueue->setStateFromUI(key, value);
}

static void setStateCallback(void* const ptr, const char* key, const char* value)
Expand All @@ -708,6 +708,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
}
#endif

/* TODO
bool fileRequest(const char*)
{
return true;
Expand All @@ -717,6 +718,7 @@ class ClapUI : public DGL_NAMESPACE::IdleCallback
{
return static_cast<ClapUI*>(ptr)->fileRequest(key);
}
*/
};

// --------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -977,6 +979,7 @@ class PluginCLAP : ClapEventQueue
case CLAP_EVENT_PARAM_GESTURE_BEGIN:
case CLAP_EVENT_PARAM_GESTURE_END:
case CLAP_EVENT_TRANSPORT:
break;
case CLAP_EVENT_MIDI:
DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t),
event->size, sizeof(clap_event_midi_t));
Expand Down Expand Up @@ -1232,7 +1235,7 @@ class PluginCLAP : ClapEventQueue

DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value),
event->size, sizeof(clap_event_param_value));

setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event)));
}
}
Expand Down Expand Up @@ -2068,7 +2071,7 @@ static const char* const kSupportedAPIs[] = {
};

// TODO DPF external UI
static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
static bool CLAP_ABI clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
{
for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
{
Expand All @@ -2080,14 +2083,14 @@ static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const ap
}

// TODO DPF external UI
static bool clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
static bool CLAP_ABI clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
{
*api = kSupportedAPIs[0];
*is_floating = false;
return true;
}

static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
static bool CLAP_ABI clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
{
for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
{
Expand All @@ -2101,13 +2104,13 @@ static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const
return false;
}

static void clap_gui_destroy(const clap_plugin_t* const plugin)
static void CLAP_ABI clap_gui_destroy(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
instance->destroyUI();
}

static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
static bool CLAP_ABI clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
Expand All @@ -2121,79 +2124,79 @@ static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double s
#endif
}

static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
static bool CLAP_ABI clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->getSize(width, height);
}

static bool clap_gui_can_resize(const clap_plugin_t* const plugin)
static bool CLAP_ABI clap_gui_can_resize(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->canResize();
}

static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
static bool CLAP_ABI clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->getResizeHints(hints);
}

static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
static bool CLAP_ABI clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->adjustSize(width, height);
}

static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
static bool CLAP_ABI clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->setSizeFromHost(width, height);
}

static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
static bool CLAP_ABI clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->setParent(window);
}

static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
static bool CLAP_ABI clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->setTransient(window);
}

static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
static void CLAP_ABI clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,);
return gui->suggestTitle(title);
}

static bool clap_gui_show(const clap_plugin_t* const plugin)
static bool CLAP_ABI clap_gui_show(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
return gui->show();
}

static bool clap_gui_hide(const clap_plugin_t* const plugin)
static bool CLAP_ABI clap_gui_hide(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
Expand Down Expand Up @@ -2223,7 +2226,7 @@ static const clap_plugin_gui_t clap_plugin_gui = {
// plugin timer

#if DPF_CLAP_USING_HOST_TIMER
static void clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id)
static void CLAP_ABI clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
Expand All @@ -2242,14 +2245,14 @@ static const clap_plugin_timer_support_t clap_timer = {
// plugin audio ports

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input)
static uint32_t CLAP_ABI clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return is_input ? instance->getAudioPortCount<true>()
: instance->getAudioPortCount<false>();
}

static bool clap_plugin_audio_ports_get(const clap_plugin_t* const plugin,
static bool CLAP_ABI clap_plugin_audio_ports_get(const clap_plugin_t* const plugin,
const uint32_t index,
const bool is_input,
clap_audio_port_info_t* const info)
Expand All @@ -2269,13 +2272,13 @@ static const clap_plugin_audio_ports_t clap_plugin_audio_ports = {
// plugin note ports

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0
static uint32_t clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input)
static uint32_t CLAP_ABI clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input)
{
return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0;
}

static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
const bool is_input, clap_note_port_info_t* const info)
static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
const bool is_input, clap_note_port_info_t* const info)
{
if (is_input)
{
Expand Down Expand Up @@ -2370,7 +2373,6 @@ static const clap_plugin_latency_t clap_plugin_latency = {
};
#endif

#if DISTRHO_PLUGIN_WANT_STATE
// --------------------------------------------------------------------------------------------------------------------
// plugin state

Expand All @@ -2390,7 +2392,6 @@ static const clap_plugin_state_t clap_plugin_state = {
clap_plugin_state_save,
clap_plugin_state_load
};
#endif

// --------------------------------------------------------------------------------------------------------------------
// plugin
Expand Down Expand Up @@ -2452,6 +2453,8 @@ static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, cons
{
if (std::strcmp(id, CLAP_EXT_PARAMS) == 0)
return &clap_plugin_params;
if (std::strcmp(id, CLAP_EXT_STATE) == 0)
return &clap_plugin_state;
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0)
return &clap_plugin_audio_ports;
Expand All @@ -2464,10 +2467,6 @@ static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, cons
if (std::strcmp(id, CLAP_EXT_LATENCY) == 0)
return &clap_plugin_latency;
#endif
#if DISTRHO_PLUGIN_WANT_STATE
if (std::strcmp(id, CLAP_EXT_STATE) == 0)
return &clap_plugin_state;
#endif
#if DISTRHO_PLUGIN_HAS_UI
if (std::strcmp(id, CLAP_EXT_GUI) == 0)
return &clap_plugin_gui;
Expand Down
6 changes: 5 additions & 1 deletion dpf/distrho/src/DistrhoPluginChecks.h
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand All @@ -17,6 +17,10 @@
#ifndef DISTRHO_PLUGIN_CHECKS_H_INCLUDED
#define DISTRHO_PLUGIN_CHECKS_H_INCLUDED

#ifndef DISTRHO_DETAILS_HPP_INCLUDED
# error wrong include order
#endif

#include "DistrhoPluginInfo.h"

// -----------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion dpf/distrho/src/DistrhoPluginInternal.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand Down
39 changes: 25 additions & 14 deletions dpf/distrho/src/DistrhoPluginJACK.cpp
Expand Up @@ -152,23 +152,25 @@ class PluginJack
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)
{
const AudioPort& port(fPlugin.getAudioPort(true, i));
fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
ulong hints = JackPortIsInput;
if (port.hints & kAudioPortIsCV)
hints |= JackPortIsControlVoltage;
fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0);
setAudioPortMetadata(port, fPortAudioIns[i], i);
}
# 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);
const AudioPort& port(fPlugin.getAudioPort(false, i));
fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
ulong hints = JackPortIsOutput;
if (port.hints & kAudioPortIsCV)
hints |= JackPortIsControlVoltage;
fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0);
setAudioPortMetadata(port, fPortAudioOuts[i], DISTRHO_PLUGIN_NUM_INPUTS+i);
}
# endif
Expand Down Expand Up @@ -623,7 +625,8 @@ class PluginJack

{
char strBuf[0xff];
snprintf(strBuf, sizeof(0xff)-1, "%u", index);
snprintf(strBuf, 0xff - 2, "%u", index);
strBuf[0xff - 1] = '\0';
jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer");
}

Expand Down Expand Up @@ -807,7 +810,7 @@ class PluginProcessTestingThread : public Thread
protected:
void run() override
{
plugin.setBufferSize(256);
plugin.setBufferSize(256, true);
plugin.activate();

float buffer[256];
Expand Down Expand Up @@ -862,8 +865,8 @@ bool runSelfTests()

plugin.activate();
plugin.deactivate();
plugin.setBufferSize(128);
plugin.setSampleRate(48000);
plugin.setBufferSize(128, true);
plugin.setSampleRate(48000, true);
plugin.activate();

float buffer[128] = {};
Expand Down Expand Up @@ -1030,6 +1033,12 @@ int main(int argc, char* argv[])
jack_status_t status = jack_status_t(0x0);
jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);

#ifdef HAVE_JACK
#define STANDALONE_NAME "JACK client"
#else
#define STANDALONE_NAME "Native audio driver"
#endif

if (client == nullptr)
{
String errorString;
Expand Down Expand Up @@ -1060,20 +1069,22 @@ int main(int argc, char* argv[])
errorString += "Backend Error;\n";
if (status & JackClientZombie)
errorString += "Client is being shutdown against its will;\n";
if (status & JackBridgeNativeFailed)
errorString += "Native audio driver was unable to start;\n";

if (errorString.isNotEmpty())
{
errorString[errorString.length()-2] = '.';
d_stderr("Failed to create the JACK client, reason was:\n%s", errorString.buffer());
d_stderr("Failed to create the " STANDALONE_NAME ", reason was:\n%s", errorString.buffer());
}
else
d_stderr("Failed to create the JACK client, cannot continue!");
d_stderr("Failed to create the " STANDALONE_NAME ", cannot continue!");

#if defined(DISTRHO_OS_MAC)
CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr,
DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8);
CFStringRef errorStringRef = CFStringCreateWithCString(nullptr,
String("Failed to create JACK client, reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);
String("Failed to create " STANDALONE_NAME ", reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);

CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel,
nullptr, nullptr, nullptr,
Expand All @@ -1097,7 +1108,7 @@ int main(int argc, char* argv[])
FreeLibrary(user32);
}

const String win32error = "Failed to create JACK client, reason was:\n" + errorString;
const String win32error = "Failed to create " STANDALONE_NAME ", reason was:\n" + errorString;
MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR);
#endif

Expand Down
18 changes: 11 additions & 7 deletions dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand All @@ -22,7 +22,7 @@
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# error Cannot use MIDI Output with LADSPA or DSSI
#endif
#if DISTRHO_PLUGIN_WANT_FULL_STATE
#if DISTRHO_PLUGIN_WANT_FULL_STATE && !defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WITH_LADSPA)
# error Cannot use full state with LADSPA or DSSI
#endif

Expand Down Expand Up @@ -618,13 +618,19 @@ static const struct DescriptorInitializer
else
portDescriptors[port] |= LADSPA_PORT_INPUT;

const uint32_t hints = plugin.getParameterHints(i);

{
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;
// LADSPA doesn't allow bounded hints on toggles
portRangeHints[port].HintDescriptor = hints & kParameterIsBoolean
? 0
: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;

portRangeHints[port].LowerBound = ranges.min;
portRangeHints[port].UpperBound = ranges.max;

/**/ if (d_isZero(defValue))
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0;
Expand Down Expand Up @@ -654,8 +660,6 @@ static const struct DescriptorInitializer
}

{
const uint32_t hints = plugin.getParameterHints(i);

if (hints & kParameterIsBoolean)
{
portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED;
Expand Down
8 changes: 5 additions & 3 deletions dpf/distrho/src/DistrhoPluginLV2.cpp
Expand Up @@ -821,7 +821,7 @@ class PluginLv2
if (options[i].type == fURIDs.atomInt)
{
const int32_t bufferSize(*(const int32_t*)options[i].value);
fPlugin.setBufferSize(bufferSize);
fPlugin.setBufferSize(bufferSize, true);
}
else
{
Expand All @@ -833,7 +833,7 @@ class PluginLv2
if (options[i].type == fURIDs.atomInt)
{
const int32_t bufferSize(*(const int32_t*)options[i].value);
fPlugin.setBufferSize(bufferSize);
fPlugin.setBufferSize(bufferSize, true);
}
else
{
Expand All @@ -846,7 +846,7 @@ class PluginLv2
{
const float sampleRate(*(const float*)options[i].value);
fSampleRate = sampleRate;
fPlugin.setSampleRate(sampleRate);
fPlugin.setSampleRate(sampleRate, true);
}
else
{
Expand Down Expand Up @@ -1070,6 +1070,7 @@ class PluginLv2

setState(key, filename);

/* FIXME host should be responsible for updating UI side, not us
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
{
if (fPlugin.getStateKey(i) == key)
Expand All @@ -1079,6 +1080,7 @@ class PluginLv2
break;
}
}
*/

return LV2_WORKER_SUCCESS;
}
Expand Down
357 changes: 343 additions & 14 deletions dpf/distrho/src/DistrhoPluginLV2export.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dpf/distrho/src/DistrhoPluginVST2.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand Down
4 changes: 2 additions & 2 deletions dpf/distrho/src/DistrhoPluginVST3.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand Down Expand Up @@ -4810,7 +4810,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
d_strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name));
d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor));
d_strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version));
d_strncpy(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version));
d_strncpy(info->sdk_version, "VST 3.7.4", ARRAY_SIZE(info->sdk_version));

if (idx == 0)
{
Expand Down
3 changes: 2 additions & 1 deletion dpf/distrho/src/DistrhoUI.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 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
Expand All @@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoDetails.hpp"
#include "src/DistrhoPluginChecks.h"
#include "src/DistrhoDefines.h"

Expand Down
5 changes: 3 additions & 2 deletions dpf/distrho/src/DistrhoUIInternal.hpp
Expand Up @@ -57,9 +57,10 @@ class UIExporter
void* const dspPtr = nullptr,
const double scaleFactor = 0.0,
const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff)
const uint32_t fgColor = 0xffffffff,
const char* const appClassName = nullptr)
: ui(nullptr),
uiData(new UI::PrivateData())
uiData(new UI::PrivateData(appClassName))
{
uiData->sampleRate = sampleRate;
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr;
Expand Down
237 changes: 224 additions & 13 deletions dpf/distrho/src/DistrhoUILV2.cpp
Expand Up @@ -78,7 +78,8 @@ class UiLv2
const float sampleRate,
const float scaleFactor,
const uint32_t bgColor,
const uint32_t fgColor)
const uint32_t fgColor,
const char* const appClassName)
: fUridMap(uridMap),
fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)),
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
Expand All @@ -97,7 +98,7 @@ class UiLv2
sendNoteCallback,
nullptr, // resize is very messy, hosts can do it without extensions
fileRequestCallback,
bundlePath, dspPtr, scaleFactor, bgColor, fgColor)
bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName)
{
if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
Expand All @@ -113,10 +114,11 @@ class UiLv2
// if winId == 0 then options must not be null
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);

#ifndef __EMSCRIPTEN__
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;
const char* windowTitle = nullptr;

for (int i=0; options[i].key != 0; ++i)
{
Expand All @@ -134,19 +136,18 @@ class UiLv2
{
if (options[i].type == fURIDs.atomString)
{
if (const char* const windowTitle = (const char*)options[i].value)
{
hasTitle = true;
fUI.setWindowTitle(windowTitle);
}
windowTitle = (const char*)options[i].value;
}
else
d_stderr("Host provides windowTitle but has wrong value type");
}
}

if (! hasTitle)
fUI.setWindowTitle(DISTRHO_PLUGIN_NAME);
if (windowTitle == nullptr)
windowTitle = DISTRHO_PLUGIN_NAME;

fUI.setWindowTitle(windowTitle);
#endif
}

// -------------------------------------------------------------------
Expand Down Expand Up @@ -213,9 +214,14 @@ class UiLv2
fUI.stateChanged(key, value);
}
}
else if (atom->type == fURIDs.midiEvent)
{
// ignore
}
else
{
d_stdout("DPF :: received atom not handled");
d_stdout("DPF :: received atom not handled :: %s",
fUridUnmap != nullptr ? fUridUnmap->unmap(fUridUnmap->handle, atom->type) : "(null)");
}
}
#endif
Expand Down Expand Up @@ -258,7 +264,7 @@ class UiLv2
if (options[i].type == fURIDs.atomFloat)
{
const float sampleRate = *(const float*)options[i].value;
fUI.setSampleRate(sampleRate);
fUI.setSampleRate(sampleRate, true);
continue;
}
else
Expand Down Expand Up @@ -559,17 +565,20 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
float scaleFactor = 0.0f;
uint32_t bgColor = 0;
uint32_t fgColor = 0xffffffff;
const char* appClassName = nullptr;

if (options != nullptr)
{
const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
const LV2_URID uridAtomString = uridMap->map(uridMap->handle, LV2_ATOM__String);
const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor);
const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor);
#ifndef DISTRHO_OS_MAC
const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
#endif
const LV2_URID uridClassName = uridMap->map(uridMap->handle, "urn:distrho:className");

for (int i=0; options[i].key != 0; ++i)
{
Expand Down Expand Up @@ -603,6 +612,13 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
d_stderr("Host provides UI scale factor but has wrong value type");
}
#endif
else if (options[i].key == uridClassName)
{
if (options[i].type == uridAtomString)
appClassName = (const char*)options[i].value;
else
d_stderr("Host provides UI scale factor but has wrong value type");
}
}
}

Expand All @@ -614,7 +630,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,

return new UiLv2(bundlePath, winId, options, uridMap, features,
controller, writeFunction, widget, instance,
sampleRate, scaleFactor, bgColor, fgColor);
sampleRate, scaleFactor, bgColor, fgColor, appClassName);
}

#define uiPtr ((UiLv2*)ui)
Expand Down Expand Up @@ -715,4 +731,199 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
return (index == 0) ? &sLv2UiDescriptor : nullptr;
}

#if defined(__MOD_DEVICES__) && defined(__EMSCRIPTEN__)
#include <emscripten/html5.h>
#include <string>

typedef void (*_custom_param_set)(uint32_t port_index, float value);
typedef void (*_custom_patch_set)(const char* uri, const char* value);

struct ModguiHandle {
LV2UI_Handle handle;
long loop_id;
_custom_param_set param_set;
_custom_patch_set patch_set;
};

enum URIs {
kUriNull,
kUriAtomEventTransfer,
kUriDpfKeyValue,
};

static std::vector<std::string> kURIs;

static LV2_URID lv2_urid_map(LV2_URID_Map_Handle, const char* const uri)
{
for (size_t i=0, size=kURIs.size(); i<size; ++i)
{
if (kURIs[i] == uri)
return i;
}

kURIs.push_back(uri);
return kURIs.size() - 1u;
}

static const char* lv2_urid_unmap(LV2_URID_Map_Handle, const LV2_URID urid)
{
return kURIs[urid].c_str();
}

static void lv2ui_write_function(LV2UI_Controller controller,
uint32_t port_index,
uint32_t buffer_size,
uint32_t port_protocol,
const void* buffer)
{
DISTRHO_SAFE_ASSERT_RETURN(buffer_size >= 1,);

// d_stdout("lv2ui_write_function %p %u %u %u %p", controller, port_index, buffer_size, port_protocol, buffer);
ModguiHandle* const mhandle = static_cast<ModguiHandle*>(controller);

switch (port_protocol)
{
case kUriNull:
mhandle->param_set(port_index, *static_cast<const float*>(buffer));
break;
case kUriAtomEventTransfer:
if (const LV2_Atom* const atom = static_cast<const LV2_Atom*>(buffer))
{
// d_stdout("lv2ui_write_function %u %u:%s", atom->size, atom->type, kURIs[atom->type].c_str());

// if (kURIs[atom->type] == "urn:distrho:KeyValueState")
{
const char* const key = (const char*)(atom + 1);
const char* const value = key + (std::strlen(key) + 1U);
// d_stdout("lv2ui_write_function %s %s", key, value);

String urikey;
urikey = DISTRHO_PLUGIN_URI "#";
urikey += key;

mhandle->patch_set(urikey, value);
}
}
break;
}
}

static void app_idle(void* const handle)
{
static_cast<UiLv2*>(handle)->lv2ui_idle();
}

DISTRHO_PLUGIN_EXPORT
LV2UI_Handle modgui_init(const char* const className, _custom_param_set param_set, _custom_patch_set patch_set)
{
d_stdout("init \"%s\"", className);
DISTRHO_SAFE_ASSERT_RETURN(className != nullptr, nullptr);

static LV2_URID_Map uridMap = { nullptr, lv2_urid_map };
static LV2_URID_Unmap uridUnmap = { nullptr, lv2_urid_unmap };

// known first URIDs, matching URIs
if (kURIs.empty())
{
kURIs.push_back("");
kURIs.push_back("http://lv2plug.in/ns/ext/atom#eventTransfer");
kURIs.push_back(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState");
}

static float sampleRateValue = 48000.f;
static LV2_Options_Option options[3] = {
{
LV2_OPTIONS_INSTANCE,
0,
uridMap.map(uridMap.handle, LV2_PARAMETERS__sampleRate),
sizeof(float),
uridMap.map(uridMap.handle, LV2_ATOM__Float),
&sampleRateValue
},
{
LV2_OPTIONS_INSTANCE,
0,
uridMap.map(uridMap.handle, "urn:distrho:className"),
std::strlen(className) + 1,
uridMap.map(uridMap.handle, LV2_ATOM__String),
className
},
{}
};

static const LV2_Feature optionsFt = { LV2_OPTIONS__options, static_cast<void*>(options) };
static const LV2_Feature uridMapFt = { LV2_URID__map, static_cast<void*>(&uridMap) };
static const LV2_Feature uridUnmapFt = { LV2_URID__unmap, static_cast<void*>(&uridUnmap) };

static const LV2_Feature* features[] = {
&optionsFt,
&uridMapFt,
&uridUnmapFt,
nullptr
};

ModguiHandle* const mhandle = new ModguiHandle;
mhandle->handle = nullptr;
mhandle->loop_id = 0;
mhandle->param_set = param_set;
mhandle->patch_set = patch_set;

LV2UI_Widget widget;
const LV2UI_Handle handle = lv2ui_instantiate(&sLv2UiDescriptor,
DISTRHO_PLUGIN_URI,
"", // bundlePath
lv2ui_write_function,
mhandle,
&widget,
features);
mhandle->handle = handle;

static_cast<UiLv2*>(handle)->lv2ui_show();
mhandle->loop_id = emscripten_set_interval(app_idle, 1000.0/60, handle);

return mhandle;
}

DISTRHO_PLUGIN_EXPORT
void modgui_param_set(const LV2UI_Handle handle, const uint32_t index, const float value)
{
lv2ui_port_event(static_cast<ModguiHandle*>(handle)->handle, index, sizeof(float), kUriNull, &value);
}

DISTRHO_PLUGIN_EXPORT
void modgui_patch_set(const LV2UI_Handle handle, const char* const uri, const char* const value)
{
static const constexpr uint32_t URI_PREFIX_LEN = sizeof(DISTRHO_PLUGIN_URI);
DISTRHO_SAFE_ASSERT_RETURN(std::strncmp(uri, DISTRHO_PLUGIN_URI "#", URI_PREFIX_LEN) == 0,);

const uint32_t keySize = std::strlen(uri + URI_PREFIX_LEN) + 1;
const uint32_t valueSize = std::strlen(value) + 1;
const uint32_t atomSize = sizeof(LV2_Atom) + keySize + valueSize;

LV2_Atom* const atom = static_cast<LV2_Atom*>(std::malloc(atomSize));
atom->size = atomSize;
atom->type = kUriDpfKeyValue;

std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)), uri + URI_PREFIX_LEN, keySize);
std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)) + keySize, value, valueSize);

lv2ui_port_event(static_cast<ModguiHandle*>(handle)->handle,
DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS, // events input port
atomSize, kUriAtomEventTransfer, atom);

std::free(atom);
}

DISTRHO_PLUGIN_EXPORT
void modgui_cleanup(const LV2UI_Handle handle)
{
d_stdout("cleanup");
ModguiHandle* const mhandle = static_cast<ModguiHandle*>(handle);
if (mhandle->loop_id != 0)
emscripten_clear_interval(mhandle->loop_id);
lv2ui_cleanup(mhandle->handle);
delete mhandle;
}
#endif

// -----------------------------------------------------------------------
89 changes: 54 additions & 35 deletions dpf/distrho/src/DistrhoUIPrivateData.hpp
Expand Up @@ -32,6 +32,11 @@
# include "../../dgl/src/pugl.hpp"
#endif

#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include <map>
# include <string>
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_UI_IS_STANDALONE 1
#else
Expand Down Expand Up @@ -60,7 +65,7 @@ struct PluginApplication
DGL_NAMESPACE::IdleCallback* idleCallback;
UI* ui;

explicit PluginApplication()
explicit PluginApplication(const char*)
: idleCallback(nullptr),
ui(nullptr) {}

Expand Down Expand Up @@ -105,20 +110,26 @@ struct PluginApplication
class PluginApplication : public DGL_NAMESPACE::Application
{
public:
explicit PluginApplication()
explicit PluginApplication(const char* className)
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE)
{
#ifndef DISTRHO_OS_WASM
const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
#if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__)
if (className == nullptr)
{
className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
}
setClassName(className);
#endif
#else
// unused
(void)className;
#endif
}

void triggerIdleCallbacks()
Expand Down Expand Up @@ -320,9 +331,10 @@ struct UI::PrivateData {
uint fgColor;
double scaleFactor;
uintptr_t winId;
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
char* uiStateFileKeyRequest;
#endif
std::map<std::string,std::string> lastUsedDirnames;
#endif
char* bundlePath;

// Ignore initial resize events while initializing
Expand All @@ -337,8 +349,8 @@ struct UI::PrivateData {
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData() noexcept
: app(),
PrivateData(const char* const appClassName) noexcept
: app(appClassName),
window(nullptr),
sampleRate(0),
parameterOffset(0),
Expand All @@ -347,9 +359,9 @@ struct UI::PrivateData {
fgColor(0xffffffff),
scaleFactor(1.0),
winId(0),
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uiStateFileKeyRequest(nullptr),
#endif
#endif
bundlePath(nullptr),
initializing(true),
callbacksPtr(nullptr),
Expand All @@ -360,32 +372,32 @@ struct UI::PrivateData {
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
#if DISTRHO_PLUGIN_WANT_LATENCY
parameterOffset += 1;
# endif
#endif
#endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
#ifdef DISTRHO_PLUGIN_TARGET_LV2
#if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
#endif
#if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
#endif
#endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_VST3
#ifdef DISTRHO_PLUGIN_TARGET_VST3
parameterOffset += kVst3InternalParameterCount;
#endif
#endif
}

~PrivateData() noexcept
{
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
std::free(uiStateFileKeyRequest);
#endif
#endif
std::free(bundlePath);
}

Expand Down Expand Up @@ -438,7 +450,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)
if (fileRequestCallbackFunc != nullptr)
return fileRequestCallbackFunc(callbacksPtr, key);

#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
std::free(uiStateFileKeyRequest);
uiStateFileKeyRequest = strdup(key);
DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false);
Expand All @@ -449,8 +461,10 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)

DGL_NAMESPACE::FileBrowserOptions opts;
opts.title = title;
if (lastUsedDirnames.count(key))
opts.startDir = lastUsedDirnames[key].c_str();
return window->openFileBrowser(opts);
#endif
#endif

return false;
}
Expand All @@ -466,16 +480,21 @@ inline void PluginWindow::onFileSelected(const char* const filename)
if (initializing)
return;

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
if (char* const key = ui->uiData->uiStateFileKeyRequest)
{
ui->uiData->uiStateFileKeyRequest = nullptr;
if (filename != nullptr)
{
// notify DSP
ui->setState(key, filename);

// notify UI
ui->stateChanged(key, filename);

// save dirname for next time
if (const char* const lastsep = std::strrchr(filename, DISTRHO_OS_SEP))
ui->uiData->lastUsedDirnames[key] = std::string(filename, lastsep-filename);
}
std::free(key);
return;
Expand Down
2 changes: 1 addition & 1 deletion dpf/distrho/src/DistrhoUIVST3.cpp
Expand Up @@ -233,7 +233,7 @@ class NativeIdleHelper
HWND fTimerWindow;
String fTimerWindowClassName;

WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD)
static void WINAPI platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD)
{
reinterpret_cast<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback();
}
Expand Down
21 changes: 20 additions & 1 deletion dpf/distrho/src/jackbridge/JackBridge.cpp
Expand Up @@ -934,8 +934,27 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options,
delete nativeBridge;
#endif
#endif

if (status != nullptr)
*status = JackServerError;
{
int err = JackServerError;

#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT))
if (nativeBridge != nullptr)
{
err
#ifdef HAVE_JACK
|=
#else
=
#endif
JackBridgeNativeFailed;
}
#endif

*status = static_cast<jack_status_t>(err);
}

return nullptr;
}

Expand Down
3 changes: 2 additions & 1 deletion dpf/distrho/src/jackbridge/JackBridge.hpp
Expand Up @@ -126,7 +126,8 @@ enum JackStatus {
JackShmFailure = 0x0200,
JackVersionError = 0x0400,
JackBackendError = 0x0800,
JackClientZombie = 0x1000
JackClientZombie = 0x1000,
JackBridgeNativeFailed = 0x10000
};

enum JackLatencyCallbackMode {
Expand Down
97 changes: 63 additions & 34 deletions dpf/distrho/src/jackbridge/NativeBridge.hpp
@@ -1,6 +1,6 @@
/*
* Native Bridge for DPF
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2023 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
Expand All @@ -21,6 +21,18 @@

#include "../../extra/RingBuffer.hpp"

#if DISTRHO_PLUGIN_NUM_INPUTS > 2
# define DISTRHO_PLUGIN_NUM_INPUTS_2 2
#else
# define DISTRHO_PLUGIN_NUM_INPUTS_2 DISTRHO_PLUGIN_NUM_INPUTS
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 2
# define DISTRHO_PLUGIN_NUM_OUTPUTS_2 2
#else
# define DISTRHO_PLUGIN_NUM_OUTPUTS_2 DISTRHO_PLUGIN_NUM_OUTPUTS
#endif

using DISTRHO_NAMESPACE::HeapRingBuffer;

struct NativeBridge {
Expand All @@ -31,6 +43,8 @@ struct NativeBridge {
// Port caching information
uint numAudioIns;
uint numAudioOuts;
uint numCvIns;
uint numCvOuts;
uint numMidiIns;
uint numMidiOuts;

Expand All @@ -43,9 +57,10 @@ struct NativeBridge {
// Runtime buffers
enum PortMask {
kPortMaskAudio = 0x1000,
kPortMaskMIDI = 0x2000,
kPortMaskInput = 0x4000,
kPortMaskOutput = 0x8000,
kPortMaskCV = 0x2000,
kPortMaskMIDI = 0x4000,
kPortMaskInput = 0x10000,
kPortMaskOutput = 0x20000,
kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI,
kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI,
};
Expand All @@ -71,6 +86,8 @@ struct NativeBridge {
sampleRate(0),
numAudioIns(0),
numAudioOuts(0),
numCvIns(0),
numCvOuts(0),
numMidiIns(0),
numMidiOuts(0),
jackProcessCallback(nullptr),
Expand Down Expand Up @@ -211,27 +228,27 @@ struct NativeBridge {

if (audio)
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)];
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)];

for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
audioBuffers[i] = audioBufferStorage + (bufferSize * i);
#endif
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
audioBuffers[i] = audioBufferStorage + (bufferSize * i);
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS);
#endif
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS);
#endif
}

if (midi)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512);
midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512);
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.createBuffer(2048);
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512);
midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512);
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.createBuffer(2048);
#endif
}
}

Expand All @@ -252,27 +269,39 @@ struct NativeBridge {

jack_port_t* registerPort(const char* const type, const ulong flags)
{
bool isAudio, isInput;

/**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0)
isAudio = true;
else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0)
isAudio = false;
else
return nullptr;
uintptr_t ret = 0;

/**/ if (flags & JackPortIsInput)
isInput = true;
ret |= kPortMaskInput;
else if (flags & JackPortIsOutput)
isInput = false;
ret |= kPortMaskOutput;
else
return nullptr;

const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI)
| (isInput ? kPortMaskInput : kPortMaskOutput);
/**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0)
{
if (flags & JackPortIsControlVoltage)
{
ret |= kPortMaskAudio;
ret += flags & JackPortIsInput ? numAudioIns++ : numAudioOuts++;
}
else
{
ret |= kPortMaskCV;
ret += flags & JackPortIsInput ? numCvIns++ : numCvOuts++;
}
}
else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0)
{
ret |= kPortMaskMIDI;
ret += flags & JackPortIsInput ? numMidiIns++ : numMidiOuts++;
}
else
{
return nullptr;
}

return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++)
: (isInput ? numMidiIns++ : numMidiOuts++)));
return (jack_port_t*)ret;
}

void* getPortBuffer(jack_port_t* const port)
Expand All @@ -281,7 +310,7 @@ struct NativeBridge {
DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr);

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (portMask & kPortMaskAudio)
if (portMask & (kPortMaskAudio|kPortMaskCV))
return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)];
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
Expand Down
38 changes: 26 additions & 12 deletions dpf/distrho/src/jackbridge/RtAudioBridge.hpp
Expand Up @@ -28,9 +28,9 @@
# define RTAUDIO_API_TYPE MACOSX_CORE
# define RTMIDI_API_TYPE MACOSX_CORE
#elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER)
# define __WINDOWS_DS__
# define __WINDOWS_WASAPI__
# define __WINDOWS_MM__
# define RTAUDIO_API_TYPE WINDOWS_DS
# define RTAUDIO_API_TYPE WINDOWS_WASAPI
# define RTMIDI_API_TYPE WINDOWS_MM
#else
# if defined(HAVE_PULSEAUDIO)
Expand All @@ -50,7 +50,9 @@
# include "rtmidi/RtMidi.h"
# include "../../extra/ScopedPointer.hpp"
# include "../../extra/String.hpp"
# include "../../extra/ScopedDenormalDisable.hpp"

using DISTRHO_NAMESPACE::ScopedDenormalDisable;
using DISTRHO_NAMESPACE::ScopedPointer;
using DISTRHO_NAMESPACE::String;

Expand Down Expand Up @@ -281,13 +283,20 @@ struct RtAudioBridge : NativeBridge {
return ok;
}

bool _open(const bool withInput)
bool _open(const bool withInput, RtAudio* tryingAgain = nullptr)
{
ScopedPointer<RtAudio> rtAudio;

try {
rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE);
} DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false);
if (tryingAgain == nullptr)
{
try {
rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE);
} DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false);
}
else
{
rtAudio = tryingAgain;
}

uint rtAudioBufferFrames = nextBufferSize;

Expand All @@ -300,15 +309,15 @@ struct RtAudioBridge : NativeBridge {
if (withInput)
{
inParams.deviceId = rtAudio->getDefaultInputDevice();
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS;
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS_2;
inParamsPtr = &inParams;
}
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
RtAudio::StreamParameters outParams;
outParams.deviceId = rtAudio->getDefaultOutputDevice();
outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS;
outParams.deviceId = tryingAgain != nullptr ? 1 : rtAudio->getDefaultOutputDevice();
outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS_2;
RtAudio::StreamParameters* const outParamsPtr = &outParams;
#else
RtAudio::StreamParameters* const outParamsPtr = nullptr;
Expand All @@ -330,6 +339,10 @@ struct RtAudioBridge : NativeBridge {
rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames,
RtAudioCallback, this, &opts, nullptr);
} catch (const RtAudioError& err) {
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (outParams.deviceId == 0 && rtAudio->getDeviceCount() > 1)
return _open(withInput, rtAudio.release());
#endif
d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
return false;
} DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false);
Expand Down Expand Up @@ -357,26 +370,27 @@ struct RtAudioBridge : NativeBridge {
if (self->jackProcessCallback == nullptr)
{
if (outputBuffer != nullptr)
std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS);
std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS_2);
return 0;
}

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
if (float* const insPtr = static_cast<float*>(inputBuffer))
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i)
self->audioBuffers[i] = insPtr + (i * numFrames);
}
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (float* const outsPtr = static_cast<float*>(outputBuffer))
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames);
}
#endif

const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);

return 0;
Expand Down
73 changes: 44 additions & 29 deletions dpf/distrho/src/jackbridge/SDL2Bridge.hpp
@@ -1,6 +1,6 @@
/*
* SDL Bridge for DPF
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2023 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
Expand All @@ -18,6 +18,7 @@
#define SDL_BRIDGE_HPP_INCLUDED

#include "NativeBridge.hpp"
#include "../../extra/ScopedDenormalDisable.hpp"

#include <SDL.h>

Expand Down Expand Up @@ -67,19 +68,20 @@ struct SDL2Bridge : NativeBridge {

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure");
requested.channels = DISTRHO_PLUGIN_NUM_INPUTS;
requested.channels = DISTRHO_PLUGIN_NUM_INPUTS_2;
requested.callback = AudioInputCallback;

SDL_AudioSpec receivedCapture;
captureDeviceId = SDL_OpenAudioDevice(nullptr, 1, &requested, &receivedCapture,
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
if (captureDeviceId == 0)
{
d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError());
d_stderr2("Failed to open SDL capture device, error was: %s", SDL_GetError());
#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
return false;
#endif
}

if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS)
else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS_2)
{
SDL_CloseAudioDevice(captureDeviceId);
captureDeviceId = 0;
Expand All @@ -91,7 +93,7 @@ struct SDL2Bridge : NativeBridge {
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
SDL_AudioSpec receivedPlayback;
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback");
requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS;
requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS_2;
requested.callback = AudioOutputCallback;

playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback,
Expand All @@ -102,7 +104,7 @@ struct SDL2Bridge : NativeBridge {
return false;
}

if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS)
if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS_2)
{
SDL_CloseAudioDevice(playbackDeviceId);
playbackDeviceId = 0;
Expand All @@ -113,30 +115,39 @@ struct SDL2Bridge : NativeBridge {

#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
// if using both input and output, make sure they match
if (receivedCapture.samples != receivedPlayback.samples)
if (receivedCapture.samples != receivedPlayback.samples && captureDeviceId != 0)
{
SDL_CloseAudioDevice(captureDeviceId);
SDL_CloseAudioDevice(playbackDeviceId);
captureDeviceId = playbackDeviceId = 0;
d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedCapture.samples);
d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedPlayback.samples);
return false;
}
if (receivedCapture.freq != receivedPlayback.freq)
if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0)
{
SDL_CloseAudioDevice(captureDeviceId);
SDL_CloseAudioDevice(playbackDeviceId);
captureDeviceId = playbackDeviceId = 0;
d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedCapture.freq);
d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedPlayback.freq);
return false;
}
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
bufferSize = receivedCapture.samples;
sampleRate = receivedCapture.freq;
#else
bufferSize = receivedPlayback.samples;
sampleRate = receivedPlayback.freq;
if (captureDeviceId != 0)
{
bufferSize = receivedCapture.samples;
sampleRate = receivedCapture.freq;
}
#endif
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
else
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
{
bufferSize = receivedPlayback.samples;
sampleRate = receivedPlayback.freq;
}
#endif

allocBuffers(true, false);
Expand All @@ -146,9 +157,11 @@ struct SDL2Bridge : NativeBridge {
bool close() override
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false);
SDL_CloseAudioDevice(captureDeviceId);
captureDeviceId = 0;
if (captureDeviceId != 0)
{
SDL_CloseAudioDevice(captureDeviceId);
captureDeviceId = 0;
}
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
Expand All @@ -163,8 +176,8 @@ struct SDL2Bridge : NativeBridge {
bool activate() override
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false);
SDL_PauseAudioDevice(captureDeviceId, 0);
if (captureDeviceId != 0)
SDL_PauseAudioDevice(captureDeviceId, 0);
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
Expand All @@ -176,8 +189,8 @@ struct SDL2Bridge : NativeBridge {
bool deactivate() override
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false);
SDL_PauseAudioDevice(captureDeviceId, 1);
if (captureDeviceId != 0)
SDL_PauseAudioDevice(captureDeviceId, 1);
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
Expand All @@ -198,19 +211,20 @@ struct SDL2Bridge : NativeBridge {
if (self->jackProcessCallback == nullptr)
return;

const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS);
const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS_2);
DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);

const float* const fstream = (const float*)stream;

for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i)
{
for (uint j=0; j<numFrames; ++j)
self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i];
self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS_2 + i];
}

#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
// if there are no outputs, run process callback now
const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);
#endif
}
Expand All @@ -231,17 +245,18 @@ struct SDL2Bridge : NativeBridge {
return;
}

const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS);
const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS_2);
DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);

const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);

float* const fstream = (float*)stream;

for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
{
for (uint j=0; j < numFrames; ++j)
fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j];
fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS_2 + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j];
}
}
#endif
Expand Down
48 changes: 34 additions & 14 deletions dpf/distrho/src/jackbridge/WebBridge.hpp
Expand Up @@ -163,7 +163,7 @@ struct WebBridge : NativeBridge {
if (WAB.audioContext.state === 'suspended')
WAB.audioContext.resume();
});
}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this);
}, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, bufferSize, audioBufferStorage, WebAudioCallback, this);

return true;
}
Expand Down Expand Up @@ -217,11 +217,11 @@ struct WebBridge : NativeBridge {
constraints['audio'] = true;
constraints['video'] = false;
constraints['autoGainControl'] = {};
constraints['autoGainControl']['exact'] = false;
constraints['autoGainControl']['ideal'] = false;
constraints['echoCancellation'] = {};
constraints['echoCancellation']['exact'] = false;
constraints['echoCancellation']['ideal'] = false;
constraints['noiseSuppression'] = {};
constraints['noiseSuppression']['exact'] = false;
constraints['noiseSuppression']['ideal'] = false;
constraints['channelCount'] = {};
constraints['channelCount']['min'] = 0;
constraints['channelCount']['ideal'] = numInputs;
Expand All @@ -236,6 +236,25 @@ struct WebBridge : NativeBridge {
constraints['googAutoGainControl'] = false;

var success = function(stream) {
var track = stream.getAudioTracks()[0];

// try to force as much as we can
track.applyConstraints({'autoGainControl': { 'exact': false } })
.then(function(){console.log("Mic/Input auto-gain control has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input auto-gain")});

track.applyConstraints({'echoCancellation': { 'exact': false } })
.then(function(){console.log("Mic/Input echo-cancellation has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input echo-cancellation")});

track.applyConstraints({'noiseSuppression': { 'exact': false } })
.then(function(){console.log("Mic/Input noise-suppression has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input noise-suppression")});

track.applyConstraints({'googAutoGainControl': { 'exact': false } })
.then(function(){})
.catch(function(){});

WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream);
WAB.captureStreamNode.connect(WAB.processor);
};
Expand All @@ -247,7 +266,7 @@ struct WebBridge : NativeBridge {
} else if (navigator.webkitGetUserMedia !== undefined) {
navigator.webkitGetUserMedia(constraints, success, fail);
}
}, DISTRHO_PLUGIN_NUM_INPUTS);
}, DISTRHO_PLUGIN_NUM_INPUTS_2);

return true;
}
Expand Down Expand Up @@ -279,7 +298,7 @@ struct WebBridge : NativeBridge {
WAB.captureStreamNode.disconnect(WAB.processor);

return 1;
}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, newBufferSize) != 0;
}, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, newBufferSize) != 0;

if (!success)
return false;
Expand All @@ -292,9 +311,10 @@ struct WebBridge : NativeBridge {
bufferSizeCallback(newBufferSize, jackBufferSizeArg);

EM_ASM({
var numInputs = $0;
var numOutputs = $1;
var bufferSize = $2;
var numInputsR = $0;
var numInputs = $1;
var numOutputs = $2;
var bufferSize = $3;
var WAB = Module['WebAudioBridge'];

// store the new processor
Expand All @@ -309,13 +329,13 @@ struct WebBridge : NativeBridge {
var buffer = e['inputBuffer']['getChannelData'](i);
for (var j = 0; j < bufferSize; ++j) {
// setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float');
HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j];
HEAPF32[$4 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j];
}
}
dynCall('vi', $4, [$5]);
dynCall('vi', $5, [$6]);
for (var i = 0; i < numOutputs; ++i) {
var buffer = e['outputBuffer']['getChannelData'](i);
var offset = bufferSize * (numInputs + i);
var offset = bufferSize * (numInputsR + i);
for (var j = 0; j < bufferSize; ++j) {
buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2];
}
Expand All @@ -329,7 +349,7 @@ struct WebBridge : NativeBridge {
if (WAB.captureStreamNode)
WAB.captureStreamNode.connect(WAB.processor);

}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this);
}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, bufferSize, audioBufferStorage, WebAudioCallback, this);

return true;
}
Expand Down Expand Up @@ -452,7 +472,7 @@ struct WebBridge : NativeBridge {
}
else
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
std::memset(self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i], 0, sizeof(float)*numFrames);
}
}
Expand Down
7 changes: 2 additions & 5 deletions dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp
Expand Up @@ -4640,13 +4640,10 @@ void RtApiWasapi::stopStream( void )
stream_.state = STREAM_STOPPING;

// wait until stream thread is stopped
while( stream_.state != STREAM_STOPPED ) {
Sleep( 1 );
for (int i=0; i < 2 && stream_.state != STREAM_STOPPED; ++i ) {
Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );
}

// Wait for the last buffer to play before stopping.
Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );

// close thread handle
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";
Expand Down
6 changes: 4 additions & 2 deletions dpf/utils/generate-ttl.sh
Expand Up @@ -33,7 +33,9 @@ FOLDERS=`find . -type d -name \*.lv2`

for i in ${FOLDERS}; do
cd ${i}
FILE="$(ls *.${EXT} | sort | head -n 1)"
${EXE_WRAPPER} "${GEN}" "./${FILE}"
FILE="$(ls *.${EXT} 2>/dev/null | sort | head -n 1)"
if [ -n "${FILE}" ]; then
${EXE_WRAPPER} "${GEN}" "./${FILE}"
fi
cd ..
done