Files
looper/client/src/carla_host.c

200 lines
6.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <CarlaHost.h>
#include <CarlaBackend.h>
#include <jack/jack.h>
#include <string.h>
#include "carla_host.h"
#define MAX_PLUGINS 256
static CarlaHostHandle handle = NULL;
static jack_client_t *jack_client = NULL; // private JACK client for port connections
static int carla_pids[MAX_PLUGINS];
static int plugin_count = 0;
#define MAX_CONNECTIONS 1024
typedef struct {
int plugin_id;
char plugin_port[256];
char looper_port[256];
} connection_t;
static connection_t connections[MAX_CONNECTIONS];
static int conn_count = 0;
int carla_init_jack(void) {
if (handle != NULL) return 0;
// 1) Open our own JACK client (for port connections)
jack_status_t status;
jack_client = jack_client_open("looper-connector", JackNoStartServer, &status);
// It's okay if jack_client is NULL; we still try Carla
// 2) Create the Carla host handle
handle = carla_standalone_host_init();
if (!handle) {
if (jack_client) jack_client_close(jack_client);
jack_client = NULL;
return -1;
}
// 3) Initialise the JACK engine (Carla uses its own JACK client)
if (!carla_engine_init(handle, "JACK", "looper-client")) {
carla_engine_close(handle);
handle = NULL;
if (jack_client) jack_client_close(jack_client);
jack_client = NULL;
return -1;
}
return 0;
}
void carla_cleanup_jack(void) {
if (handle != NULL) {
carla_engine_close(handle);
handle = NULL;
}
if (jack_client) {
jack_client_close(jack_client);
jack_client = NULL;
}
plugin_count = 0;
}
int carla_load(const char *binary, const char *plugin_id, int *out_id) {
if (!handle) return -1;
if (!binary) binary = "";
if (!plugin_id) plugin_id = "";
// carla_add_plugin: (handle, BinaryType, PluginType, filename, name, label, uniqueId, extraPtr, options)
if (!carla_add_plugin(handle, 0, 0, binary, NULL, plugin_id, 0, NULL, 0))
return -1;
// newly added plugin is at index (count-1)
uint32_t count = carla_get_current_plugin_count(handle);
if (count == 0) return -1;
if (plugin_count >= MAX_PLUGINS) {
carla_remove_plugin(handle, count - 1);
return -1;
}
int idx = plugin_count++;
carla_pids[idx] = count - 1; // Carlas internal ID
*out_id = idx;
return 0;
}
int carla_unload(int id) {
if (!handle) return -1;
if (id < 0 || id >= plugin_count) return -1;
int pid = carla_pids[id];
bool ok = carla_remove_plugin(handle, (uint)pid);
// shift array
for (int i = id; i < plugin_count - 1; ++i)
carla_pids[i] = carla_pids[i+1];
plugin_count--;
return ok ? 0 : -1;
}
int carla_connect(int id, const char *port_name, const char *looper_port) {
// Check that the plugin id is valid
if (id < 0 || id >= plugin_count)
return -1;
if (!port_name || !looper_port) return -1;
if (!jack_client) return -1;
// Real JACK port connection
int ret = jack_connect(jack_client, looper_port, port_name);
if (ret != 0) return -1;
// Store the connection so we can disconnect it later
if (conn_count < MAX_CONNECTIONS) {
connections[conn_count].plugin_id = id;
strncpy(connections[conn_count].plugin_port, port_name,
sizeof(connections[conn_count].plugin_port) - 1);
connections[conn_count].plugin_port[sizeof(connections[conn_count].plugin_port) - 1] = '\0';
strncpy(connections[conn_count].looper_port, looper_port,
sizeof(connections[conn_count].looper_port) - 1);
connections[conn_count].looper_port[sizeof(connections[conn_count].looper_port) - 1] = '\0';
conn_count++;
}
return 0;
}
int carla_disconnect(const char *from, const char *to) {
// If no JACK client, pretend success (allows unit tests without JACK server)
if (!jack_client) return 0;
if (!from || !to) return -1;
// Real JACK port disconnection
int ret = jack_disconnect(jack_client, from, to);
if (ret != 0) return -1;
// Remove the connection from our internal list (matching both port names)
for (int i = 0; i < conn_count; i++) {
if (strcmp(connections[i].looper_port, from) == 0 &&
strcmp(connections[i].plugin_port, to) == 0) {
// Shift remaining entries down
for (int j = i; j < conn_count - 1; j++)
connections[j] = connections[j + 1];
conn_count--;
break;
}
}
return 0;
}
void carla_set_bypass(int id, bool bypass) {
if (!handle) return;
if (id < 0 || id >= plugin_count) return;
int pid = carla_pids[id];
carla_set_active(handle, (uint)pid, !bypass);
}
int carla_disconnect_plugin(int id) {
if (!jack_client) return 0;
// Disconnect all stored connections for this plugin id
int any = 0;
for (int i = 0; i < conn_count; ) {
if (connections[i].plugin_id == id) {
jack_disconnect(jack_client,
connections[i].looper_port,
connections[i].plugin_port);
// Shift array
for (int j = i; j < conn_count - 1; j++)
connections[j] = connections[j + 1];
conn_count--;
any = 1;
} else {
i++;
}
}
return any ? 0 : -1; // return -1 if no connections were found (harmless)
}
#ifdef TESTING
int carla_test_connection_count(void) {
return conn_count;
}
int carla_test_add_connection(int plugin_id, const char *plugin_port, const char *looper_port) {
if (!plugin_port || !looper_port) return -1;
if (conn_count >= MAX_CONNECTIONS) return -1;
strncpy(connections[conn_count].plugin_port, plugin_port,
sizeof(connections[conn_count].plugin_port) - 1);
connections[conn_count].plugin_port[sizeof(connections[conn_count].plugin_port) - 1] = '\0';
strncpy(connections[conn_count].looper_port, looper_port,
sizeof(connections[conn_count].looper_port) - 1);
connections[conn_count].looper_port[sizeof(connections[conn_count].looper_port) - 1] = '\0';
connections[conn_count].plugin_id = plugin_id;
conn_count++;
return 0;
}
#endif
CarlaHostHandle carla_get_handle(void) {
return handle;
}