Files
looper/client/src/carla_host.c

277 lines
8.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.
#define _GNU_SOURCE
#include <stdlib.h>
#include <CarlaHost.h>
#include <CarlaBackend.h>
#include <string.h>
#include "carla_host.h"
#ifdef MOCK_JACK
/* Mock JACK functions always succeed */
/* Provide a dummy type so we can have a nonNULL pointer */
typedef void jack_client_t;
static int mock_jack_connect(const char *from, const char *to) {
(void)from; (void)to;
return 0;
}
static int mock_jack_disconnect(const char *from, const char *to) {
(void)from; (void)to;
return 0;
}
/* Provide a fake jack_client pointer that is nonNULL */
#define jack_client ((jack_client_t*)1)
/* Real jack_connect/jack_disconnect take 3 arguments (client, a, b).
We ignore the client and forward to the mock 2arg functions. */
#define jack_connect(client, a, b) ((void)(client), mock_jack_connect(a, b))
#define jack_disconnect(client, a, b) ((void)(client), mock_jack_disconnect(a, b))
#else
#include <jack/jack.h>
#endif
#define MAX_PLUGINS 256
static CarlaHostHandle handle = NULL;
#ifdef MOCK_JACK
/* jack_client is defined via macro above (nonNULL) */
#else
static jack_client_t *jack_client = NULL; // private JACK client for port connections
#endif
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;
#ifndef MOCK_JACK
// 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
#endif
// 2) Create the Carla host handle
handle = carla_standalone_host_init();
if (!handle) {
#ifndef MOCK_JACK
if (jack_client) jack_client_close(jack_client);
jack_client = NULL;
#endif
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;
#ifndef MOCK_JACK
if (jack_client) jack_client_close(jack_client);
jack_client = NULL;
#endif
return -1;
}
return 0;
}
void carla_cleanup_jack(void) {
if (handle != NULL) {
carla_engine_close(handle);
handle = NULL;
}
#ifndef MOCK_JACK
if (jack_client) {
jack_client_close(jack_client);
jack_client = NULL;
}
#endif
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 MOCK_JACK
/* Mock: return a few fake port names */
int carla_get_ports(const char *type, char ***ports, int *count) {
(void)type;
static const char *fake[] = {"system:capture_1", "system:capture_2", "system:playback_1", "system:playback_2"};
*count = 4;
*ports = malloc(*count * sizeof(char*));
if (!*ports) { *count = 0; return -1; }
for (int i = 0; i < *count; i++)
(*ports)[i] = strdup(fake[i]);
return 0;
}
#else
#include <jack/jack.h>
int carla_get_ports(const char *type, char ***ports, int *count) {
(void)type;
if (!jack_client) {
*ports = NULL;
*count = 0;
return -1;
}
const char **jports = jack_get_ports(jack_client, NULL, NULL, 0);
if (!jports) {
*ports = NULL;
*count = 0;
return -1;
}
int n = 0;
while (jports[n]) n++;
*count = n;
*ports = malloc(n * sizeof(char*));
if (!*ports) {
jack_free(jports);
return -1;
}
for (int i = 0; i < n; i++)
(*ports)[i] = strdup(jports[i]);
jack_free(jports);
return 0;
}
#endif
#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;
}