clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit 60d4e1206803b32a229d5fc99ea3d1e2fba89a58
parent 0bbf5e081ea63220842b03135adaedd38f4ffc4b
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date:   Thu, 20 Nov 2014 02:24:52 +0100

Started a jack host

Diffstat:
Mexamples/thyns/plugin.c | 72+++++++++++++-----------------------------------------------------------
Mexamples/thyns/thyns.h | 1+
Minclude/clap/clap-midi-parser.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/clap/clap-midi-parser.h | 11+++++++++++
Minclude/clap/clap.h | 19++++++++++---------
Mtools/CMakeLists.txt | 1+
Atools/clap-jack-host/CMakeLists.txt | 2++
Atools/clap-jack-host/clap-jack-host.c | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 339 insertions(+), 68 deletions(-)

diff --git a/examples/thyns/plugin.c b/examples/thyns/plugin.c @@ -9,7 +9,6 @@ struct thyns_plugin { struct thyns thyns; struct clap_plugin plugin; - struct clap_plugin_ports ports; struct clap_host *host; }; @@ -56,71 +55,28 @@ thyns_plugin_get_attribute(struct clap_plugin *plugin, #undef attr } -uint32_t -thyns_plugin_get_ports_configs_count(struct clap_plugin *plugin) +enum clap_process_status +thyns_plugin_process(struct clap_plugin *plugin, + struct clap_process *process) { - return 1; + return thyns_process(plugin->plugin_data, process); } -bool -thyns_plugin_get_ports_config(struct clap_plugin *plugin, - uint32_t config_index, - struct clap_ports_config *config) +void * +thyns_plugin_extension(struct clap_plugin *plugin, const char *extension) { - switch (config_index) { - case 0: - snprintf(config->name, sizeof (config->name), "mono"); - config->inputs_count = 0; - config->outputs_count = 1; - return true; - - default: - return false; - } + return NULL; } bool -thyns_plugin_get_port_info(struct clap_plugin *plugin, - uint32_t config_index, - uint32_t port_index, - struct clap_port_info *port) +thyns_plugin_activate(struct clap_plugin *plugin) { - switch (config_index) { - case 0: - switch (port_index) { - case 0: - snprintf(port->name, sizeof (port->name), "out"); - port->type = CLAP_PORT_MONO; - port->role = CLAP_PORT_INOUT; - port->is_repeatable = false; - return true;; - } - return false; - - default: - return false; - } return true; } -bool -thyns_plugin_set_ports_config(struct clap_plugin *plugin, - uint32_t config_index) -{ - switch (config_index) { - case 0: - return true; - - default: - return false; - } -} - -enum clap_process_status -thyns_plugin_process(struct clap_plugin *plugin, - struct clap_process *process) +void +thyns_plugin_deactivate(struct clap_plugin *plugin) { - return thyns_process(plugin->plugin_data, process); } struct thyns_plugin * @@ -143,12 +99,10 @@ thyns_plugin_create(struct clap_host *host, p->plugin.plugin_data = p; p->plugin.get_attribute = thyns_plugin_get_attribute; p->plugin.process = thyns_plugin_process; + p->plugin.extension = thyns_plugin_extension; + p->plugin.activate = thyns_plugin_activate; + p->plugin.deactivate = thyns_plugin_deactivate; - // initialize ports extension - p->ports.get_ports_configs_count = thyns_plugin_get_ports_configs_count; - p->ports.get_ports_config = thyns_plugin_get_ports_config; - p->ports.get_port_info = thyns_plugin_get_port_info; - p->ports.set_ports_config = thyns_plugin_set_ports_config; return p; } diff --git a/examples/thyns/thyns.h b/examples/thyns/thyns.h @@ -135,6 +135,7 @@ thyns_process(struct thyns *thyns, struct clap_process *process) // process process->output[0][i] = thyns_step(thyns, process); + process->output[1][i] = process->output[0][i]; } if (thyns->singing) diff --git a/include/clap/clap-midi-parser.c b/include/clap/clap-midi-parser.c @@ -61,6 +61,24 @@ clap_midi_parse_track(struct clap_midi_parser *parser) } static inline enum clap_midi_parser_status +clap_midi_parse_channel_event(struct clap_midi_parser *parser) +{ + if (parser->size < 3) + return CLAP_MIDI_PARSER_EOB; + + parser->channel.event_type = parser->in[0] & 0xf; + parser->channel.channel = parser->in[0] >> 4; + parser->channel.param1 = parser->in[1]; + parser->channel.param2 = parser->in[2]; + + parser->in += 3; + parser->size -= 3; + parser->track.size -= 3; + + return CLAP_MIDI_PARSER_CHANNEL; +} + +static inline enum clap_midi_parser_status clap_midi_parse_meta_event(struct clap_midi_parser *parser) { assert(parser->in[0] == 0xff); @@ -86,6 +104,8 @@ clap_midi_parse_meta_event(struct clap_midi_parser *parser) static inline enum clap_midi_parser_status clap_midi_parse_event(struct clap_midi_parser *parser) { + if ((parser->in[0] & 0xf) <= 0xe) + return clap_midi_parse_channel_event(parser); if (parser->in[0] == 0xff) return clap_midi_parse_meta_event(parser); return CLAP_MIDI_PARSER_ERROR; @@ -116,3 +136,41 @@ clap_midi_parse(struct clap_midi_parser *parser) return CLAP_MIDI_PARSER_ERROR; } } + +static inline void +clap_midi_convert(const uint8_t *in, + uint32_t size, + struct clap_event *event) +{ + struct clap_midi_parser parser; + parser.state = CLAP_MIDI_PARSER_TRACK; + parser.in = in; + parser.size = size; + parser.track.size = size; + + enum clap_midi_parser_status status = clap_midi_parse(&parser); + switch (status) { + case CLAP_MIDI_PARSER_CHANNEL: + switch (parser.channel.event_type) { + case CLAP_MIDI_CHANNEL_NOTE_OFF: + event->type = CLAP_EVENT_NOTE_OFF; + event->note.key = parser.channel.param1; + event->note.velocity = ((float)parser.channel.param2) / 127.0f; + event->note.events = NULL; + break; + + case CLAP_MIDI_CHANNEL_NOTE_ON: + event->type = CLAP_EVENT_NOTE_ON; + event->note.key = parser.channel.param1; + event->note.velocity = ((float)parser.channel.param2) / 127.0f; + event->note.events = NULL; + break; + } + + default: + event->type = CLAP_EVENT_MIDI; + event->midi.buffer = in; + event->midi.size = size; + break; + } +} diff --git a/include/clap/clap-midi-parser.h b/include/clap/clap-midi-parser.h @@ -10,6 +10,8 @@ # include <stdint.h> # include <string.h> +# include "clap.h" + enum clap_midi_parser_status { CLAP_MIDI_PARSER_EOB = -2, @@ -78,6 +80,7 @@ struct clap_midi_parser const uint8_t *in; uint32_t size; + /* result */ struct clap_midi_header header; struct clap_midi_track track; struct clap_midi_channel_event channel; @@ -88,6 +91,14 @@ struct clap_midi_parser static inline enum clap_midi_parser_status clap_midi_parse(struct clap_midi_parser *parser); +/* Converts a midi buffer in the state track, into a clap_event. + * If the midi data can't be converted into clap's events, it is then + * converted as a clap_midi_event. */ +static inline void +clap_midi_convert(const uint8_t *in, + uint32_t size, + struct clap_event *event); + # include "clap-midi-parser.c" #endif /* !CLAP_MIDI_PARSER_H */ diff --git a/include/clap/clap.h b/include/clap/clap.h @@ -130,6 +130,7 @@ struct clap_event_note { uint8_t key; float pitch; + float velocity; // 0..1 struct clap_event *events; // events specific to this note }; @@ -157,8 +158,8 @@ struct clap_event_preset struct clap_event_midi { - uint32_t size; - uint8_t *buffer; + const uint8_t *buffer; + uint32_t size; }; struct clap_event_latency @@ -200,9 +201,6 @@ enum clap_process_status struct clap_process { - /* host custom ptr */ - void *host_data; - /* audio buffers */ float **input; float **output; @@ -213,6 +211,8 @@ struct clap_process uint32_t tempo; // the tempo in samples uint64_t song_time; // the song time in samples uint64_t steady_time; // the steady time in samples + uint32_t loop_start_time; + uint32_t loop_end_time; /* events */ struct clap_event *events; @@ -227,10 +227,10 @@ struct clap_host uint32_t clap_version; // initialized to CLAP_VERSION /* returns the size of the original string, 0 if not string */ - uint32_t (*get_attribute)(struct clap_plugin *plugin, - const char *attr, - char *buffer, - uint32_t size); + uint32_t (*get_attribute)(struct clap_host *host, + const char *attr, + char *buffer, + uint32_t size); /* for events generated by the plugin. */ void (*events)(struct clap_host *host, @@ -244,6 +244,7 @@ struct clap_host /* Log a message through the host. */ void (*log)(struct clap_host *host, + struct clap_plugin *plugin, enum clap_log_severity severity, const char *msg); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(clap-info) +add_subdirectory(clap-jack-host) diff --git a/tools/clap-jack-host/CMakeLists.txt b/tools/clap-jack-host/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(clap-jack-host clap-jack-host.c) +target_link_libraries(clap-jack-host jack dl) diff --git a/tools/clap-jack-host/clap-jack-host.c b/tools/clap-jack-host/clap-jack-host.c @@ -0,0 +1,243 @@ +#include <stdlib.h> +#include <stdio.h> +#include <dlfcn.h> +#include <unistd.h> + +#include <clap/clap.h> +#include <clap/clap-midi-parser.h> + +#include <jack/jack.h> +#include <jack/midiport.h> + +struct clap_jack_host +{ + /* clap */ + struct clap_host host; + struct clap_plugin *plugin; + void *library_handle; + + /* host */ + uint32_t sample_rate; + uint64_t steady_time; + + /* jack */ + jack_client_t *client; + jack_port_t *input_ports[2]; + jack_port_t *output_ports[2]; + jack_port_t *midi_in; +}; + +static void deinitialize(struct clap_jack_host *app); + +static void host_events(struct clap_host *host, + struct clap_plugin *plugin, + struct clap_event *events) +{ +} + +static uint64_t host_steady_time(struct clap_host *host) +{ + return 0; +} + +static void *host_extension(struct clap_host *host, const char *extension_id) +{ + return NULL; +} + +static void host_log(struct clap_host *host, + struct clap_plugin *plugin, + enum clap_log_severity severity, + const char *msg) +{ + const char *severities[] = { + "debug", + "info", + "warning", + "error", + "fatal", + }; + + fprintf(stdout, "[%s] %s\n", severities[severity], msg); +} + +static uint32_t host_attribute(struct clap_host *host, + const char *attr, + char *buffer, + uint32_t size) +{ + return 0; +} + +int process(jack_nframes_t nframes, void *arg) +{ + struct clap_jack_host *app = arg; + + /* get jack ports */ + jack_default_audio_sample_t *in[2], *out[2]; + for (int i = 0; i < 2; ++i) { + in[i] = jack_port_get_buffer(app->input_ports[i], nframes); + out[i] = jack_port_get_buffer(app->output_ports[i], nframes); + } + void *midi_in_buf = jack_port_get_buffer(app->midi_in, nframes); + uint32_t midi_in_count = jack_midi_get_event_count(midi_in_buf); + + struct clap_process p; + p.input = in; + p.output = out; + p.samples_count = nframes; + p.is_offline = false; + // XXX add time info + + /* convert midi events */ + p.events = NULL; + struct clap_event *last_event = NULL; + for (uint32_t i = 0; i < midi_in_count; ++i) { + jack_midi_event_t midi; + jack_midi_event_get(&midi, midi_in_buf, i); + + struct clap_event *event = calloc(1, sizeof (*event)); + if (!event) + break; + if (last_event) + last_event->next = event; + else + p.events = event; + last_event = event; + + clap_midi_convert(midi.buffer, midi.size, event); + event->steady_time = app->steady_time + midi.time; + } + + /* process */ + app->plugin->process(app->plugin, &p); + + /* release events */ + while (p.events) { + struct clap_event *next = p.events->next; + free(p.events); + p.events = next; + } + + app->steady_time += nframes; + return 0; +} + +void shutdown(void *arg) +{ + deinitialize(arg); +} + +static bool initialize(struct clap_jack_host *app, + const char *plugin_path, + uint32_t plugin_index) +{ + /* jack */ + jack_status_t jack_status; + app->client = jack_client_open("clap-host", JackNullOption, &jack_status, NULL); + if (app->client == NULL) { + fprintf(stderr, "jack_client_open() failed, status: %d\n", jack_status); + return false; + } + + jack_set_process_callback(app->client, process, app); + jack_on_shutdown(app->client, shutdown, app); + app->input_ports[0] = jack_port_register(app->client, "input left", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + app->input_ports[1] = jack_port_register(app->client, "input right", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + app->output_ports[0] = jack_port_register(app->client, "output left", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + app->output_ports[1] = jack_port_register(app->client, "output right", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + app->midi_in = jack_port_register(app->client, "midi in", JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput, 0); + + printf("engine sample rate: %"PRIu32"\n", jack_get_sample_rate(app->client)); + + /* host initialization */ + app->host.clap_version = CLAP_VERSION; + app->host.events = host_events; + app->host.steady_time = host_steady_time; + app->host.extension = host_extension; + app->host.get_attribute = host_attribute; + app->host.log = host_log; + app->steady_time = 0; + app->sample_rate = jack_get_sample_rate(app->client); + + /* plugin initialization */ + app->library_handle = dlopen(plugin_path, RTLD_NOW | RTLD_LOCAL); + if (!app->library_handle) { + fprintf(stderr, "failed to load %s: %s\n", plugin_path, dlerror()); + goto fail_jack; + } + + union { + void *ptr; + clap_create_f clap_create; + } symbol; + + symbol.ptr = dlsym(app->library_handle, "clap_create"); + if (!symbol.ptr) { + fprintf(stderr, "symbol not found: clap_create\n"); + goto fail_dlclose; + } + + uint32_t plugin_count; + app->plugin = symbol.clap_create(plugin_index, &app->host, app->sample_rate, &plugin_count); + if (!app->plugin) { + fprintf(stderr, "failed to create plugin\n"); + goto fail_dlclose; + } + + return true; + +fail_dlclose: + dlclose(app->library_handle); +fail_jack: + jack_client_close(app->client); + return false; +} + +static void deinitialize(struct clap_jack_host *app) +{ + /* clap */ + app->plugin->destroy(app->plugin); + dlclose(app->library_handle); + + /* jack */ + jack_client_close(app->client); +} + +int main(int argc, char **argv) +{ + struct clap_jack_host app; + + if (argc != 3) { + fprintf(stderr, "usage: %s plugin.so index\n", argv[0]); + return 2; + } + + if (!initialize(&app, argv[1], atoi(argv[2]))) + return 1; + + if (!app.plugin->activate(app.plugin)) { + fprintf(stderr, "can't activate the plugin\n"); + return 1; + } + + if (jack_activate(app.client)) { + fprintf(stderr, "can't activate jack.\n"); + return 1; + } + + // wait until application closes + sleep(-1); + + deinitialize(&app); + return 0; +}