Support UI in LV2
This commit is contained in:
parent
e2b8b21967
commit
b92209de74
17 changed files with 966 additions and 43 deletions
|
|
@ -15,7 +15,8 @@ buildenv make DESTDIR=${PWD}/${INSTALL_DIR} install
|
|||
|
||||
# Bundle LV2 dependencies
|
||||
cd "${INSTALL_DIR}"/Library/Audio/Plug-Ins/LV2
|
||||
dylibbundler -od -b -x sfizz.lv2/sfizz.so -d sfizz.lv2/libs/ -p @loader_path/libs/
|
||||
dylibbundler -od -b -x sfizz.lv2/Contents/Binary/sfizz.so -d sfizz.lv2/Contents/libs/ -p @loader_path/../libs/
|
||||
dylibbundler -od -b -x sfizz.lv2/Contents/Binary/sfizz_ui.so -d sfizz.lv2/Contents/libs/ -p @loader_path/../libs/
|
||||
cd "${TRAVIS_BUILD_DIR}/build"
|
||||
|
||||
# Bundle VST3 dependencies
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
set(VSTGUI_BASEDIR "${CMAKE_CURRENT_SOURCE_DIR}/external/vstgui4")
|
||||
include("cmake/Vstgui.cmake")
|
||||
|
||||
set(EDITOR_RESOURCES
|
||||
logo.png
|
||||
PARENT_SCOPE)
|
||||
|
||||
# editor
|
||||
add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
|
||||
src/editor/EditIds.h
|
||||
|
|
@ -13,4 +17,5 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
|
|||
src/editor/utility/vstgui_after.h
|
||||
src/editor/utility/vstgui_before.h)
|
||||
target_include_directories(sfizz_editor PUBLIC "src")
|
||||
target_link_libraries(sfizz_editor PRIVATE sfizz-vstgui absl::strings)
|
||||
target_link_libraries(sfizz_editor PRIVATE sfizz-vstgui)
|
||||
target_link_libraries(sfizz_editor PUBLIC absl::strings absl::variant)
|
||||
|
|
|
|||
|
|
@ -207,3 +207,14 @@ endif()
|
|||
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
|
||||
target_compile_definitions(sfizz-vstgui PRIVATE "RELEASE")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(sfizz-vstgui PRIVATE
|
||||
"-Wno-deprecated-copy"
|
||||
"-Wno-ignored-qualifiers"
|
||||
"-Wno-reorder"
|
||||
"-Wno-sign-compare"
|
||||
"-Wno-unused-function"
|
||||
"-Wno-unused-parameter"
|
||||
"-Wno-unused-variable")
|
||||
endif()
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
|
@ -288,7 +288,7 @@ void Editor::Impl::createFrameContents()
|
|||
|
||||
view->setBackgroundColor(CColor(0xff, 0xff, 0xff));
|
||||
|
||||
SharedPointer<CBitmap> logo = new CBitmap("logo.png");
|
||||
SharedPointer<CBitmap> logo { new CBitmap("logo.png") };
|
||||
|
||||
CRect bottomRow = bounds;
|
||||
bottomRow.top = bottomRow.bottom - 30;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ set (LV2PLUGIN_TTL_SRC_FILES
|
|||
manifest.ttl.in
|
||||
${PROJECT_NAME}.ttl.in
|
||||
)
|
||||
if (SFIZZ_LV2_UI)
|
||||
list(APPEND LV2PLUGIN_TTL_SRC_FILES
|
||||
${PROJECT_NAME}_ui.ttl.in)
|
||||
endif()
|
||||
source_group("Turtle Files" FILES
|
||||
${LV2PLUGIN_TTL_SRC_FILES}
|
||||
)
|
||||
|
|
@ -19,31 +23,61 @@ add_library (${LV2PLUGIN_PRJ_NAME} MODULE
|
|||
atomic_compat.h
|
||||
${LV2PLUGIN_TTL_SRC_FILES})
|
||||
target_link_libraries (${LV2PLUGIN_PRJ_NAME} ${PROJECT_NAME}::${PROJECT_NAME})
|
||||
|
||||
if (SFIZZ_LV2_UI)
|
||||
add_library (${LV2PLUGIN_PRJ_NAME}_ui MODULE
|
||||
${PROJECT_NAME}_ui.cpp
|
||||
vstgui_helpers.h
|
||||
vstgui_helpers.cpp)
|
||||
target_link_libraries (${LV2PLUGIN_PRJ_NAME}_ui sfizz_editor sfizz-vstgui)
|
||||
endif()
|
||||
|
||||
# Explicitely strip all symbols on Linux but lv2_descriptor()
|
||||
# MacOS linker does not support this apparently https://bugs.webkit.org/show_bug.cgi?id=144555
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
file(COPY lv2.version DESTINATION ${CMAKE_BINARY_DIR}/lv2)
|
||||
target_link_libraries(${LV2PLUGIN_PRJ_NAME} "-Wl,--version-script=lv2.version")
|
||||
# target_link_libraries(${LV2PLUGIN_PRJ_NAME} "-Wl,-u,lv2_descriptor")
|
||||
if (SFIZZ_LV2_UI)
|
||||
file(COPY lv2ui.version DESTINATION ${CMAKE_BINARY_DIR}/lv2)
|
||||
target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui "-Wl,--version-script=lv2ui.version")
|
||||
# target_link_libraries(${LV2PLUGIN_PRJ_NAME}_ui "-Wl,-u,lv2ui_descriptor")
|
||||
endif()
|
||||
endif()
|
||||
target_include_directories(${LV2PLUGIN_PRJ_NAME} PRIVATE . external/ardour)
|
||||
sfizz_enable_lto_if_needed (${LV2PLUGIN_PRJ_NAME})
|
||||
if (MINGW)
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES LINK_FLAGS "-static")
|
||||
endif()
|
||||
if (SFIZZ_LV2_UI)
|
||||
target_include_directories(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE . external/ardour)
|
||||
sfizz_enable_lto_if_needed (${LV2PLUGIN_PRJ_NAME}_ui)
|
||||
if (MINGW)
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES LINK_FLAGS "-static")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Remove the "lib" prefix, rename the target name and build it in the .lv build dir
|
||||
# <build_dir>/lv2/<plugin_name>_lv2.<ext> to
|
||||
# <build_dir>/lv2/<plugin_name>.lv2/<plugin_name>.<ext>
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES PREFIX "")
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}")
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Contents/Binary")
|
||||
|
||||
if (SFIZZ_LV2_UI)
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES PREFIX "")
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES OUTPUT_NAME "${PROJECT_NAME}_ui")
|
||||
set_target_properties (${LV2PLUGIN_PRJ_NAME}_ui PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Contents/Binary")
|
||||
endif()
|
||||
|
||||
# Generate *.ttl files from *.in sources,
|
||||
# create the destination directory if it doesn't exists and copy needed files
|
||||
file (MAKE_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
configure_file (manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl)
|
||||
configure_file (${PROJECT_NAME}.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ttl)
|
||||
if (SFIZZ_LV2_UI)
|
||||
configure_file (${PROJECT_NAME}_ui.ttl.in ${PROJECT_BINARY_DIR}/${PROJECT_NAME}_ui.ttl)
|
||||
endif()
|
||||
configure_file (LICENSE.md.in ${PROJECT_BINARY_DIR}/LICENSE.md)
|
||||
if (SFIZZ_USE_VCPKG OR SFIZZ_STATIC_DEPENDENCIES OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
file(COPY "lgpl-3.0.txt" DESTINATION ${PROJECT_BINARY_DIR})
|
||||
|
|
@ -54,12 +88,22 @@ set(LV2_RESOURCES
|
|||
DefaultInstrument.sfz
|
||||
DefaultScale.scl)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Resources")
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources")
|
||||
foreach(res ${LV2_RESOURCES})
|
||||
file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${res}"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/Resources")
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/Contents/Resources")
|
||||
endforeach()
|
||||
|
||||
# Copy editor resources
|
||||
if (SFIZZ_LV2_UI)
|
||||
execute_process (
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources")
|
||||
foreach(res ${EDITOR_RESOURCES})
|
||||
file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources/${res}"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/Contents/Resources")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Installation
|
||||
if (NOT MSVC)
|
||||
install (DIRECTORY ${PROJECT_BINARY_DIR} DESTINATION ${LV2PLUGIN_INSTALL_DIR}
|
||||
|
|
|
|||
4
lv2/lv2ui.version
Normal file
4
lv2/lv2ui.version
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
LV2UIABI_1.0 {
|
||||
global: *lv2ui_descriptor*;
|
||||
local: *;
|
||||
};
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
|
||||
<@LV2PLUGIN_URI@>
|
||||
a lv2:Plugin ;
|
||||
lv2:binary <@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
|
||||
lv2:binary <Contents/Binary/@PROJECT_NAME@@CMAKE_SHARED_MODULE_SUFFIX@> ;
|
||||
rdfs:seeAlso <@PROJECT_NAME@.ttl> .
|
||||
|
||||
@LV2PLUGIN_IF_ENABLE_UI@<@LV2PLUGIN_URI@#ui>
|
||||
@LV2PLUGIN_IF_ENABLE_UI@ a ui:@LV2_UI_TYPE@ ;
|
||||
@LV2PLUGIN_IF_ENABLE_UI@ ui:binary <Contents/Binary/@PROJECT_NAME@_ui@CMAKE_SHARED_MODULE_SUFFIX@> ;
|
||||
@LV2PLUGIN_IF_ENABLE_UI@ rdfs:seeAlso <@PROJECT_NAME@_ui.ttl> .
|
||||
|
|
|
|||
61
lv2/sfizz.c
61
lv2/sfizz.c
|
|
@ -32,6 +32,9 @@
|
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sfizz_lv2.h"
|
||||
|
||||
#include <lv2/atom/atom.h>
|
||||
#include <lv2/atom/forge.h>
|
||||
#include <lv2/atom/util.h>
|
||||
#include <lv2/buf-size/buf-size.h>
|
||||
|
|
@ -58,23 +61,11 @@
|
|||
|
||||
#include "atomic_compat.h"
|
||||
|
||||
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
|
||||
#define SFIZZ_PREFIX SFIZZ_URI "#"
|
||||
#define SFIZZ__sfzFile SFIZZ_URI ":" "sfzfile"
|
||||
#define SFIZZ__tuningfile SFIZZ_URI ":" "tuningfile"
|
||||
#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices"
|
||||
#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size"
|
||||
#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling"
|
||||
// These ones are just for the worker
|
||||
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
|
||||
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
|
||||
|
||||
#define CHANNEL_MASK 0x0F
|
||||
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
||||
#define MIDI_STATUS(byte) (byte & ~CHANNEL_MASK)
|
||||
#define PITCH_BUILD_AND_CENTER(first_byte, last_byte) (int)(((unsigned int)last_byte << 7) + (unsigned int)first_byte) - 8192
|
||||
#define MAX_BLOCK_SIZE 8192
|
||||
#define MAX_PATH_SIZE 1024
|
||||
#define MAX_VOICES 256
|
||||
#define DEFAULT_VOICES 64
|
||||
#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1
|
||||
|
|
@ -82,8 +73,8 @@
|
|||
#define LOG_SAMPLE_COUNT 48000
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#define DEFAULT_SCALA_FILE "Resources/DefaultScale.scl"
|
||||
#define DEFAULT_SFZ_FILE "Resources/DefaultInstrument.sfz"
|
||||
#define DEFAULT_SCALA_FILE "Contents/Resources/DefaultScale.scl"
|
||||
#define DEFAULT_SFZ_FILE "Contents/Resources/DefaultInstrument.sfz"
|
||||
// This assumes that the longest path is the default sfz file; if not, change it
|
||||
#define MAX_BUNDLE_PATH_SIZE (MAX_PATH_SIZE - sizeof(DEFAULT_SFZ_FILE))
|
||||
|
||||
|
|
@ -115,6 +106,11 @@ typedef struct
|
|||
const float *tuning_frequency_port;
|
||||
const float *stretch_tuning_port;
|
||||
float *active_voices_port;
|
||||
float *num_curves_port;
|
||||
float *num_masters_port;
|
||||
float *num_groups_port;
|
||||
float *num_regions_port;
|
||||
float *num_samples_port;
|
||||
|
||||
// Atom forge
|
||||
LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread
|
||||
|
|
@ -187,23 +183,6 @@ typedef struct
|
|||
char bundle_path[MAX_BUNDLE_PATH_SIZE];
|
||||
} sfizz_plugin_t;
|
||||
|
||||
enum
|
||||
{
|
||||
SFIZZ_CONTROL = 0,
|
||||
SFIZZ_NOTIFY = 1,
|
||||
SFIZZ_LEFT = 2,
|
||||
SFIZZ_RIGHT = 3,
|
||||
SFIZZ_VOLUME = 4,
|
||||
SFIZZ_POLYPHONY = 5,
|
||||
SFIZZ_OVERSAMPLING = 6,
|
||||
SFIZZ_PRELOAD = 7,
|
||||
SFIZZ_FREEWHEELING = 8,
|
||||
SFIZZ_SCALA_ROOT_KEY = 9,
|
||||
SFIZZ_TUNING_FREQUENCY = 10,
|
||||
SFIZZ_STRETCH_TUNING = 11,
|
||||
SFIZZ_ACTIVE_VOICES = 12,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SFIZZ_TIMEINFO_POSITION = 1 << 0,
|
||||
|
|
@ -369,6 +348,21 @@ connect_port(LV2_Handle instance,
|
|||
case SFIZZ_ACTIVE_VOICES:
|
||||
self->active_voices_port = (float *)data;
|
||||
break;
|
||||
case SFIZZ_NUM_CURVES:
|
||||
self->num_curves_port = (float *)data;
|
||||
break;
|
||||
case SFIZZ_NUM_MASTERS:
|
||||
self->num_masters_port = (float *)data;
|
||||
break;
|
||||
case SFIZZ_NUM_GROUPS:
|
||||
self->num_groups_port = (float *)data;
|
||||
break;
|
||||
case SFIZZ_NUM_REGIONS:
|
||||
self->num_regions_port = (float *)data;
|
||||
break;
|
||||
case SFIZZ_NUM_SAMPLES:
|
||||
self->num_samples_port = (float *)data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -977,6 +971,11 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
sfizz_lv2_check_oversampling(self);
|
||||
sfizz_lv2_check_num_voices(self);
|
||||
*(self->active_voices_port) = sfizz_get_num_active_voices(self->synth);
|
||||
*(self->num_curves_port) = sfizz_get_num_curves(self->synth);
|
||||
*(self->num_masters_port) = sfizz_get_num_masters(self->synth);
|
||||
*(self->num_groups_port) = sfizz_get_num_groups(self->synth);
|
||||
*(self->num_regions_port) = sfizz_get_num_regions(self->synth);
|
||||
*(self->num_samples_port) = sfizz_get_num_preloaded_samples(self->synth);
|
||||
|
||||
// Log the buffer usage
|
||||
self->sample_counter += (int)sample_count;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix state: <http://lv2plug.in/ns/ext/state#> .
|
||||
@prefix time: <http://lv2plug.in/ns/ext/time#> .
|
||||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
|
||||
|
|
@ -83,6 +84,8 @@ midnam:update a lv2:Feature .
|
|||
opts:supportedOption param:sampleRate ;
|
||||
opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ;
|
||||
|
||||
@LV2PLUGIN_IF_ENABLE_UI@ui:ui <@LV2PLUGIN_URI@#ui> ;
|
||||
|
||||
patch:writable <@LV2PLUGIN_URI@:sfzfile> ,
|
||||
<@LV2PLUGIN_URI@:tuningfile> ;
|
||||
|
||||
|
|
@ -310,4 +313,70 @@ midnam:update a lv2:Feature .
|
|||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 256 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 12 ;
|
||||
lv2:symbol "active_voices" ;
|
||||
lv2:name "Active voices",
|
||||
"Voix utilisées"@fr ;
|
||||
pg:group <@LV2PLUGIN_URI@#status> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 256 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 13 ;
|
||||
lv2:symbol "num_curves" ;
|
||||
lv2:name "Number of curves",
|
||||
"Nombre de courbes"@fr ;
|
||||
pg:group <@LV2PLUGIN_URI@#status> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 14 ;
|
||||
lv2:symbol "num_masters" ;
|
||||
lv2:name "Number of masters",
|
||||
"Nombre de maîtres"@fr ;
|
||||
pg:group <@LV2PLUGIN_URI@#status> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 15 ;
|
||||
lv2:symbol "num_groups" ;
|
||||
lv2:name "Number of groups",
|
||||
"Nombre de groupes"@fr ;
|
||||
pg:group <@LV2PLUGIN_URI@#status> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 16 ;
|
||||
lv2:symbol "num_regions" ;
|
||||
lv2:name "Number of regions",
|
||||
"Nombre de regions"@fr ;
|
||||
pg:group <@LV2PLUGIN_URI@#status> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 17 ;
|
||||
lv2:symbol "num_samples" ;
|
||||
lv2:name "Number of samples",
|
||||
"Nombre d'échantillons"@fr ;
|
||||
pg:group <@LV2PLUGIN_URI@#status> ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:default 0 ;
|
||||
lv2:minimum 0 ;
|
||||
lv2:maximum 65535 ;
|
||||
] .
|
||||
|
|
|
|||
43
lv2/sfizz_lv2.h
Normal file
43
lv2/sfizz_lv2.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MAX_PATH_SIZE 1024
|
||||
|
||||
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
|
||||
#define SFIZZ_UI_URI "http://sfztools.github.io/sfizz#ui"
|
||||
#define SFIZZ_PREFIX SFIZZ_URI "#"
|
||||
#define SFIZZ__sfzFile SFIZZ_URI ":" "sfzfile"
|
||||
#define SFIZZ__tuningfile SFIZZ_URI ":" "tuningfile"
|
||||
#define SFIZZ__numVoices SFIZZ_URI ":" "numvoices"
|
||||
#define SFIZZ__preloadSize SFIZZ_URI ":" "preload_size"
|
||||
#define SFIZZ__oversampling SFIZZ_URI ":" "oversampling"
|
||||
// These ones are just for the worker
|
||||
#define SFIZZ__logStatus SFIZZ_URI ":" "log_status"
|
||||
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
|
||||
|
||||
enum
|
||||
{
|
||||
SFIZZ_CONTROL = 0,
|
||||
SFIZZ_NOTIFY = 1,
|
||||
SFIZZ_LEFT = 2,
|
||||
SFIZZ_RIGHT = 3,
|
||||
SFIZZ_VOLUME = 4,
|
||||
SFIZZ_POLYPHONY = 5,
|
||||
SFIZZ_OVERSAMPLING = 6,
|
||||
SFIZZ_PRELOAD = 7,
|
||||
SFIZZ_FREEWHEELING = 8,
|
||||
SFIZZ_SCALA_ROOT_KEY = 9,
|
||||
SFIZZ_TUNING_FREQUENCY = 10,
|
||||
SFIZZ_STRETCH_TUNING = 11,
|
||||
SFIZZ_ACTIVE_VOICES = 12,
|
||||
SFIZZ_NUM_CURVES = 13,
|
||||
SFIZZ_NUM_MASTERS = 14,
|
||||
SFIZZ_NUM_GROUPS = 15,
|
||||
SFIZZ_NUM_REGIONS = 16,
|
||||
SFIZZ_NUM_SAMPLES = 17,
|
||||
};
|
||||
492
lv2/sfizz_ui.cpp
Normal file
492
lv2/sfizz_ui.cpp
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
SPDX-License-Identifier: ISC
|
||||
|
||||
Sfizz LV2 plugin
|
||||
|
||||
Copyright 2019-2020, Paul Ferrand <paul@ferrand.cc>
|
||||
|
||||
This file was based on skeleton and example code from the LV2 plugin
|
||||
distribution available at http://lv2plug.in/
|
||||
|
||||
The LV2 sample plugins have the following copyright and notice, which are
|
||||
extended to the current work:
|
||||
Copyright 2011-2016 David Robillard <d@drobilla.net>
|
||||
Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
|
||||
Copyright 2011 James Morris <jwm.art.net@gmail.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.
|
||||
|
||||
Compiling this plugin statically against libsndfile implies distributing it
|
||||
under the terms of the LGPL v3 license. See the LICENSE.md file for more
|
||||
information. If you did not receive a LICENSE.md file, inform the current
|
||||
maintainer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sfizz_lv2.h"
|
||||
|
||||
#include "editor/Editor.h"
|
||||
#include "editor/EditorController.h"
|
||||
#include "editor/EditIds.h"
|
||||
#include <lv2/ui/ui.h>
|
||||
#include <lv2/atom/atom.h>
|
||||
#include <lv2/atom/forge.h>
|
||||
#include <lv2/atom/util.h>
|
||||
#include <lv2/midi/midi.h>
|
||||
#include <lv2/patch/patch.h>
|
||||
#include <lv2/urid/urid.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include "vstgui_helpers.h"
|
||||
|
||||
#include "editor/utility/vstgui_before.h"
|
||||
#include "vstgui/lib/cframe.h"
|
||||
#include "vstgui/lib/platform/iplatformframe.h"
|
||||
#include "editor/utility/vstgui_after.h"
|
||||
using namespace VSTGUI;
|
||||
|
||||
///
|
||||
struct FrameHolderDeleter {
|
||||
void operator()(CFrame* frame) const
|
||||
{
|
||||
if (frame->getNbReference() != 1)
|
||||
frame->forget();
|
||||
else
|
||||
frame->close();
|
||||
}
|
||||
};
|
||||
typedef std::unique_ptr<CFrame, FrameHolderDeleter> FrameHolder;
|
||||
|
||||
///
|
||||
struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
||||
LV2UI_Write_Function write = nullptr;
|
||||
LV2UI_Controller con = nullptr;
|
||||
LV2_URID_Map *map = nullptr;
|
||||
LV2_URID_Unmap *unmap = nullptr;
|
||||
LV2UI_Resize *resize = nullptr;
|
||||
LV2UI_Touch *touch = nullptr;
|
||||
std::unique_ptr<Editor> editor;
|
||||
FrameHolder uiFrame;
|
||||
#if LINUX
|
||||
SharedPointer<Lv2IdleRunLoop> runLoop;
|
||||
#endif
|
||||
|
||||
/// VSTGUIEditorInterface
|
||||
CFrame* getFrame() const override { return uiFrame.get(); }
|
||||
|
||||
LV2_Atom_Forge atom_forge;
|
||||
LV2_URID atom_event_transfer_uri;
|
||||
LV2_URID atom_object_uri;
|
||||
LV2_URID atom_path_uri;
|
||||
LV2_URID atom_urid_uri;
|
||||
LV2_URID midi_event_uri;
|
||||
LV2_URID patch_get_uri;
|
||||
LV2_URID patch_set_uri;
|
||||
LV2_URID patch_property_uri;
|
||||
LV2_URID patch_value_uri;
|
||||
LV2_URID sfizz_sfz_file_uri;
|
||||
LV2_URID sfizz_scala_file_uri;
|
||||
|
||||
protected:
|
||||
void uiSendValue(EditId id, const EditValue& v) override;
|
||||
void uiBeginSend(EditId id) override;
|
||||
void uiEndSend(EditId id) override;
|
||||
void uiSendMIDI(const uint8_t* msg, uint32_t len) override;
|
||||
|
||||
private:
|
||||
void uiTouch(EditId id, bool t);
|
||||
};
|
||||
|
||||
static LV2UI_Handle
|
||||
instantiate(const LV2UI_Descriptor *descriptor,
|
||||
const char *plugin_uri,
|
||||
const char *bundle_path,
|
||||
LV2UI_Write_Function write_function,
|
||||
LV2UI_Controller controller,
|
||||
LV2UI_Widget *widget,
|
||||
const LV2_Feature * const *features)
|
||||
{
|
||||
std::unique_ptr<sfizz_ui_t> self { new sfizz_ui_t };
|
||||
|
||||
(void)descriptor;
|
||||
(void)plugin_uri;
|
||||
(void)bundle_path;
|
||||
|
||||
self->write = write_function;
|
||||
self->con = controller;
|
||||
|
||||
void *parentWindowId = nullptr;
|
||||
|
||||
LV2_URID_Map *map = nullptr;
|
||||
LV2_URID_Unmap *unmap = nullptr;
|
||||
|
||||
for (const LV2_Feature *const *f = features; *f; f++)
|
||||
{
|
||||
if (!strcmp((**f).URI, LV2_URID__map))
|
||||
self->map = map = (LV2_URID_Map *)(**f).data;
|
||||
else if (!strcmp((**f).URI, LV2_URID__unmap))
|
||||
self->unmap = unmap = (LV2_URID_Unmap *)(**f).data;
|
||||
else if (!strcmp((**f).URI, LV2_UI__resize))
|
||||
self->resize = (LV2UI_Resize *)(**f).data;
|
||||
else if (!strcmp((**f).URI, LV2_UI__touch))
|
||||
self->touch = (LV2UI_Touch*)(**f).data;
|
||||
else if (!strcmp((**f).URI, LV2_UI__parent))
|
||||
parentWindowId = (**f).data;
|
||||
}
|
||||
|
||||
// The map feature is required
|
||||
if (!map || !unmap)
|
||||
return nullptr;
|
||||
|
||||
LV2_Atom_Forge *forge = &self->atom_forge;
|
||||
lv2_atom_forge_init(forge, map);
|
||||
self->atom_event_transfer_uri = map->map(map->handle, LV2_ATOM__eventTransfer);
|
||||
self->atom_object_uri = map->map(map->handle, LV2_ATOM__Object);
|
||||
self->atom_path_uri = map->map(map->handle, LV2_ATOM__Path);
|
||||
self->atom_urid_uri = map->map(map->handle, LV2_ATOM__URID);
|
||||
self->midi_event_uri = map->map(map->handle, LV2_MIDI__MidiEvent);
|
||||
self->patch_get_uri = map->map(map->handle, LV2_PATCH__Get);
|
||||
self->patch_set_uri = map->map(map->handle, LV2_PATCH__Set);
|
||||
self->patch_property_uri = map->map(map->handle, LV2_PATCH__property);
|
||||
self->patch_value_uri = map->map(map->handle, LV2_PATCH__value);
|
||||
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile);
|
||||
|
||||
// set up the resource path
|
||||
// * on Linux, this is determined by going 2 folders back from the SO path
|
||||
// name, and appending "Contents/Resources" (not overridable)
|
||||
// * on Windows, the folder is set programmatically
|
||||
// * on macOS, resource files are looked up using CFBundle APIs
|
||||
#if _WIN32
|
||||
IWin32PlatformFrame::setResourceBasePath((std::string(bundle_path) + "\\Contents\\Resources\\").c_str());
|
||||
#elif __APPLE__
|
||||
#pragma message("TODO: make resources work on macOS using bundles")
|
||||
#endif
|
||||
|
||||
// makes labels refresh correctly
|
||||
CView::kDirtyCallAlwaysOnMainThread = true;
|
||||
|
||||
const CRect uiBounds(0, 0, Editor::viewWidth, Editor::viewHeight);
|
||||
CFrame* uiFrame = new CFrame(uiBounds, self.get());
|
||||
self->uiFrame.reset(uiFrame);
|
||||
|
||||
IPlatformFrameConfig* config = nullptr;
|
||||
#if LINUX
|
||||
SharedPointer<Lv2IdleRunLoop> runLoop = new Lv2IdleRunLoop;
|
||||
self->runLoop = runLoop;
|
||||
VSTGUI::X11::FrameConfig x11Config;
|
||||
x11Config.runLoop = runLoop;
|
||||
config = &x11Config;
|
||||
#endif
|
||||
|
||||
if (!uiFrame->open(parentWindowId, kDefaultNative, config))
|
||||
return nullptr;
|
||||
|
||||
Editor *editor = new Editor(*self);
|
||||
self->editor.reset(editor);
|
||||
editor->open(*uiFrame);
|
||||
|
||||
*widget = reinterpret_cast<LV2UI_Widget>(uiFrame->getPlatformFrame()->getPlatformRepresentation());
|
||||
|
||||
if (self->resize)
|
||||
self->resize->ui_resize(self->resize->handle, Editor::viewWidth, Editor::viewHeight);
|
||||
|
||||
// send a request to receive all parameters
|
||||
uint8_t buffer[256];
|
||||
lv2_atom_forge_set_buffer(forge, buffer, sizeof(buffer));
|
||||
LV2_Atom_Forge_Frame frame;
|
||||
LV2_Atom *msg = (LV2_Atom *)lv2_atom_forge_object(forge, &frame, 0, self->patch_get_uri);
|
||||
lv2_atom_forge_pop(forge, &frame);
|
||||
write_function(controller, 0, lv2_atom_total_size(msg), self->atom_event_transfer_uri, msg);
|
||||
|
||||
return self.release();
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
delete self;
|
||||
}
|
||||
|
||||
static void
|
||||
port_event(LV2UI_Handle ui,
|
||||
uint32_t port_index,
|
||||
uint32_t buffer_size,
|
||||
uint32_t format,
|
||||
const void *buffer)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
|
||||
if (format == 0) {
|
||||
const float v = *reinterpret_cast<const float*>(buffer);
|
||||
|
||||
switch (port_index) {
|
||||
case SFIZZ_VOLUME:
|
||||
self->uiReceiveValue(EditId::Volume, v);
|
||||
break;
|
||||
case SFIZZ_POLYPHONY:
|
||||
self->uiReceiveValue(EditId::Polyphony, v);
|
||||
break;
|
||||
case SFIZZ_OVERSAMPLING:
|
||||
self->uiReceiveValue(EditId::Oversampling, v);
|
||||
break;
|
||||
case SFIZZ_PRELOAD:
|
||||
self->uiReceiveValue(EditId::PreloadSize, v);
|
||||
break;
|
||||
case SFIZZ_SCALA_ROOT_KEY:
|
||||
self->uiReceiveValue(EditId::ScalaRootKey, v);
|
||||
break;
|
||||
case SFIZZ_TUNING_FREQUENCY:
|
||||
self->uiReceiveValue(EditId::TuningFrequency, v);
|
||||
break;
|
||||
case SFIZZ_STRETCH_TUNING:
|
||||
self->uiReceiveValue(EditId::StretchTuning, v);
|
||||
break;
|
||||
case SFIZZ_ACTIVE_VOICES:
|
||||
self->uiReceiveValue(EditId::UINumActiveVoices, v);
|
||||
break;
|
||||
case SFIZZ_NUM_CURVES:
|
||||
self->uiReceiveValue(EditId::UINumCurves, v);
|
||||
break;
|
||||
case SFIZZ_NUM_MASTERS:
|
||||
self->uiReceiveValue(EditId::UINumMasters, v);
|
||||
break;
|
||||
case SFIZZ_NUM_GROUPS:
|
||||
self->uiReceiveValue(EditId::UINumGroups, v);
|
||||
break;
|
||||
case SFIZZ_NUM_REGIONS:
|
||||
self->uiReceiveValue(EditId::UINumRegions, v);
|
||||
break;
|
||||
case SFIZZ_NUM_SAMPLES:
|
||||
self->uiReceiveValue(EditId::UINumPreloadedSamples, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (format == self->atom_event_transfer_uri) {
|
||||
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer);
|
||||
|
||||
if (atom->type == self->atom_object_uri) {
|
||||
const LV2_Atom *prop = nullptr;
|
||||
const LV2_Atom *value = nullptr;
|
||||
|
||||
lv2_atom_object_get(
|
||||
reinterpret_cast<const LV2_Atom_Object *>(atom),
|
||||
self->patch_property_uri, &prop, self->patch_value_uri, &value, 0);
|
||||
|
||||
if (prop && value && prop->type == self->atom_urid_uri) {
|
||||
const LV2_URID prop_uri = reinterpret_cast<const LV2_Atom_URID *>(prop)->body;
|
||||
auto *value_body = reinterpret_cast<const char *>(LV2_ATOM_BODY_CONST(value));
|
||||
|
||||
if (prop_uri == self->sfizz_sfz_file_uri && value->type == self->atom_path_uri) {
|
||||
std::string path(value_body, strnlen(value_body, value->size));
|
||||
self->uiReceiveValue(EditId::SfzFile, path);
|
||||
}
|
||||
else if (prop_uri == self->sfizz_scala_file_uri && value->type == self->atom_path_uri) {
|
||||
std::string path(value_body, strnlen(value_body, value->size));
|
||||
self->uiReceiveValue(EditId::ScalaFile, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)buffer_size;
|
||||
}
|
||||
|
||||
static int
|
||||
idle(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
|
||||
#if LINUX
|
||||
self->runLoop->execIdle();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const LV2UI_Idle_Interface idle_interface = {
|
||||
&idle,
|
||||
};
|
||||
|
||||
static int
|
||||
show(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
self->uiFrame->setVisible(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hide(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
self->uiFrame->setVisible(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const LV2UI_Show_Interface show_interface = {
|
||||
&show,
|
||||
&hide,
|
||||
};
|
||||
|
||||
const void *
|
||||
extension_data(const char *uri)
|
||||
{
|
||||
if (!strcmp(uri, LV2_UI__idleInterface))
|
||||
return &idle_interface;
|
||||
|
||||
if (!strcmp(uri, LV2_UI__showInterface))
|
||||
return &show_interface;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const LV2UI_Descriptor descriptor = {
|
||||
SFIZZ_UI_URI,
|
||||
instantiate,
|
||||
cleanup,
|
||||
port_event,
|
||||
extension_data,
|
||||
};
|
||||
|
||||
LV2_SYMBOL_EXPORT
|
||||
const LV2UI_Descriptor *
|
||||
lv2ui_descriptor(uint32_t index)
|
||||
{
|
||||
#if LINUX
|
||||
VSTGUI::initializeSoHandle();
|
||||
#endif
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return &descriptor;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v)
|
||||
{
|
||||
auto sendFloat = [this](int port, float value) {
|
||||
write(con, port, sizeof(float), 0, &value);
|
||||
};
|
||||
|
||||
auto sendPath = [this](LV2_URID property, const std::string& value) {
|
||||
LV2_Atom_Forge *forge = &atom_forge;
|
||||
LV2_Atom_Forge_Frame frame;
|
||||
alignas(LV2_Atom) uint8_t buffer[MAX_PATH_SIZE + 512];
|
||||
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer);
|
||||
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
|
||||
if (lv2_atom_forge_object(forge, &frame, 0, patch_set_uri) &&
|
||||
lv2_atom_forge_key(forge, patch_property_uri) &&
|
||||
lv2_atom_forge_urid(forge, property) &&
|
||||
lv2_atom_forge_key(forge, patch_value_uri) &&
|
||||
lv2_atom_forge_path(forge, value.data(), value.size()))
|
||||
{
|
||||
lv2_atom_forge_pop(forge, &frame);
|
||||
write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom);
|
||||
}
|
||||
};
|
||||
|
||||
switch (id) {
|
||||
case EditId::Volume:
|
||||
sendFloat(SFIZZ_VOLUME, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::Polyphony:
|
||||
sendFloat(SFIZZ_POLYPHONY, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::Oversampling:
|
||||
sendFloat(SFIZZ_OVERSAMPLING, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::PreloadSize:
|
||||
sendFloat(SFIZZ_PRELOAD, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::ScalaRootKey:
|
||||
sendFloat(SFIZZ_SCALA_ROOT_KEY, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::TuningFrequency:
|
||||
sendFloat(SFIZZ_TUNING_FREQUENCY, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::StretchTuning:
|
||||
sendFloat(SFIZZ_STRETCH_TUNING, absl::get<float>(v));
|
||||
break;
|
||||
case EditId::SfzFile:
|
||||
sendPath(sfizz_sfz_file_uri, absl::get<std::string>(v));
|
||||
break;
|
||||
case EditId::ScalaFile:
|
||||
sendPath(sfizz_scala_file_uri, absl::get<std::string>(v));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sfizz_ui_t::uiBeginSend(EditId id)
|
||||
{
|
||||
uiTouch(id, true);
|
||||
}
|
||||
|
||||
void sfizz_ui_t::uiEndSend(EditId id)
|
||||
{
|
||||
uiTouch(id, false);
|
||||
}
|
||||
|
||||
void sfizz_ui_t::uiTouch(EditId id, bool t)
|
||||
{
|
||||
if (!touch)
|
||||
return;
|
||||
|
||||
switch (id) {
|
||||
case EditId::Volume:
|
||||
touch->touch(touch->handle, SFIZZ_VOLUME, t);
|
||||
break;
|
||||
case EditId::Polyphony:
|
||||
touch->touch(touch->handle, SFIZZ_POLYPHONY, t);
|
||||
break;
|
||||
case EditId::Oversampling:
|
||||
touch->touch(touch->handle, SFIZZ_OVERSAMPLING, t);
|
||||
break;
|
||||
case EditId::PreloadSize:
|
||||
touch->touch(touch->handle, SFIZZ_PRELOAD, t);
|
||||
break;
|
||||
case EditId::ScalaRootKey:
|
||||
touch->touch(touch->handle, SFIZZ_SCALA_ROOT_KEY, t);
|
||||
break;
|
||||
case EditId::TuningFrequency:
|
||||
touch->touch(touch->handle, SFIZZ_TUNING_FREQUENCY, t);
|
||||
break;
|
||||
case EditId::StretchTuning:
|
||||
touch->touch(touch->handle, SFIZZ_STRETCH_TUNING, t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sfizz_ui_t::uiSendMIDI(const uint8_t* msg, uint32_t len)
|
||||
{
|
||||
LV2_Atom_Forge *forge = &atom_forge;
|
||||
alignas(LV2_Atom) uint8_t buffer[512];
|
||||
auto *atom = reinterpret_cast<const LV2_Atom *>(buffer);
|
||||
lv2_atom_forge_set_buffer(forge, (uint8_t *)&buffer, sizeof(buffer));
|
||||
if (lv2_atom_forge_atom(forge, len, midi_event_uri) &&
|
||||
lv2_atom_forge_write(forge, msg, len))
|
||||
{
|
||||
write(con, SFIZZ_CONTROL, lv2_atom_total_size(atom), atom_event_transfer_uri, atom);
|
||||
}
|
||||
}
|
||||
14
lv2/sfizz_ui.ttl.in
Normal file
14
lv2/sfizz_ui.ttl.in
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
|
||||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
|
||||
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
|
||||
|
||||
<@LV2PLUGIN_URI@#ui>
|
||||
lv2:extensionData ui:idleInterface ;
|
||||
lv2:extensionData ui:showInterface ;
|
||||
lv2:requiredFeature ui:idleInterface ;
|
||||
lv2:optionalFeature ui:noUserResize ;
|
||||
lv2:optionalFeature ui:resize ;
|
||||
lv2:optionalFeature ui:parent ;
|
||||
lv2:optionalFeature ui:touch ;
|
||||
lv2:requiredFeature urid:map ;
|
||||
lv2:requiredFeature urid:unmap .
|
||||
170
lv2/vstgui_helpers.cpp
Normal file
170
lv2/vstgui_helpers.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
// Note(jpc) same code as used in Surge LV2, I am the original author
|
||||
|
||||
#include "vstgui_helpers.h"
|
||||
#include <lv2/ui/ui.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#if LINUX
|
||||
#include <dlfcn.h>
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#if LINUX
|
||||
void Lv2IdleRunLoop::execIdle()
|
||||
{
|
||||
std::chrono::steady_clock::time_point tick = std::chrono::steady_clock::now();
|
||||
|
||||
for (Event& ev : _events)
|
||||
{
|
||||
if (!ev.alive)
|
||||
continue;
|
||||
|
||||
// TODO LV2: fix me, XCB descriptor polling not working at this point
|
||||
#if 0
|
||||
pollfd pfd = {};
|
||||
pfd.fd = ev.fd;
|
||||
pfd.events = POLLIN|POLLERR|POLLHUP;
|
||||
if (poll(&pfd, 1, 0) > 0)
|
||||
#endif
|
||||
{
|
||||
ev.handler->onEvent();
|
||||
}
|
||||
}
|
||||
|
||||
for (Timer& tm : _timers)
|
||||
{
|
||||
if (!tm.alive)
|
||||
continue;
|
||||
|
||||
if (tm.lastTickValid)
|
||||
{
|
||||
std::chrono::steady_clock::duration duration = tick - tm.lastTick;
|
||||
tm.counter += std::chrono::duration_cast<std::chrono::microseconds>(duration);
|
||||
if (tm.counter >= tm.interval)
|
||||
{
|
||||
tm.handler->onTimer();
|
||||
tm.counter = std::min(tm.counter - tm.interval, tm.interval);
|
||||
}
|
||||
}
|
||||
tm.lastTick = tick;
|
||||
tm.lastTickValid = true;
|
||||
}
|
||||
|
||||
garbageCollectDeadHandlers<Event>(_events);
|
||||
garbageCollectDeadHandlers<Timer>(_timers);
|
||||
}
|
||||
|
||||
bool Lv2IdleRunLoop::registerEventHandler(int fd, VSTGUI::X11::IEventHandler* handler)
|
||||
{
|
||||
// fprintf(stderr, "registerEventHandler %d %p\n", fd, handler);
|
||||
|
||||
Event ev;
|
||||
ev.fd = fd;
|
||||
ev.handler = handler;
|
||||
ev.alive = true;
|
||||
_events.push_back(ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lv2IdleRunLoop::unregisterEventHandler(VSTGUI::X11::IEventHandler* handler)
|
||||
{
|
||||
// fprintf(stderr, "unregisterEventHandler %p\n", handler);
|
||||
|
||||
auto it = std::find_if(_events.begin(), _events.end(), [handler](const Event& ev) -> bool {
|
||||
return ev.handler == handler && ev.alive;
|
||||
});
|
||||
|
||||
if (it != _events.end())
|
||||
it->alive = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lv2IdleRunLoop::registerTimer(uint64_t interval, VSTGUI::X11::ITimerHandler* handler)
|
||||
{
|
||||
// fprintf(stderr, "registerTimer %lu %p\n", interval, handler);
|
||||
|
||||
Timer tm;
|
||||
tm.interval = std::chrono::milliseconds(interval);
|
||||
tm.counter = std::chrono::microseconds(0);
|
||||
tm.lastTickValid = false;
|
||||
tm.handler = handler;
|
||||
tm.alive = true;
|
||||
_timers.push_back(tm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lv2IdleRunLoop::unregisterTimer(VSTGUI::X11::ITimerHandler* handler)
|
||||
{
|
||||
// fprintf(stderr, "unregisterTimer %p\n", handler);
|
||||
|
||||
auto it = std::find_if(_timers.begin(), _timers.end(), [handler](const Timer& tm) -> bool {
|
||||
return tm.handler == handler && tm.alive;
|
||||
});
|
||||
|
||||
if (it != _timers.end())
|
||||
it->alive = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T> void Lv2IdleRunLoop::garbageCollectDeadHandlers(std::list<T>& handlers)
|
||||
{
|
||||
auto pos = handlers.begin();
|
||||
auto end = handlers.end();
|
||||
|
||||
while (pos != end)
|
||||
{
|
||||
auto curPos = pos++;
|
||||
if (!curPos->alive)
|
||||
handlers.erase(curPos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
///
|
||||
#if LINUX
|
||||
namespace VSTGUI
|
||||
{
|
||||
void* soHandle = nullptr;
|
||||
|
||||
static volatile bool soHandleInitialized = false;
|
||||
static std::mutex soHandleMutex;
|
||||
|
||||
struct Dl_handle_deleter
|
||||
{
|
||||
void operator()(void* x) const noexcept
|
||||
{
|
||||
dlclose(x);
|
||||
}
|
||||
};
|
||||
static std::unique_ptr<void, Dl_handle_deleter> soHandlePointer;
|
||||
} // namespace VSTGUI
|
||||
|
||||
void VSTGUI::initializeSoHandle()
|
||||
{
|
||||
if (VSTGUI::soHandleInitialized)
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(VSTGUI::soHandleMutex);
|
||||
if (VSTGUI::soHandleInitialized)
|
||||
return;
|
||||
|
||||
Dl_info info;
|
||||
if (dladdr((void*)&lv2ui_descriptor, &info))
|
||||
{
|
||||
VSTGUI::soHandle = dlopen(info.dli_fname, RTLD_LAZY);
|
||||
VSTGUI::soHandlePointer.reset(VSTGUI::soHandle);
|
||||
}
|
||||
VSTGUI::soHandleInitialized = true;
|
||||
}
|
||||
#endif
|
||||
68
lv2/vstgui_helpers.h
Normal file
68
lv2/vstgui_helpers.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
// Note(jpc) same code as used in Surge LV2, I am the original author
|
||||
|
||||
#pragma once
|
||||
#include <list>
|
||||
#include <chrono>
|
||||
|
||||
#include "editor/utility/vstgui_before.h"
|
||||
#include "vstgui/lib/vstguibase.h"
|
||||
#if LINUX
|
||||
#include "vstgui/lib/platform/platform_x11.h"
|
||||
#include "vstgui/lib/platform/linux/x11platform.h"
|
||||
#endif
|
||||
#include "editor/utility/vstgui_after.h"
|
||||
|
||||
#if LINUX
|
||||
class Lv2IdleRunLoop : public VSTGUI::X11::IRunLoop
|
||||
{
|
||||
public:
|
||||
void execIdle();
|
||||
|
||||
bool registerEventHandler(int fd, VSTGUI::X11::IEventHandler* handler) override;
|
||||
bool unregisterEventHandler(VSTGUI::X11::IEventHandler* handler) override;
|
||||
bool registerTimer(uint64_t interval, VSTGUI::X11::ITimerHandler* handler) override;
|
||||
bool unregisterTimer(VSTGUI::X11::ITimerHandler* handler) override;
|
||||
|
||||
void forget() override
|
||||
{}
|
||||
void remember() override
|
||||
{}
|
||||
|
||||
private:
|
||||
struct Event
|
||||
{
|
||||
int fd;
|
||||
VSTGUI::X11::IEventHandler* handler;
|
||||
bool alive;
|
||||
};
|
||||
struct Timer
|
||||
{
|
||||
std::chrono::microseconds interval;
|
||||
std::chrono::microseconds counter;
|
||||
bool lastTickValid;
|
||||
std::chrono::steady_clock::time_point lastTick;
|
||||
VSTGUI::X11::ITimerHandler* handler;
|
||||
bool alive;
|
||||
};
|
||||
|
||||
private:
|
||||
template <class T> static void garbageCollectDeadHandlers(std::list<T>& handlers);
|
||||
|
||||
private:
|
||||
std::list<Event> _events;
|
||||
std::list<Timer> _timers;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if LINUX
|
||||
namespace VSTGUI
|
||||
{
|
||||
void initializeSoHandle();
|
||||
}
|
||||
#endif
|
||||
|
|
@ -27,9 +27,6 @@ set(VSTPLUGIN_HEADERS
|
|||
SfizzVstState.h
|
||||
X11RunLoop.h)
|
||||
|
||||
set(VSTPLUGIN_RESOURCES
|
||||
logo.png)
|
||||
|
||||
add_library(${VSTPLUGIN_PRJ_NAME} MODULE
|
||||
${VSTPLUGIN_HEADERS}
|
||||
${VSTPLUGIN_SOURCES})
|
||||
|
|
@ -71,8 +68,8 @@ endif()
|
|||
# Create the bundle (see "VST 3 Locations / Format")
|
||||
execute_process (
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources")
|
||||
foreach(res ${VSTPLUGIN_RESOURCES})
|
||||
file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${res}"
|
||||
foreach(res ${EDITOR_RESOURCES})
|
||||
file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources/${res}"
|
||||
DESTINATION "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources")
|
||||
endforeach()
|
||||
if(WIN32)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue