control: define and export GOption related entities
[ng-jackspa.git] / jackspa.c
blobb67afe3e67013c29025e8410a7eead4a3005595f
1 /* jackspa.c - LADSPA plugin instance with JACK audio ports
2 * Copyright © 2007 Nick Thomas
3 * Copyright © 2013 Géraud Meyer <graud@gmx.com>
5 * jackspa.c is part of ng-jackspa.
7 * ng-jackpsa is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License version 2 as published by the
9 * Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <dlfcn.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "jackspa.h"
27 /* Finds the plugin descriptor with the given ID in the given object file,
28 * storing it in state->descriptor. Returns 1 on success. On failure, returns 0
29 * and sets *error to an appropriate error message.
31 int find_plugin(state_t *state, const char *file, const unsigned long id,
32 const char **error)
34 int err = 0;
35 unsigned long i; /* port index */
36 void *library;
37 LADSPA_Descriptor *(*descriptor_fun)(unsigned long index);
38 LADSPA_Descriptor *descriptor;
40 /* Open the library. */
41 library = dlopen(file, RTLD_LAZY);
42 if (!library)
43 err = 1, *error = dlerror();
45 /* Find the ladspa_descriptor() function. */
46 if (!err) {
47 descriptor_fun = (LADSPA_Descriptor *(*)(unsigned long))
48 dlsym(library, "ladspa_descriptor");
49 if (!descriptor_fun)
50 err = 1, *error = dlerror();
53 /* Find the appropriate descriptor. */
54 for (i = 0; !err; i++) {
55 descriptor = descriptor_fun(i);
57 if (!descriptor)
58 err = 1, *error = "no such plugin index in the given file";
59 else if (descriptor->UniqueID == id) {
60 state->descriptor = descriptor;
61 break;
65 return !err;
68 /* The JACK processing callback. */
69 int process(jack_nframes_t nframes, void *arg)
71 state_t *state = (state_t *)arg;
72 unsigned long p; /* loop variable for ports */
73 LADSPA_Data buf; /* buffer for the control outputs */
75 /* Connect audio ports and unused control ports */
76 for (p = 0; p < state->descriptor->PortCount; p++)
77 if (LADSPA_IS_PORT_AUDIO(state->descriptor->PortDescriptors[p]))
78 state->descriptor->connect_port
79 ( state->handle, p,
80 (LADSPA_Data *)jack_port_get_buffer(state->jack_ports[p], nframes) );
81 else if ( LADSPA_IS_PORT_CONTROL(state->descriptor->PortDescriptors[p]) &&
82 LADSPA_IS_PORT_OUTPUT(state->descriptor->PortDescriptors[p]) )
83 state->descriptor->connect_port(state->handle, p, &buf);
84 /* forget the control output ports values */
86 /* Run the plugin. */
87 state->descriptor->run(state->handle, nframes);
89 return 0;
92 /* Initializes JACK, opening the appropriate ports, instantiating the LADSPA
93 * Plugin, and starting the synthesis thread running.. Returns 1 on success. On
94 * failure, returns 0 and sets *error to an appropriate error message.
96 int init_jack(state_t *state, const char **error)
98 static char client_name_prefix[] = "jackspa_";
99 int err = 0;
100 jack_status_t jack_status;
101 unsigned long p; /* loop variable for ports */
102 unsigned long flags;
104 /* Allocate memory for the client name. */
105 state->client_name = (char *)calloc
106 ( sizeof(client_name_prefix) + strlen(state->descriptor->Label),
107 sizeof(char) );
108 if (!state->client_name)
109 err = 1, *error = strerror(errno);
111 /* Set the client name. */
112 if (!err) {
113 /* state->client_name[0] = '\0'; */ /* done by calloc */
114 strcat(state->client_name, client_name_prefix);
115 strcat(state->client_name, state->descriptor->Label);
118 /* Open JACK. */
119 if (!err) {
120 state->jack_client =
121 jack_client_open(state->client_name, JackNullOption, &jack_status);
122 if (!state->jack_client)
123 err = 1, *error = "could not connect to JACK";
126 /* Free memory for the client name */
127 free(state->client_name);
128 if (!err) state->client_name = jack_get_client_name(state->jack_client);
130 /* Allocate memory for the list of ports. */
131 if (!err) {
132 state->jack_ports = (jack_port_t **)calloc
133 (state->descriptor->PortCount, sizeof(jack_port_t *));
134 if (!state->jack_ports)
135 err = 1, *error = strerror(errno);
138 /* Allocate memory for the list of control port values. */
139 if (!err) {
140 state->control_port_values = (LADSPA_Data *)calloc
141 ((size_t)state->descriptor->PortCount, sizeof(LADSPA_Data));
142 if (!state->control_port_values)
143 err = 1, *error = strerror(errno);
146 /* Register ports. */
147 state->num_control_ports = 0;
148 for (p = 0; !err && p < state->descriptor->PortCount; p++) {
149 if ( LADSPA_IS_PORT_CONTROL(state->descriptor-> PortDescriptors[p]) &&
150 LADSPA_IS_PORT_INPUT(state->descriptor->PortDescriptors[p]) )
152 state->num_control_ports++;
153 continue;
156 if (LADSPA_IS_PORT_INPUT(state->descriptor->PortDescriptors[p]))
157 flags = JackPortIsInput;
158 else if (LADSPA_IS_PORT_AUDIO(state->descriptor->PortDescriptors[p]))
159 flags = JackPortIsOutput;
160 else
161 continue; /* skip the control outputs (aka meters) */
163 state->jack_ports[p] = jack_port_register
164 ( state->jack_client, state->descriptor->PortNames[p],
165 JACK_DEFAULT_AUDIO_TYPE, flags, 0 );
167 if (!state->jack_ports[p])
168 err = 1, *error = "could not register JACK ports";
171 /* Register our processing callback. */
172 if (!err)
173 if (jack_set_process_callback(state->jack_client, &process, state))
174 err = 1, *error = "could not register the JACK processing callback";
176 /* Instantiate the LADSPA plugin. */
177 if (!err) {
178 state->handle = state->descriptor->instantiate
179 (state->descriptor, jack_get_sample_rate(state->jack_client));
180 if (!state->handle)
181 err = 1, *error = "could not instantiate the plugin.";
184 /* Connect input control ports. */
185 if (!err)
186 for (p = 0; p < state->descriptor->PortCount; p++)
187 if ( LADSPA_IS_PORT_CONTROL(state->descriptor->PortDescriptors[p]) &&
188 LADSPA_IS_PORT_INPUT(state->descriptor->PortDescriptors[p]))
190 state->descriptor->connect_port
191 (state->handle, p, &state->control_port_values[p]);
192 /* state->control_port_values[p] = 0.0; */ /* done by calloc() */
195 /* Activate the LADSPA plugin. */
196 if (!err)
197 if (state->descriptor->activate)
198 state->descriptor->activate(state->handle);
200 /* Get the bits flowing. */
201 if (!err)
202 if (jack_activate(state->jack_client))
203 err = 1, *error = "could not activate audio processing";
205 return !err;
208 typedef enum {
209 st_want_plugin_file,
210 st_want_plugin_id,
211 st_done
212 } arg_parser_state;
214 /* Parses command-line arguments. Returns 1 on success. On failure, returns 0
215 * and sets *error to an appropriate error message.
217 int parse_args(state_t *state, int argc, char **argv, char **plugin_file,
218 unsigned long *plugin_id, const char **error)
220 int err = 0;
221 int i; /* arg index */
222 arg_parser_state st = st_want_plugin_file;
224 argc--, argv++; /* skip the program name */
226 for (i = 0; i < argc; i++)
227 switch (st) {
228 case st_want_plugin_file:
229 *plugin_file = argv[i];
230 st = st_want_plugin_id;
231 break;
233 case st_want_plugin_id:
234 *plugin_id = atol(argv[i]);
235 st = st_done;
236 break;
238 case st_done:
239 default:
240 err = 1, *error = "extra arguments given";
241 break;
244 if (st != st_done)
245 err = 1, *error = "must supply a plugin file and a plugin ID";
247 return !err;
250 int jackspa_init(state_t *state, int argc, char **argv)
252 const char *error;
253 int err = 0;
254 char *plugin_file;
255 unsigned long plugin_id;
257 state->jack_client = 0;
259 err = !parse_args(state, argc, argv, &plugin_file, &plugin_id, &error);
261 if (!err)
262 err = !find_plugin(state, plugin_file, plugin_id, &error);
264 if (!err)
265 err = !init_jack(state, &error);
267 if (err)
268 fprintf(stderr, "jackspa error: %s\n", error);
270 return !err;