Files
jack-looper/carla.c
Loic Coenen 12a2f8ede2 feat: implement URI-based plugin loading and GUI text input for Carla
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
2026-05-02 22:35:55 +00:00

333 lines
11 KiB
C

#include "carla.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// Initialize Carla host
int carla_init(CarlaHost *host, jack_client_t *client) {
if (!host) return -1;
memset(host, 0, sizeof(CarlaHost));
host->client = client;
// Initialize all channel racks
for (int ch = 0; ch < 8; ch++) {
host->channel_racks[ch].num_plugins = 0;
host->channel_racks[ch].volume = 1.0f;
host->channel_racks[ch].bypassed = false;
}
host->num_available_plugins = 0;
host->available_plugins = NULL;
host->initialized = true;
printf("Carla host initialized\n");
return 0;
}
void carla_cleanup(CarlaHost *host) {
if (!host) return;
if (host->available_plugins) {
for (int i = 0; i < host->num_available_plugins; i++) {
free(host->available_plugins[i]);
}
free(host->available_plugins);
}
for (int ch = 0; ch < 8; ch++) {
ChannelRack *rack = &host->channel_racks[ch];
for (int p = 0; p < rack->num_plugins; p++) {
PluginInfo *plugin = &rack->plugins[p];
if (plugin->parameters) {
free(plugin->parameters);
}
if (plugin->parameter_names) {
for (int i = 0; i < plugin->num_parameters; i++) {
free(plugin->parameter_names[i]);
}
free(plugin->parameter_names);
}
}
}
host->initialized = false;
}
static int add_gain_plugin(CarlaHost *host, int channel) {
if (channel < 0 || channel >= 8) return -1;
ChannelRack *rack = &host->channel_racks[channel];
if (rack->num_plugins >= 16) return -1;
PluginInfo *plugin = &rack->plugins[rack->num_plugins];
strcpy(plugin->name, "Gain");
strcpy(plugin->uri, "internal://gain");
plugin->type = PLUGIN_TYPE_INTERNAL;
plugin->carla_plugin_id = -1;
plugin->num_parameters = 1;
plugin->parameters = malloc(sizeof(float));
plugin->parameters[0] = 1.0f;
plugin->parameter_names = malloc(sizeof(char*));
plugin->parameter_names[0] = strdup("Gain");
rack->num_plugins++;
return rack->num_plugins - 1;
}
int carla_add_plugin(CarlaHost *host, int channel, const char *uri, PluginType type) {
if (!host || !host->initialized) return -1;
if (channel < 0 || channel >= 8) return -1;
if (!uri) return -1;
ChannelRack *rack = &host->channel_racks[channel];
if (rack->num_plugins >= 16) return -1;
PluginInfo *plugin = &rack->plugins[rack->num_plugins];
// Parse URI to determine plugin type and name
if (strstr(uri, "internal://") == uri) {
// Internal plugin
plugin->type = PLUGIN_TYPE_INTERNAL;
strncpy(plugin->uri, uri, sizeof(plugin->uri) - 1);
plugin->uri[sizeof(plugin->uri) - 1] = '\0';
const char *name = uri + strlen("internal://");
if (strcmp(name, "gain") == 0) {
strcpy(plugin->name, "Gain");
plugin->num_parameters = 1;
plugin->parameters = malloc(sizeof(float));
plugin->parameters[0] = 1.0f;
plugin->parameter_names = malloc(sizeof(char*));
plugin->parameter_names[0] = strdup("Gain");
} else if (strcmp(name, "reverb") == 0) {
strcpy(plugin->name, "Reverb");
plugin->num_parameters = 2;
plugin->parameters = malloc(2 * sizeof(float));
plugin->parameters[0] = 0.5f; // Mix
plugin->parameters[1] = 0.5f; // Decay
plugin->parameter_names = malloc(2 * sizeof(char*));
plugin->parameter_names[0] = strdup("Mix");
plugin->parameter_names[1] = strdup("Decay");
} else if (strcmp(name, "delay") == 0) {
strcpy(plugin->name, "Delay");
plugin->num_parameters = 2;
plugin->parameters = malloc(2 * sizeof(float));
plugin->parameters[0] = 0.3f; // Feedback
plugin->parameters[1] = 0.5f; // Mix
plugin->parameter_names = malloc(2 * sizeof(char*));
plugin->parameter_names[0] = strdup("Feedback");
plugin->parameter_names[1] = strdup("Mix");
} else if (strcmp(name, "filter") == 0) {
strcpy(plugin->name, "Filter");
plugin->num_parameters = 2;
plugin->parameters = malloc(2 * sizeof(float));
plugin->parameters[0] = 1000.0f; // Cutoff
plugin->parameters[1] = 1.0f; // Resonance
plugin->parameter_names = malloc(2 * sizeof(char*));
plugin->parameter_names[0] = strdup("Cutoff");
plugin->parameter_names[1] = strdup("Resonance");
} else {
// Unknown internal plugin, default to gain
strcpy(plugin->name, "Unknown Internal");
plugin->num_parameters = 0;
plugin->parameters = NULL;
plugin->parameter_names = NULL;
}
} else {
// External plugin (LV2, VST, etc.) - store URI for future Carla integration
plugin->type = type;
strncpy(plugin->uri, uri, sizeof(plugin->uri) - 1);
plugin->uri[sizeof(plugin->uri) - 1] = '\0';
// Extract a display name from the URI
const char *last_slash = strrchr(uri, '/');
if (last_slash) {
strncpy(plugin->name, last_slash + 1, sizeof(plugin->name) - 1);
} else {
strncpy(plugin->name, uri, sizeof(plugin->name) - 1);
}
plugin->name[sizeof(plugin->name) - 1] = '\0';
plugin->num_parameters = 0;
plugin->parameters = NULL;
plugin->parameter_names = NULL;
}
plugin->carla_plugin_id = -1;
rack->num_plugins++;
printf("Added plugin '%s' (URI: %s) to channel %d\n", plugin->name, plugin->uri, channel);
return rack->num_plugins - 1;
}
int carla_remove_plugin(CarlaHost *host, int channel, int plugin_index) {
if (!host || !host->initialized) return -1;
if (channel < 0 || channel >= 8) return -1;
ChannelRack *rack = &host->channel_racks[channel];
if (plugin_index < 0 || plugin_index >= rack->num_plugins) return -1;
PluginInfo *plugin = &rack->plugins[plugin_index];
if (plugin->parameters) {
free(plugin->parameters);
}
if (plugin->parameter_names) {
for (int i = 0; i < plugin->num_parameters; i++) {
free(plugin->parameter_names[i]);
}
free(plugin->parameter_names);
}
for (int i = plugin_index; i < rack->num_plugins - 1; i++) {
rack->plugins[i] = rack->plugins[i + 1];
}
rack->num_plugins--;
return 0;
}
int carla_set_parameter(CarlaHost *host, int channel, int plugin_index, int param_index, float value) {
if (!host || !host->initialized) return -1;
if (channel < 0 || channel >= 8) return -1;
ChannelRack *rack = &host->channel_racks[channel];
if (plugin_index < 0 || plugin_index >= rack->num_plugins) return -1;
PluginInfo *plugin = &rack->plugins[plugin_index];
if (param_index < 0 || param_index >= plugin->num_parameters) return -1;
plugin->parameters[param_index] = value;
return 0;
}
float carla_get_parameter(CarlaHost *host, int channel, int plugin_index, int param_index) {
if (!host || !host->initialized) return 0.0f;
if (channel < 0 || channel >= 8) return 0.0f;
ChannelRack *rack = &host->channel_racks[channel];
if (plugin_index < 0 || plugin_index >= rack->num_plugins) return 0.0f;
PluginInfo *plugin = &rack->plugins[plugin_index];
if (param_index < 0 || param_index >= plugin->num_parameters) return 0.0f;
return plugin->parameters[param_index];
}
void carla_set_channel_volume(CarlaHost *host, int channel, float volume) {
if (!host || !host->initialized) return;
if (channel < 0 || channel >= 8) return;
host->channel_racks[channel].volume = fmaxf(0.0f, fminf(2.0f, volume));
}
float carla_get_channel_volume(CarlaHost *host, int channel) {
if (!host || !host->initialized) return 1.0f;
if (channel < 0 || channel >= 8) return 1.0f;
return host->channel_racks[channel].volume;
}
void carla_process(CarlaHost *host, int channel, float *in_buffer, float *out_buffer, jack_nframes_t nframes) {
if (!host || !host->initialized) return;
if (channel < 0 || channel >= 8) return;
ChannelRack *rack = &host->channel_racks[channel];
if (rack->bypassed || rack->num_plugins == 0) {
for (jack_nframes_t i = 0; i < nframes; i++) {
out_buffer[i] = in_buffer[i] * rack->volume;
}
return;
}
float *current_in = in_buffer;
float *current_out = out_buffer;
float *temp_buffer = malloc(nframes * sizeof(float));
if (!temp_buffer) {
for (jack_nframes_t i = 0; i < nframes; i++) {
out_buffer[i] = in_buffer[i] * rack->volume;
}
return;
}
for (int p = 0; p < rack->num_plugins; p++) {
PluginInfo *plugin = &rack->plugins[p];
if (plugin->type == PLUGIN_TYPE_INTERNAL && strcmp(plugin->uri, "internal://gain") == 0) {
float gain = plugin->parameters[0];
for (jack_nframes_t i = 0; i < nframes; i++) {
current_out[i] = current_in[i] * gain;
}
}
if (p < rack->num_plugins - 1) {
float *swap = current_in;
current_in = current_out;
current_out = swap;
}
}
for (jack_nframes_t i = 0; i < nframes; i++) {
current_out[i] *= rack->volume;
}
if (current_out == temp_buffer) {
memcpy(out_buffer, temp_buffer, nframes * sizeof(float));
}
free(temp_buffer);
}
int carla_scan_plugins(CarlaHost *host) {
if (!host || !host->initialized) return -1;
if (host->available_plugins) {
for (int i = 0; i < host->num_available_plugins; i++) {
free(host->available_plugins[i]);
}
free(host->available_plugins);
}
const char *builtins[] = {
"internal://gain - Gain (Built-in)",
"internal://reverb - Reverb (Built-in)",
"internal://delay - Delay (Built-in)",
"internal://filter - Filter (Built-in)"
};
host->num_available_plugins = 4;
host->available_plugins = malloc(host->num_available_plugins * sizeof(char*));
for (int i = 0; i < host->num_available_plugins; i++) {
host->available_plugins[i] = strdup(builtins[i]);
}
return host->num_available_plugins;
}
const char** carla_get_available_plugins(CarlaHost *host, int *count) {
if (!host || !host->initialized) {
if (count) *count = 0;
return NULL;
}
if (count) *count = host->num_available_plugins;
return (const char**)host->available_plugins;
}
const char* carla_get_plugin_name(CarlaHost *host, int channel, int plugin_index) {
if (!host || !host->initialized) return NULL;
if (channel < 0 || channel >= 8) return NULL;
ChannelRack *rack = &host->channel_racks[channel];
if (plugin_index < 0 || plugin_index >= rack->num_plugins) return NULL;
return rack->plugins[plugin_index].name;
}