clap

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

commit 1e3b2e208083f780fbea9302e2c3548359221f11
parent c926cd520c29d7b2a4d858395152669fef32dec5
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date:   Mon, 29 Jun 2015 22:03:01 +0200

Cool clap alsa host! :D

Diffstat:
Mtools/CMakeLists.txt | 1+
Atools/clap-alsa-host/CMakeLists.txt | 2++
Atools/clap-alsa-host/clap-alsa-host.c | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 389 insertions(+), 0 deletions(-)

diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(clap-info) +add_subdirectory(clap-alsa-host) add_subdirectory(clap-jack-host) diff --git a/tools/clap-alsa-host/CMakeLists.txt b/tools/clap-alsa-host/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(clap-alsa-host clap-alsa-host.c) +target_link_libraries(clap-alsa-host asound dl m) diff --git a/tools/clap-alsa-host/clap-alsa-host.c b/tools/clap-alsa-host/clap-alsa-host.c @@ -0,0 +1,386 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <stdio.h> +#include <dlfcn.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> + +#include <clap/clap.h> +#include <clap/midi/parser.h> +#include <clap/ext/gui.h> +#include <clap/ext/state.h> + +#include <alsa/asoundlib.h> + +#define SAMPLES_COUNT 512 +#define SAMPLE_RATE 48000 + +struct clap_alsa_host +{ + /* clap */ + struct clap_host host; + struct clap_plugin *plugin; + void *library_handle; + + /* host */ + uint32_t sample_rate; + uint64_t steady_time; + + /* alsa */ + snd_pcm_t *pcm; + snd_seq_t *seq; + int seq_port; + + /* buffers */ + float out[2][SAMPLES_COUNT]; + + /* config */ + const char *state_path; + + /* state */ + bool quit; +}; + +struct clap_alsa_host *g_app = NULL; + +static void deinitialize(struct clap_alsa_host *app); + +static void host_events(struct clap_host *host, + struct clap_plugin *plugin, + struct clap_event *events) +{ +} + +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; +} + +bool convert_seq_event(snd_seq_event_t *seq_ev, struct clap_event *clap_ev) +{ + switch (seq_ev->type) + { + case SND_SEQ_EVENT_NOTEON: + clap_ev->type = CLAP_EVENT_NOTE_ON; + clap_ev->note.key = seq_ev->data.note.note; + clap_ev->note.velocity = seq_ev->data.note.velocity; + clap_ev->note.pitch = clap_midi_pitches[clap_ev->note.key]; + return true; + + case SND_SEQ_EVENT_NOTEOFF: + clap_ev->type = CLAP_EVENT_NOTE_OFF; + clap_ev->note.key = seq_ev->data.note.note; + clap_ev->note.velocity = seq_ev->data.note.velocity; + clap_ev->note.pitch = clap_midi_pitches[clap_ev->note.key]; + return true; + + default: + return false; + } +} + +int process(struct clap_alsa_host *app) +{ + float *in[2] = { NULL, NULL }; + float *out[2] = { app->out[0], app->out[1] }; + + struct clap_process p; + p.inputs = in; + p.outputs = out; + p.samples_count = SAMPLES_COUNT; + p.steady_time = app->steady_time; + p.events = NULL; + // XXX add time info + + /* convert midi events */ + struct clap_event *last_event = NULL; + snd_seq_event_t *seq_ev = NULL; + while (snd_seq_event_input(app->seq, &seq_ev) >= 0) { + if (!seq_ev) + break; + + struct clap_event *event = calloc(1, sizeof (*event)); + if (!event) + break; + + if (convert_seq_event(seq_ev, event)) + { + if (last_event) + last_event->next = event; + else + p.events = event; + last_event = event; + event->steady_time = app->steady_time;// seq_ev->time; + } + else + { + free(event); + } + + snd_seq_free_event(seq_ev); + } + + /* process */ + app->plugin->process(app->plugin, &p); + + void *buffs[2] = { out[0], out[1] }; + int err = snd_pcm_writen(app->pcm, buffs, SAMPLES_COUNT); + if (err < 0) + fprintf(stderr, "snd_pcm_writen: %s\n", snd_strerror(err)); + + /* release events */ + while (p.events) { + struct clap_event *next = p.events->next; + free(p.events); + p.events = next; + } + + app->steady_time += SAMPLES_COUNT; + return 0; +} + +void shutdown(void *arg) +{ +} + +static bool initialize(struct clap_alsa_host *app, + const char *device, + const char *plugin_path, + uint32_t plugin_index) +{ + snd_pcm_hw_params_t *hwparams; + int err; + + app->quit = false; + + /* alsa/pcm initialization */ + err = snd_pcm_open(&app->pcm, device, SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) { + fprintf(stderr, "cannot open %s: %s", device, snd_strerror(err)); + return false; + } + + err = snd_pcm_set_params(app->pcm, SND_PCM_FORMAT_FLOAT, + SND_PCM_ACCESS_RW_NONINTERLEAVED, + 2, SAMPLE_RATE, true, 512); + + if (err < 0) { + fprintf(stderr, "failed to set the pcm params: %s\n", snd_strerror(err)); + goto fail_pcm; + } + + /* alsa/seq initialization */ + err = snd_seq_open(&app->seq, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK); + if (err < 0) { + fprintf(stderr, "failed to open sequencer: %s\n", snd_strerror(err)); + goto fail_pcm; + } + + err = snd_seq_set_client_name(app->seq, "clap-alsa-host"); + if (err < 0) { + fprintf(stderr, "snd_seq_set_client_name: %s\n", snd_strerror(err)); + goto fail_seq; + } + + app->seq_port = snd_seq_create_simple_port( + app->seq, "port", + SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ| + SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_MIDI_GENERIC); + if (app->seq_port < 0) { + fprintf(stderr, "snd_seq_create_simple_port: %s\n", snd_strerror(app->seq_port)); + goto fail_seq; + } + + /* host initialization */ + app->host.clap_version = CLAP_VERSION; + app->host.events = host_events; + app->host.steady_time = &app->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 = SAMPLE_RATE; + + /* 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_seq; + } + + 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_seq: + snd_seq_close(app->seq); +fail_pcm: + snd_pcm_close(app->pcm); + return false; +} + +static void load_state(struct clap_alsa_host *app) +{ + struct stat st; + struct clap_plugin_state *state = app->plugin->extension( + app->plugin, CLAP_EXT_STATE); + + if (!state) + return; + + if (!app->state_path) + return; + + int fd = open(app->state_path, O_RDONLY); + if (fd < 0) + return; + + if (fstat(fd, &st)) { + close(fd); + return; + } + + void *mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) { + close(fd); + return; + } + + state->restore(app->plugin, mem, st.st_size); + + munmap(mem, st.st_size); + close(fd); +} + +static void save_state(struct clap_alsa_host *app) +{ + struct clap_plugin_state *state = app->plugin->extension( + app->plugin, CLAP_EXT_STATE); + + if (!state) { + fprintf(stdout, "no state extension\n"); + return; + } + + if (!app->state_path) { + fprintf(stdout, "no state path\n"); + return; + } + + void *buffer = NULL; + uint32_t size = 0; + if (!state->save(app->plugin, &buffer, &size)) { + fprintf(stdout, "failed to save the state\n"); + return; + } + + int fd = open(app->state_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + fprintf(stdout, "open(%s): %m\n", app->state_path); + return; + } + + write(fd, buffer, size); + close(fd); +} + +static void deinitialize(struct clap_alsa_host *app) +{ + /* clap */ + save_state(app); + app->plugin->deactivate(app->plugin); + app->plugin->destroy(app->plugin); + dlclose(app->library_handle); + + /* alsa */ + snd_pcm_close(app->pcm); +} + +void sig_int(int sig) +{ + g_app->quit = true; +} + +int main(int argc, char **argv) +{ + static struct clap_alsa_host app; + + g_app = &app; + + signal(SIGINT, sig_int); + + if (argc < 3) { + fprintf(stderr, "usage: %s plugin.so index [state]\n", argv[0]); + return 2; + } + + if (!initialize(&app, "default", argv[1], atoi(argv[2]))) + return 1; + + if (!app.plugin->activate(app.plugin)) { + fprintf(stderr, "can't activate the plugin\n"); + return 1; + } + + if (argc == 4) { + app.state_path = argv[3]; + load_state(&app); + } else + app.state_path = NULL; + + struct clap_plugin_gui *gui = app.plugin->extension(app.plugin, CLAP_EXT_GUI); + if (gui) + gui->open(app.plugin); + + while (!app.quit) + process(&app); + + deinitialize(&app); + return 0; +}