Merge pull request #1 from atsampson/master
[calfbox.git] / engine.c
blob27f6402d24d23738f1aeed1ffc8129f1fc0632ae
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 "blob.h"
20 #include "dom.h"
21 #include "engine.h"
22 #include "instr.h"
23 #include "io.h"
24 #include "layer.h"
25 #include "midi.h"
26 #include "mididest.h"
27 #include "module.h"
28 #include "rt.h"
29 #include "scene.h"
30 #include "seq.h"
31 #include "song.h"
32 #include "stm.h"
33 #include "track.h"
34 #include <assert.h>
35 #include <stdio.h>
36 #include <unistd.h>
38 CBOX_CLASS_DEFINITION_ROOT(cbox_engine)
40 static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
42 struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt)
44 struct cbox_engine *engine = malloc(sizeof(struct cbox_engine));
45 CBOX_OBJECT_HEADER_INIT(engine, cbox_engine, doc);
47 engine->rt = rt;
48 engine->scenes = NULL;
49 engine->scene_count = 0;
50 engine->effect = NULL;
51 engine->master = cbox_master_new(engine);
52 engine->master->song = cbox_song_new(doc);
53 engine->spb = NULL;
55 if (rt)
56 cbox_io_env_copy(&engine->io_env, &rt->io_env);
57 else
59 engine->io_env.srate = 0; // must be overridden
60 engine->io_env.buffer_size = 256;
61 engine->io_env.input_count = 0;
62 engine->io_env.output_count = 2;
65 cbox_midi_buffer_init(&engine->midibuf_aux);
66 cbox_midi_buffer_init(&engine->midibuf_jack);
67 cbox_midi_buffer_init(&engine->midibuf_song);
68 cbox_midi_appsink_init(&engine->appsink, rt);
70 cbox_command_target_init(&engine->cmd_target, cbox_engine_process_cmd, engine);
71 CBOX_OBJECT_REGISTER(engine);
73 return engine;
76 struct cbox_objhdr *cbox_engine_newfunc(struct cbox_class *class_ptr, struct cbox_document *doc)
78 return NULL;
81 void cbox_engine_destroyfunc(struct cbox_objhdr *obj_ptr)
83 struct cbox_engine *engine = (struct cbox_engine *)obj_ptr;
84 while(engine->scene_count)
85 CBOX_DELETE(engine->scenes[0]);
86 if (engine->master->song)
88 CBOX_DELETE(engine->master->song);
89 engine->master->song = NULL;
91 cbox_master_destroy(engine->master);
92 engine->master = NULL;
94 free(engine);
97 static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
99 struct cbox_engine *engine = ct->user_data;
100 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
102 for (int i = 0; i < engine->scene_count; i++)
104 if (!cbox_execute_on(fb, NULL, "/scene", "o", error, engine->scenes[i]))
105 return FALSE;
107 return CBOX_OBJECT_DEFAULT_STATUS(engine, fb, error);
109 else if (!strcmp(cmd->command, "/render_stereo") && !strcmp(cmd->arg_types, "i"))
111 if (!cbox_check_fb_channel(fb, cmd->command, error))
112 return FALSE;
113 if (engine->rt && engine->rt->io)
115 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot use render function in real-time mode.");
116 return FALSE;
118 struct cbox_midi_buffer midibuf_song;
119 cbox_midi_buffer_init(&midibuf_song);
120 int nframes = CBOX_ARG_I(cmd, 0);
121 float *data = malloc(2 * nframes * sizeof(float));
122 float *data_i = malloc(2 * nframes * sizeof(float));
123 float *buffers[2] = { data, data + nframes };
124 for (int i = 0; i < nframes; i++)
126 buffers[0][i] = 0.f;
127 buffers[1][i] = 0.f;
129 cbox_engine_process(engine, NULL, nframes, buffers);
130 for (int i = 0; i < nframes; i++)
132 data_i[i * 2] = buffers[0][i];
133 data_i[i * 2 + 1] = buffers[1][i];
135 free(data);
137 if (!cbox_execute_on(fb, NULL, "/data", "b", error, cbox_blob_new_acquire_data(data_i, nframes * 2 * sizeof(float))))
138 return FALSE;
139 return TRUE;
141 else if (!strncmp(cmd->command, "/master_effect/",15))
143 return cbox_module_slot_process_cmd(&engine->effect, fb, cmd, cmd->command + 14, CBOX_GET_DOCUMENT(engine), engine->rt, engine, error);
145 else if (!strcmp(cmd->command, "/new_scene") && !strcmp(cmd->arg_types, ""))
147 if (!cbox_check_fb_channel(fb, cmd->command, error))
148 return FALSE;
150 struct cbox_scene *s = cbox_scene_new(CBOX_GET_DOCUMENT(engine), engine);
152 return s ? cbox_execute_on(fb, NULL, "/uuid", "o", error, s) : FALSE;
154 else if (!strcmp(cmd->command, "/new_recorder") && !strcmp(cmd->arg_types, "s"))
156 if (!cbox_check_fb_channel(fb, cmd->command, error))
157 return FALSE;
159 struct cbox_recorder *rec = cbox_recorder_new_stream(engine, engine->rt, CBOX_ARG_S(cmd, 0));
161 return rec ? cbox_execute_on(fb, NULL, "/uuid", "o", error, rec) : FALSE;
163 else
164 return cbox_object_default_process_cmd(ct, fb, cmd, error);
167 void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes, float **output_buffers)
169 struct cbox_module *effect = engine->effect;
170 uint32_t i, j;
172 cbox_midi_buffer_clear(&engine->midibuf_aux);
173 cbox_midi_buffer_clear(&engine->midibuf_song);
174 if (io)
175 cbox_io_get_midi_data(io, &engine->midibuf_jack);
176 else
177 cbox_midi_buffer_clear(&engine->midibuf_jack);
179 // Copy MIDI input to the app-sink with no timing information
180 cbox_midi_appsink_supply(&engine->appsink, &engine->midibuf_jack);
182 if (engine->rt)
183 cbox_rt_handle_rt_commands(engine->rt);
185 // Combine various sources of events (song, non-RT thread, JACK input)
186 if (engine->spb)
187 cbox_song_playback_render(engine->spb, &engine->midibuf_song, nframes);
189 for (int i = 0; i < engine->scene_count; i++)
190 cbox_scene_render(engine->scenes[i], nframes, output_buffers);
192 // Process "master" effect
193 if (effect)
195 for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE)
197 cbox_sample_t left[CBOX_BLOCK_SIZE], right[CBOX_BLOCK_SIZE];
198 cbox_sample_t *in_bufs[2] = {output_buffers[0] + i, output_buffers[1] + i};
199 cbox_sample_t *out_bufs[2] = {left, right};
200 (*effect->process_block)(effect, in_bufs, out_bufs);
201 for (j = 0; j < CBOX_BLOCK_SIZE; j++)
203 output_buffers[0][i + j] = left[j];
204 output_buffers[1][i + j] = right[j];
211 ////////////////////////////////////////////////////////////////////////////////////////
213 void cbox_engine_add_scene(struct cbox_engine *engine, struct cbox_scene *scene)
215 assert(scene->engine == engine);
217 cbox_rt_array_insert(engine->rt, (void ***)&engine->scenes, &engine->scene_count, -1, scene);
220 void cbox_engine_remove_scene(struct cbox_engine *engine, struct cbox_scene *scene)
222 assert(scene->engine == engine);
223 cbox_rt_array_remove_by_value(engine->rt, (void ***)&engine->scenes, &engine->scene_count, scene);
226 ////////////////////////////////////////////////////////////////////////////////////////
228 #define cbox_engine_set_song_playback_args(ARG) ARG(struct cbox_song_playback *, new_song) ARG(int, new_time_ppqn)
230 DEFINE_RT_VOID_FUNC(cbox_engine, engine, cbox_engine_set_song_playback)
232 // If there's no new song, silence all ongoing notes. Otherwise, copy the
233 // ongoing notes to the new playback structure so that the notes get released
234 // when playback is stopped (or possibly earlier).
235 if (engine->spb)
237 if (new_song)
238 cbox_song_playback_apply_old_state(new_song);
240 if (cbox_song_playback_active_notes_release(engine->spb, &engine->midibuf_aux) < 0)
242 RT_CALL_AGAIN_LATER();
243 return;
246 struct cbox_song_playback *old_song = engine->spb;
247 engine->spb = new_song;
248 engine->master->spb = new_song;
249 if (new_song)
251 if (new_time_ppqn == -1)
253 int old_time_ppqn = old_song ? old_song->song_pos_ppqn : 0;
254 cbox_song_playback_seek_samples(engine->master->spb, old_song ? old_song->song_pos_samples : 0);
255 // If tempo change occurred anywhere before playback point, then
256 // sample-based position corresponds to a completely different location.
257 // So, if new sample-based position corresponds to different PPQN
258 // position, seek again using PPQN position.
259 if (old_song && abs(new_song->song_pos_ppqn - old_time_ppqn) > 1)
260 cbox_song_playback_seek_ppqn(engine->master->spb, old_time_ppqn, FALSE);
262 else
263 cbox_song_playback_seek_ppqn(engine->master->spb, new_time_ppqn, FALSE);
267 void cbox_engine_update_song(struct cbox_engine *engine, int new_pos_ppqn)
269 struct cbox_song_playback *old_song, *new_song;
270 old_song = engine->spb;
271 new_song = cbox_song_playback_new(engine->master->song, engine->master, engine, old_song );
272 cbox_engine_set_song_playback(engine, new_song, new_pos_ppqn);
273 if (old_song)
274 cbox_song_playback_destroy(old_song);
277 ////////////////////////////////////////////////////////////////////////////////////////
279 void cbox_engine_update_song_playback(struct cbox_engine *engine)
281 cbox_engine_update_song(engine, -1);
284 ////////////////////////////////////////////////////////////////////////////////////////
286 void cbox_engine_update_input_connections(struct cbox_engine *engine)
288 for (int i = 0; i < engine->scene_count; i++)
289 cbox_scene_update_connected_inputs(engine->scenes[i]);
292 ////////////////////////////////////////////////////////////////////////////////////////
294 void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer)
296 if (!engine || !buffer)
297 return;
298 if (merger)
299 cbox_midi_merger_push(merger, buffer, engine->rt);
300 else
302 for (int i = 0; i < engine->scene_count; i++)
303 cbox_midi_merger_push(&engine->scenes[i]->scene_input_merger, buffer, engine->rt);
304 if (!engine->rt || !engine->rt->io)
305 return;
306 for (GSList *p = engine->rt->io->midi_outputs; p; p = p->next)
308 struct cbox_midi_output *midiout = p->data;
309 cbox_midi_merger_push(&midiout->merger, buffer, engine->rt);
314 ////////////////////////////////////////////////////////////////////////////////////////
316 gboolean cbox_engine_on_transport_sync(struct cbox_engine *engine, enum cbox_transport_state state, uint32_t frame)
318 if (state == ts_stopping)
320 if (engine->master->state == CMTS_ROLLING)
321 engine->master->state = engine->spb ? CMTS_STOPPING : CMTS_STOP;
323 return engine->master->state == CMTS_STOP;
325 if (state == ts_starting)
327 if (engine->master->state == CMTS_STOPPING)
328 return FALSE;
329 if (engine->master->state == CMTS_ROLLING)
331 if (engine->spb->song_pos_samples == frame)
332 return TRUE;
333 engine->master->state = CMTS_STOPPING;
334 return FALSE;
336 if (engine->spb && engine->spb->song_pos_samples != frame)
338 cbox_song_playback_seek_samples(engine->spb, frame);
340 return TRUE;
342 if (state == ts_rolling)
344 engine->master->state = CMTS_ROLLING;
345 return TRUE;
347 if (state == ts_stopped)
349 if (engine->master->state == CMTS_ROLLING)
350 engine->master->state = CMTS_STOPPING;
352 if (engine->master->state == CMTS_STOP && engine->spb && engine->spb->song_pos_samples != frame)
354 cbox_song_playback_seek_samples(engine->spb, frame);
357 return engine->master->state == CMTS_STOP;
359 return TRUE;
362 ////////////////////////////////////////////////////////////////////////////////////////
364 struct cbox_midi_merger *cbox_engine_get_midi_output(struct cbox_engine *engine, struct cbox_uuid *uuid)
366 struct cbox_objhdr *objhdr = cbox_document_get_object_by_uuid(CBOX_GET_DOCUMENT(engine), uuid);
367 if (!objhdr)
368 return NULL;
369 struct cbox_scene *scene = (struct cbox_scene *)objhdr;
370 if (!CBOX_IS_A(scene, cbox_scene))
371 return NULL;
372 return &scene->scene_input_merger;