Mark the ad-hoc pattern as completed only after all the notes have been processed.
[calfbox.git] / rt.c
blobf5ffb609da8055780e6450c1c1485f5cdc2652e4
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2013 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "dom.h"
20 #include "engine.h"
21 #include "io.h"
22 #include "midi.h"
23 #include "module.h"
24 #include "rt.h"
25 #include "stm.h"
26 #include <assert.h>
27 #include <stdio.h>
28 #include <unistd.h>
30 CBOX_CLASS_DEFINITION_ROOT(cbox_rt)
32 static void cbox_rt_process(void *user_data, struct cbox_io *io, uint32_t nframes);
34 struct cbox_rt_cmd_instance
36 struct cbox_rt_cmd_definition *definition;
37 void *user_data;
38 int is_async;
41 static gboolean cbox_rt_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
43 struct cbox_rt *rt = ct->user_data;
44 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
46 if (!cbox_check_fb_channel(fb, cmd->command, error))
47 return FALSE;
48 if (rt->io)
50 GError *cerror = NULL;
51 if (cbox_io_get_disconnect_status(rt->io, &cerror))
53 return cbox_execute_on(fb, NULL, "/audio_channels", "ii", error, rt->io->io_env.input_count, rt->io->io_env.output_count) &&
54 cbox_execute_on(fb, NULL, "/state", "is", error, 1, "OK") &&
55 CBOX_OBJECT_DEFAULT_STATUS(rt, fb, error);
57 else
59 return cbox_execute_on(fb, NULL, "/audio_channels", "ii", error, rt->io->io_env.input_count, rt->io->io_env.output_count) &&
60 cbox_execute_on(fb, NULL, "/state", "is", error, -1, cerror ? cerror->message : "Unknown error") &&
61 CBOX_OBJECT_DEFAULT_STATUS(rt, fb, error);
64 else
65 return cbox_execute_on(fb, NULL, "/audio_channels", "ii", error, 0, 2) &&
66 cbox_execute_on(fb, NULL, "/state", "is", error, 0, "Offline") &&
67 CBOX_OBJECT_DEFAULT_STATUS(rt, fb, error);
69 else if (!strcmp(cmd->command, "/cycle") && !strcmp(cmd->arg_types, ""))
71 if (rt->io && !cbox_io_get_disconnect_status(rt->io, NULL))
73 return cbox_io_cycle(rt->io, fb, error);
75 else
77 if (rt->io)
78 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Already connected");
79 else
80 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot cycle connection in off-line mode");
81 return FALSE;
84 else if (!strncmp(cmd->command, "/engine/", 8))
85 return cbox_execute_sub(&rt->engine->cmd_target, fb, cmd, cmd->command + 7, error);
86 else
87 return cbox_object_default_process_cmd(ct, fb, cmd, error);
90 struct cbox_rt *cbox_rt_new(struct cbox_document *doc)
92 struct cbox_rt *rt = malloc(sizeof(struct cbox_rt));
93 CBOX_OBJECT_HEADER_INIT(rt, cbox_rt, doc);
94 rt->rb_execute = cbox_fifo_new(sizeof(struct cbox_rt_cmd_instance) * RT_CMD_QUEUE_ITEMS);
95 rt->rb_cleanup = cbox_fifo_new(sizeof(struct cbox_rt_cmd_instance) * RT_CMD_QUEUE_ITEMS * 2);
96 rt->io = NULL;
97 rt->engine = NULL;
98 rt->started = FALSE;
99 rt->disconnected = FALSE;
100 rt->io_env.srate = 0;
101 rt->io_env.buffer_size = 0;
103 cbox_command_target_init(&rt->cmd_target, cbox_rt_process_cmd, rt);
104 CBOX_OBJECT_REGISTER(rt);
105 cbox_document_set_service(doc, "rt", &rt->_obj_hdr);
107 return rt;
110 struct cbox_objhdr *cbox_rt_newfunc(struct cbox_class *class_ptr, struct cbox_document *doc)
112 return NULL;
115 void cbox_rt_destroyfunc(struct cbox_objhdr *obj_ptr)
117 struct cbox_rt *rt = (void *)obj_ptr;
118 cbox_fifo_destroy(rt->rb_execute);
119 cbox_fifo_destroy(rt->rb_cleanup);
121 free(rt);
124 static void cbox_rt_on_disconnected(void *user_data)
126 struct cbox_rt *rt = user_data;
127 rt->disconnected = TRUE;
130 static void cbox_rt_on_reconnected(void *user_data)
132 struct cbox_rt *rt = user_data;
133 rt->disconnected = FALSE;
136 static void cbox_rt_on_midi_outputs_changed(void *user_data)
138 struct cbox_rt *rt = user_data;
139 if (rt->engine)
140 cbox_engine_update_song_playback(rt->engine);
143 static void cbox_rt_on_midi_inputs_changed(void *user_data)
145 struct cbox_rt *rt = user_data;
146 if (rt->engine)
147 cbox_engine_update_input_connections(rt->engine);
150 void cbox_rt_on_update_io_env(struct cbox_rt *rt)
152 if (rt->engine)
154 cbox_io_env_copy(&rt->engine->io_env, &rt->io_env);
155 cbox_master_set_sample_rate(rt->engine->master, rt->io_env.srate);
159 void cbox_rt_set_io(struct cbox_rt *rt, struct cbox_io *io)
161 assert(!rt->started);
162 rt->io = io;
163 if (io)
165 cbox_io_env_copy(&rt->io_env, &io->io_env);
166 cbox_rt_on_update_io_env(rt);
168 else
170 cbox_io_env_clear(&rt->io_env);
174 void cbox_rt_set_offline(struct cbox_rt *rt, int sample_rate, int buffer_size)
176 assert(!rt->started);
177 rt->io = NULL;
178 rt->io_env.srate = sample_rate;
179 rt->io_env.buffer_size = buffer_size;
180 rt->io_env.input_count = 0;
181 rt->io_env.output_count = 2;
182 cbox_rt_on_update_io_env(rt);
185 void cbox_rt_on_started(void *user_data)
187 struct cbox_rt *rt = user_data;
188 rt->started = 1;
191 void cbox_rt_on_stopped(void *user_data)
193 struct cbox_rt *rt = user_data;
194 rt->started = 0;
197 gboolean cbox_rt_on_transport_sync(void *user_data, enum cbox_transport_state state, uint32_t frame)
199 struct cbox_rt *rt = user_data;
200 if (!rt->engine)
201 return TRUE;
202 return cbox_engine_on_transport_sync(rt->engine, state, frame);
205 void cbox_rt_start(struct cbox_rt *rt, struct cbox_command_target *fb)
207 if (rt->io)
209 rt->cbs = calloc(1, sizeof(struct cbox_io_callbacks));
210 rt->cbs->user_data = rt;
211 rt->cbs->process = cbox_rt_process;
212 rt->cbs->on_started = cbox_rt_on_started;
213 rt->cbs->on_stopped = cbox_rt_on_stopped;
214 rt->cbs->on_disconnected = cbox_rt_on_disconnected;
215 rt->cbs->on_reconnected = cbox_rt_on_reconnected;
216 rt->cbs->on_midi_inputs_changed = cbox_rt_on_midi_inputs_changed;
217 rt->cbs->on_midi_outputs_changed = cbox_rt_on_midi_outputs_changed;
218 rt->cbs->on_transport_sync = cbox_rt_on_transport_sync;
220 assert(!rt->started);
221 cbox_io_start(rt->io, rt->cbs, fb);
222 assert(rt->started);
226 void cbox_rt_stop(struct cbox_rt *rt)
228 if (rt->io)
230 assert(rt->started);
231 cbox_io_stop(rt->io);
232 free(rt->cbs);
233 rt->cbs = NULL;
234 assert(!rt->started);
238 void cbox_rt_handle_cmd_queue(struct cbox_rt *rt)
240 struct cbox_rt_cmd_instance cmd;
242 while(cbox_fifo_read_atomic(rt->rb_cleanup, &cmd, sizeof(cmd)))
244 cmd.definition->cleanup(cmd.user_data);
248 static void wait_write_space(struct cbox_fifo *rb)
250 int t = 0;
251 while (cbox_fifo_writespace(rb) < sizeof(struct cbox_rt_cmd_instance))
253 // wait until some space frees up in the execute queue
254 usleep(1000);
255 t++;
256 if (t >= 1000)
258 fprintf(stderr, "Execute queue full, waiting...\n");
259 t = 0;
264 void cbox_rt_execute_cmd_sync(struct cbox_rt *rt, struct cbox_rt_cmd_definition *def, void *user_data)
266 struct cbox_rt_cmd_instance cmd;
268 if (def->prepare)
269 if (def->prepare(user_data))
270 return;
272 // No realtime thread - do it all in the main thread
273 if (!rt || !rt->started || rt->disconnected)
275 while (!def->execute(user_data))
277 if (def->cleanup)
278 def->cleanup(user_data);
279 return;
282 memset(&cmd, 0, sizeof(cmd));
283 cmd.definition = def;
284 cmd.user_data = user_data;
285 cmd.is_async = 0;
287 wait_write_space(rt->rb_execute);
288 cbox_fifo_write_atomic(rt->rb_execute, &cmd, sizeof(cmd));
291 struct cbox_rt_cmd_instance cmd2;
293 if (!cbox_fifo_read_atomic(rt->rb_cleanup, &cmd2, sizeof(cmd2)))
295 // still no result in cleanup queue - wait
296 usleep(10000);
297 continue;
299 if (!memcmp(&cmd, &cmd2, sizeof(cmd)))
301 if (def->cleanup)
302 def->cleanup(user_data);
303 break;
305 // async command - clean it up
306 if (cmd2.definition->cleanup)
307 cmd2.definition->cleanup(cmd2.user_data);
308 } while(1);
311 void cbox_rt_execute_cmd_async(struct cbox_rt *rt, struct cbox_rt_cmd_definition *def, void *user_data)
313 struct cbox_rt_cmd_instance cmd = { def, user_data, 1 };
315 if (def->prepare)
317 if (def->prepare(user_data))
318 return;
320 // No realtime thread - do it all in the main thread
321 if (!rt || !rt->started || rt->disconnected)
323 while (!def->execute(user_data))
325 if (def->cleanup)
326 def->cleanup(user_data);
327 return;
330 wait_write_space(rt->rb_execute);
331 cbox_fifo_write_atomic(rt->rb_execute, &cmd, sizeof(cmd));
333 // will be cleaned up by next sync call or by cbox_rt_cmd_handle_queue
336 ////////////////////////////////////////////////////////////////////////////////////////
338 void cbox_rt_process(void *user_data, struct cbox_io *io, uint32_t nframes)
340 struct cbox_rt *rt = user_data;
341 if (rt->engine)
342 cbox_engine_process(rt->engine, io, nframes, io->output_buffers);
343 else
344 cbox_rt_handle_rt_commands(rt);
347 ////////////////////////////////////////////////////////////////////////////////////////
349 void cbox_rt_handle_rt_commands(struct cbox_rt *rt)
351 struct cbox_rt_cmd_instance cmd;
353 // Process command queue, needs engine's MIDI aux buf to be initialised to work
354 int cost = 0;
355 while(cost < RT_MAX_COST_PER_CALL && cbox_fifo_peek(rt->rb_execute, &cmd, sizeof(cmd)))
357 int result = (cmd.definition->execute)(cmd.user_data);
358 if (!result)
359 break;
360 cost += result;
361 cbox_fifo_consume(rt->rb_execute, sizeof(cmd));
362 if (cmd.definition->cleanup || !cmd.is_async)
364 gboolean success = cbox_fifo_write_atomic(rt->rb_cleanup, (const char *)&cmd, sizeof(cmd));
365 if (!success)
366 g_error("Clean-up FIFO full. Main thread deadlock?");
371 ////////////////////////////////////////////////////////////////////////////////////////
373 #define cbox_rt_swap_pointers_args(ARG) ARG(void **, ptr) ARG(void *, new_value)
375 DEFINE_RT_FUNC(void *, cbox_rt, rt, cbox_rt_swap_pointers)
377 void *old_value = *ptr;
378 *ptr = new_value;
379 return old_value;
382 #define cbox_rt_swap_pointers_and_update_count_args(ARG) ARG(void **, ptr) ARG(void *, new_value) ARG(int *, pcount) ARG(int, new_count)
384 DEFINE_RT_FUNC(void *, cbox_rt, rt, cbox_rt_swap_pointers_and_update_count)
386 void *old_value = *ptr;
387 *ptr = new_value;
388 if (pcount)
389 *pcount = new_count;
390 return old_value;
393 void cbox_rt_array_insert(struct cbox_rt *rt, void ***ptr, int *pcount, int index, void *new_value)
395 assert(index >= -1);
396 assert(index <= *pcount);
397 assert(*pcount >= 0);
398 void **new_array = stm_array_clone_insert(*ptr, *pcount, index, new_value);
399 free(cbox_rt_swap_pointers_and_update_count(rt, (void **)ptr, new_array, pcount, *pcount + 1));
402 void *cbox_rt_array_remove(struct cbox_rt *rt, void ***ptr, int *pcount, int index)
404 if (index == -1)
405 index = *pcount - 1;
406 assert(index >= 0);
407 assert(index < *pcount);
408 assert(*pcount > 0);
409 void *p = (*ptr)[index];
410 void **new_array = stm_array_clone_remove(*ptr, *pcount, index);
411 free(cbox_rt_swap_pointers_and_update_count(rt, (void **)ptr, new_array, pcount, *pcount - 1));
412 return p;
415 gboolean cbox_rt_array_remove_by_value(struct cbox_rt *rt, void ***ptr, int *pcount, void *value_to_remove)
417 for (int i = 0; i < *pcount; i++)
419 if ((*ptr)[i] == value_to_remove)
421 cbox_rt_array_remove(rt, ptr, pcount, i);
422 return TRUE;
425 return FALSE;
428 ////////////////////////////////////////////////////////////////////////////////////////
430 struct cbox_midi_merger *cbox_rt_get_midi_output(struct cbox_rt *rt, struct cbox_uuid *uuid)
432 if (rt->engine)
434 struct cbox_midi_merger *merger = cbox_engine_get_midi_output(rt->engine, uuid);
435 if (merger)
436 return merger;
438 if (!rt->io)
439 return NULL;
441 struct cbox_midi_output *midiout = cbox_io_get_midi_output(rt->io, NULL, uuid);
442 return midiout ? &midiout->merger : NULL;
445 ////////////////////////////////////////////////////////////////////////////////////////