You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
611 lines
14 KiB
611 lines
14 KiB
/* |
|
* Copyright (C) 2021 Alexandros Theodotou <alex at zrythm dot org> |
|
* |
|
* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Affero General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Affero General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Affero General Public License |
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include "config.h" |
|
|
|
#include <stdbool.h> |
|
#include <stdlib.h> |
|
|
|
#include "carla_api_tester_app.h" |
|
|
|
#include <gtk/gtk.h> |
|
|
|
/** This is declared extern in zrythm_app.h. */ |
|
CarlaApiTesterApp * carla_api_tester_app = NULL; |
|
|
|
G_DEFINE_TYPE ( |
|
CarlaApiTesterApp, carla_api_tester_app, |
|
GTK_TYPE_APPLICATION) |
|
|
|
/** |
|
* Tick callback for the plugin UI. |
|
*/ |
|
static int |
|
carla_plugin_tick_cb ( |
|
GtkWidget * widget, |
|
GdkFrameClock * frame_clock, |
|
CarlaApiTesterApp * self) |
|
{ |
|
self->native_plugin_descriptor->ui_idle ( |
|
self->native_plugin_handle); |
|
return G_SOURCE_CONTINUE; |
|
} |
|
|
|
static uint32_t |
|
host_get_buffer_size ( |
|
NativeHostHandle handle) |
|
{ |
|
uint32_t buffer_size = 512; |
|
|
|
return buffer_size; |
|
} |
|
|
|
static double |
|
host_get_sample_rate ( |
|
NativeHostHandle handle) |
|
{ |
|
double sample_rate = 44000.0; |
|
|
|
return sample_rate; |
|
} |
|
|
|
static bool |
|
host_is_offline ( |
|
NativeHostHandle handle) |
|
{ |
|
return false; |
|
} |
|
|
|
static const NativeTimeInfo* |
|
host_get_time_info ( |
|
CarlaApiTesterApp * self) |
|
{ |
|
return &self->time_info; |
|
} |
|
|
|
static bool |
|
host_write_midi_event ( |
|
CarlaApiTesterApp * self, |
|
const NativeMidiEvent * event) |
|
{ |
|
g_message ("write midi event"); |
|
|
|
return 0; |
|
} |
|
|
|
static void |
|
host_ui_parameter_changed ( |
|
CarlaApiTesterApp * self, |
|
uint32_t index, |
|
float value) |
|
{ |
|
g_message ("handle ui param changed"); |
|
} |
|
|
|
static void |
|
host_ui_custom_data_changed ( |
|
CarlaApiTesterApp * self, |
|
const char* key, |
|
const char* value) |
|
{ |
|
} |
|
|
|
static void |
|
host_ui_closed ( |
|
CarlaApiTesterApp * self) |
|
{ |
|
g_message ("ui closed"); |
|
} |
|
|
|
static intptr_t |
|
host_dispatcher ( |
|
CarlaApiTesterApp * self, |
|
NativeHostDispatcherOpcode opcode, |
|
int32_t index, |
|
intptr_t value, |
|
void* ptr, |
|
float opt) |
|
{ |
|
/* TODO */ |
|
g_message ("host dispatcher (opcode %d)", opcode); |
|
switch (opcode) |
|
{ |
|
case NATIVE_HOST_OPCODE_HOST_IDLE: |
|
/* some expensive computation is happening. |
|
* this is used so that the GTK ui does not |
|
* block */ |
|
/* note: disabled because some logic depends |
|
* on this plugin being activated */ |
|
#if 0 |
|
while (gtk_events_pending ()) |
|
{ |
|
gtk_main_iteration (); |
|
} |
|
#endif |
|
break; |
|
#if 0 |
|
case NATIVE_HOST_OPCODE_INTERNAL_PLUGIN: |
|
/* falktx: you will need to call the new |
|
* juce functions, then return 1 on the |
|
* INTERNAL_PLUGIN host opcode. |
|
* when that opcode returns 1, carla-plugin |
|
* will let the host do the juce idling |
|
* which is for the best here, since you want |
|
* to show UIs without carla itself */ |
|
return 1; |
|
break; |
|
#endif |
|
case NATIVE_HOST_OPCODE_GET_FILE_PATH: |
|
g_debug ("get file path"); |
|
g_return_val_if_fail (ptr, 0); |
|
if (!strcmp ((char *) ptr, "carla")) |
|
{ |
|
g_debug ("ptr is carla"); |
|
} |
|
break; |
|
case NATIVE_HOST_OPCODE_UI_RESIZE: |
|
g_debug ("ui resize"); |
|
/* TODO handle UI resize */ |
|
break; |
|
case NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER: |
|
g_debug ("ui touch"); |
|
break; |
|
case NATIVE_HOST_OPCODE_UI_UNAVAILABLE: |
|
/* TODO handle UI close */ |
|
g_debug ("UI unavailable"); |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void |
|
engine_callback ( |
|
CarlaApiTesterApp * self, |
|
EngineCallbackOpcode action, |
|
uint plugin_id, |
|
int val1, |
|
int val2, |
|
int val3, |
|
float valf, |
|
const char * val_str) |
|
{ |
|
switch (action) |
|
{ |
|
case ENGINE_CALLBACK_UI_STATE_CHANGED: |
|
switch (val1) |
|
{ |
|
case 0: |
|
case -1: |
|
g_message ("plugin became hidden"); |
|
break; |
|
case 1: |
|
g_message ("plugin became visible"); |
|
break; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
static PluginType |
|
get_plugin_type_from_str ( |
|
const char * str) |
|
{ |
|
if (!strcmp (str, "lv2")) |
|
{ |
|
return PLUGIN_LV2; |
|
} |
|
else if (!strcmp (str, "ladspa")) |
|
{ |
|
return PLUGIN_LADSPA; |
|
} |
|
else if (!strcmp (str, "dssi")) |
|
{ |
|
return PLUGIN_DSSI; |
|
} |
|
else if (!strcmp (str, "vst2")) |
|
{ |
|
return PLUGIN_VST2; |
|
} |
|
else if (!strcmp (str, "vst3")) |
|
{ |
|
return PLUGIN_VST3; |
|
} |
|
else if (!strcmp (str, "au")) |
|
{ |
|
return PLUGIN_AU; |
|
} |
|
else if (!strcmp (str, "sfz")) |
|
{ |
|
return PLUGIN_SFZ; |
|
} |
|
else if (!strcmp (str, "sf2")) |
|
{ |
|
return PLUGIN_SF2; |
|
} |
|
} |
|
|
|
/* |
|
* Called after startup if no filename is passed on |
|
* command line. |
|
*/ |
|
static void |
|
carla_api_tester_app_activate ( |
|
GApplication * _app) |
|
{ |
|
g_message ("Activating..."); |
|
|
|
CarlaApiTesterApp * self = |
|
CARLA_API_TESTER_APP (_app); |
|
|
|
self->native_host_descriptor.handle = self; |
|
self->native_host_descriptor.uiName = |
|
"Carla API tester"; |
|
|
|
self->native_host_descriptor.uiParentId = 0; |
|
|
|
/* set resources dir */ |
|
const char * carla_filename = |
|
carla_get_library_filename (); |
|
char * tmp = g_path_get_dirname (carla_filename); |
|
char * dir = g_path_get_dirname (tmp); |
|
g_free (tmp); |
|
tmp = g_path_get_dirname (dir); |
|
g_free (dir); |
|
dir = tmp; |
|
self->native_host_descriptor.resourceDir = |
|
g_build_filename ( |
|
dir, "share", "carla", "resources", NULL); |
|
g_free (dir); |
|
|
|
self->native_host_descriptor.get_buffer_size = |
|
host_get_buffer_size; |
|
self->native_host_descriptor.get_sample_rate = |
|
host_get_sample_rate; |
|
self->native_host_descriptor.is_offline = |
|
host_is_offline; |
|
self->native_host_descriptor.get_time_info = |
|
(const NativeTimeInfo * (*)(void *)) |
|
host_get_time_info; |
|
self->native_host_descriptor.write_midi_event = |
|
(_Bool (*)(void *, const NativeMidiEvent *)) |
|
host_write_midi_event; |
|
self->native_host_descriptor. |
|
ui_parameter_changed = |
|
(void (*)(void *, uint32_t, float)) |
|
host_ui_parameter_changed; |
|
self->native_host_descriptor. |
|
ui_custom_data_changed = |
|
(intptr_t (*)(void *, NativeHostDispatcherOpcode, int32_t, intptr_t, void *, float)) |
|
host_ui_custom_data_changed; |
|
self->native_host_descriptor.ui_closed = |
|
(void (*)(void *)) |
|
host_ui_closed; |
|
self->native_host_descriptor.ui_open_file = |
|
NULL; |
|
self->native_host_descriptor.ui_save_file = |
|
NULL; |
|
self->native_host_descriptor.dispatcher = |
|
(intptr_t (*)(CarlaApiTesterApp *, NativeHostDispatcherOpcode, int32_t, intptr_t, void *, float)) |
|
host_dispatcher; |
|
|
|
self->time_info.bbt.valid = 1; |
|
|
|
/* instantiate the plugin to get its info */ |
|
self->native_plugin_descriptor = |
|
carla_get_native_rack_plugin (); |
|
self->native_plugin_handle = |
|
self->native_plugin_descriptor->instantiate ( |
|
&self->native_host_descriptor); |
|
self->host_handle = |
|
carla_create_native_plugin_host_handle ( |
|
self->native_plugin_descriptor, |
|
self->native_plugin_handle); |
|
|
|
/* set binary paths */ |
|
g_message ( |
|
"setting carla engine option " |
|
"[ENGINE_OPTION_PATH_BINARIES] to '%s'", |
|
CONFIGURE_BINDIR); |
|
carla_set_engine_option ( |
|
self->host_handle, |
|
ENGINE_OPTION_PATH_BINARIES, 0, |
|
CONFIGURE_BINDIR); |
|
|
|
#if 0 |
|
/* set lv2 path */ |
|
carla_set_engine_option ( |
|
self->host_handle, |
|
ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, |
|
PLUGIN_MANAGER->lv2_path); |
|
|
|
/* set UI scale factor */ |
|
carla_set_engine_option ( |
|
self->host_handle, |
|
ENGINE_OPTION_FRONTEND_UI_SCALE, |
|
(int) |
|
((float) self->plugin->ui_scale_factor * |
|
1000.f), |
|
NULL); |
|
|
|
|
|
/* set whether UI should stay on top */ |
|
if (!ZRYTHM_TESTING && |
|
g_settings_get_boolean ( |
|
S_P_PLUGINS_UIS, "stay-on-top")) |
|
{ |
|
carla_set_engine_option ( |
|
self->host_handle, |
|
ENGINE_OPTION_UIS_ALWAYS_ON_TOP, true, |
|
NULL); |
|
} |
|
#endif |
|
|
|
g_message ( |
|
"%s: using bridge mode %s", __func__, |
|
self->bridge); |
|
|
|
if (!strcmp (self->bridge, "full")) |
|
{ |
|
carla_set_engine_option ( |
|
self->host_handle, |
|
ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, |
|
true, NULL); |
|
} |
|
else if (!strcmp (self->bridge, "ui")) |
|
{ |
|
carla_set_engine_option ( |
|
self->host_handle, |
|
ENGINE_OPTION_PREFER_UI_BRIDGES, |
|
true, NULL); |
|
} |
|
|
|
int ret = -1; |
|
PluginType type = |
|
get_plugin_type_from_str (self->format); |
|
if (!strcmp (self->format, "lv2") || |
|
!strcmp (self->format, "au")) |
|
{ |
|
g_message ("uri %s", self->plugin); |
|
ret = |
|
carla_add_plugin ( |
|
self->host_handle, |
|
self->woe32_binary ? |
|
BINARY_WIN32 : BINARY_NATIVE, |
|
type, NULL, "name", |
|
self->plugin, 0, NULL, 0); |
|
} |
|
else if (!strcmp (self->format, "vst2") || |
|
!strcmp (self->format, "vst3")) |
|
{ |
|
ret = |
|
carla_add_plugin ( |
|
self->host_handle, |
|
self->woe32_binary ? |
|
BINARY_WIN32 : BINARY_NATIVE, |
|
type, self->plugin, "name", |
|
/* TODO UNIQUE ID */ |
|
"name", 0, NULL, 0); |
|
} |
|
else if (!strcmp (self->format, "dssi") || |
|
!strcmp (self->format, "ladspa")) |
|
{ |
|
ret = |
|
carla_add_plugin ( |
|
self->host_handle, BINARY_NATIVE, |
|
type, self->plugin, "name", |
|
self->plugin, 0, NULL, 0); |
|
} |
|
else if (!strcmp (self->format, "sfz") || |
|
!strcmp (self->format, "sf2")) |
|
{ |
|
ret = |
|
carla_add_plugin ( |
|
self->host_handle, |
|
BINARY_NATIVE, |
|
type, self->plugin, "name", |
|
"name", 0, NULL, 0); |
|
} |
|
else |
|
{ |
|
g_error ("unknown format %s", self->format); |
|
} |
|
|
|
if (ret != 1) |
|
{ |
|
g_error ( |
|
"Error adding carla plugin: %s", |
|
carla_get_last_error (self->host_handle)); |
|
} |
|
|
|
/* enable various messages */ |
|
#define ENABLE_OPTION(x) \ |
|
carla_set_option ( \ |
|
self->host_handle, 0, \ |
|
PLUGIN_OPTION_##x, true) |
|
|
|
ENABLE_OPTION (FORCE_STEREO); |
|
ENABLE_OPTION (SEND_CONTROL_CHANGES); |
|
ENABLE_OPTION (SEND_CHANNEL_PRESSURE); |
|
ENABLE_OPTION (SEND_NOTE_AFTERTOUCH); |
|
ENABLE_OPTION (SEND_PITCHBEND); |
|
ENABLE_OPTION (SEND_ALL_SOUND_OFF); |
|
ENABLE_OPTION (SEND_PROGRAM_CHANGES); |
|
|
|
/* add engine callback */ |
|
carla_set_engine_callback ( |
|
self->host_handle, engine_callback, self); |
|
|
|
g_message ("done"); |
|
} |
|
|
|
/** |
|
* Called when a filename is passed to the command line |
|
* instead of activate. |
|
* |
|
* Always gets called after startup and before the tasks. |
|
*/ |
|
static void |
|
carla_api_tester_app_open ( |
|
GApplication * _app, |
|
GFile ** files, |
|
gint n_files, |
|
const gchar * hint) |
|
{ |
|
g_message ("Opening..."); |
|
|
|
#if 0 |
|
g_warn_if_fail (n_files == 1); |
|
|
|
GFile * file = files[0]; |
|
ZRYTHM->open_filename = |
|
g_file_get_path (file); |
|
g_message ("open %s", ZRYTHM->open_filename); |
|
#endif |
|
|
|
g_message ("done"); |
|
} |
|
|
|
/** |
|
* First function that gets called. |
|
*/ |
|
static void |
|
carla_api_tester_app_startup ( |
|
GApplication * app) |
|
{ |
|
g_message ("Starting up..."); |
|
|
|
CarlaApiTesterApp * self = |
|
CARLA_API_TESTER_APP (app); |
|
G_APPLICATION_CLASS ( |
|
carla_api_tester_app_parent_class)-> |
|
startup (G_APPLICATION (self)); |
|
g_message ( |
|
"called startup on G_APPLICATION_CLASS"); |
|
|
|
bool ret = |
|
g_application_get_is_registered ( |
|
G_APPLICATION (self)); |
|
bool remote = |
|
g_application_get_is_remote ( |
|
G_APPLICATION (self)); |
|
GMenuModel * app_menu_model = |
|
gtk_application_get_app_menu ( |
|
GTK_APPLICATION (app)); |
|
bool prefers_app_menu = |
|
gtk_application_prefers_app_menu ( |
|
GTK_APPLICATION (app)); |
|
g_message ( |
|
"application registered: %d, is remote %d, " |
|
"app menu exists: %d, prefers app menu: %d", |
|
ret, remote, app_menu_model ? true : false, |
|
prefers_app_menu); |
|
|
|
g_message ( |
|
"application resources base path: %s", |
|
g_application_get_resource_base_path ( |
|
G_APPLICATION (app))); |
|
|
|
g_message ("done"); |
|
} |
|
|
|
/** |
|
* Called immediately after the main GTK loop |
|
* terminates. |
|
* |
|
* This is also called manually on SIGINT. |
|
*/ |
|
static void |
|
carla_api_tester_app_on_shutdown ( |
|
GApplication * application, |
|
CarlaApiTesterApp * self) |
|
{ |
|
g_message ("Shutting down..."); |
|
|
|
g_message ("done"); |
|
} |
|
|
|
/** |
|
* Creates the CarlaApiTester GApplication. |
|
*/ |
|
CarlaApiTesterApp * |
|
carla_api_tester_app_new ( |
|
const char * plugin, |
|
const char * format, |
|
const char * bridge, |
|
bool woe32_binary) |
|
{ |
|
CarlaApiTesterApp * self = g_object_new ( |
|
CARLA_API_TESTER_APP_TYPE, |
|
/* if an ID is provided, this application |
|
* becomes unique (only one instance allowed) */ |
|
/*"application-id", "org.carla-api-tester.App",*/ |
|
NULL); |
|
|
|
carla_api_tester_app = self; |
|
|
|
self->bridge = bridge; |
|
self->plugin = plugin; |
|
self->format = format; |
|
self->woe32_binary = woe32_binary; |
|
|
|
/* add shutdown handler */ |
|
g_signal_connect ( |
|
G_OBJECT (self), "shutdown", |
|
G_CALLBACK (carla_api_tester_app_on_shutdown), |
|
self); |
|
|
|
return self; |
|
} |
|
|
|
static void |
|
finalize ( |
|
CarlaApiTesterApp * self) |
|
{ |
|
g_message ("finalizing app..."); |
|
|
|
g_message ("done"); |
|
} |
|
|
|
static void |
|
carla_api_tester_app_class_init ( |
|
CarlaApiTesterAppClass * class) |
|
{ |
|
G_APPLICATION_CLASS (class)->activate = |
|
carla_api_tester_app_activate; |
|
G_APPLICATION_CLASS (class)->startup = |
|
carla_api_tester_app_startup; |
|
G_APPLICATION_CLASS (class)->open = |
|
carla_api_tester_app_open; |
|
|
|
GObjectClass * klass = G_OBJECT_CLASS (class); |
|
|
|
klass->finalize = (GObjectFinalizeFunc) finalize; |
|
} |
|
|
|
static void |
|
carla_api_tester_app_init ( |
|
CarlaApiTesterApp * self) |
|
{ |
|
g_message ("initing app"); |
|
|
|
g_message ("done"); |
|
}
|
|
|