Fix automatic to-string conversion for status objects.
[calfbox.git] / engine.c
blob392b81252f51b6ead77fe254f9309c4b6017d944
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 "instr.h"
22 #include "io.h"
23 #include "layer.h"
24 #include "midi.h"
25 #include "module.h"
26 #include "rt.h"
27 #include "scene.h"
28 #include "seq.h"
29 #include "song.h"
30 #include "stm.h"
31 #include "track.h"
32 #include <assert.h>
33 #include <stdio.h>
34 #include <unistd.h>
36 CBOX_CLASS_DEFINITION_ROOT(cbox_engine)
38 static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
40 struct cbox_engine *cbox_engine_new(struct cbox_rt *rt)
42 struct cbox_engine *engine = malloc(sizeof(struct cbox_engine));
43 CBOX_OBJECT_HEADER_INIT(engine, cbox_engine, CBOX_GET_DOCUMENT(rt));
45 engine->rt = rt;
46 engine->scene = NULL;
47 engine->effect = NULL;
48 engine->master = cbox_master_new(engine);
49 engine->master->song = cbox_song_new(CBOX_GET_DOCUMENT(rt));
50 cbox_midi_buffer_init(&engine->midibuf_aux);
51 cbox_midi_buffer_init(&engine->midibuf_jack);
52 cbox_midi_buffer_init(&engine->midibuf_song);
53 cbox_midi_buffer_init(&engine->midibuf_total);
54 cbox_midi_merger_init(&engine->scene_input_merger, &engine->midibuf_total);
56 cbox_midi_merger_connect(&engine->scene_input_merger, &engine->midibuf_aux, NULL);
57 cbox_midi_merger_connect(&engine->scene_input_merger, &engine->midibuf_jack, NULL);
58 cbox_midi_merger_connect(&engine->scene_input_merger, &engine->midibuf_song, NULL);
60 cbox_midi_buffer_init(&engine->midibufs_appsink[0]);
61 cbox_midi_buffer_init(&engine->midibufs_appsink[1]);
62 engine->current_appsink_buffer = 0;
64 cbox_command_target_init(&engine->cmd_target, cbox_engine_process_cmd, engine);
65 CBOX_OBJECT_REGISTER(engine);
66 cbox_document_set_service(CBOX_GET_DOCUMENT(rt), "engine", &engine->_obj_hdr);
68 return engine;
71 struct cbox_objhdr *cbox_engine_newfunc(struct cbox_class *class_ptr, struct cbox_document *doc)
73 return NULL;
76 void cbox_engine_destroyfunc(struct cbox_objhdr *obj_ptr)
78 struct cbox_engine *engine = (struct cbox_engine *)obj_ptr;
79 if (engine->master->song)
81 CBOX_DELETE(engine->master->song);
82 engine->master->song = NULL;
84 cbox_master_destroy(engine->master);
85 engine->master = NULL;
87 cbox_midi_merger_close(&engine->scene_input_merger);
89 free(engine);
92 static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
94 // struct cbox_engine *engine = ct->user_data;
95 return cbox_object_default_process_cmd(ct, fb, cmd, error);
98 void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes)
100 struct cbox_module *effect = engine->effect;
101 uint32_t i, j;
103 cbox_midi_buffer_clear(&engine->midibuf_aux);
104 cbox_midi_buffer_clear(&engine->midibuf_song);
105 cbox_midi_buffer_clear(&engine->midibuf_total);
106 cbox_io_get_midi_data(io, &engine->midibuf_jack);
108 // Copy MIDI input to the app-sink with no timing information
109 struct cbox_midi_buffer *appsink = &engine->midibufs_appsink[engine->current_appsink_buffer];
110 for (int i = 0; i < engine->midibuf_jack.count; i++)
112 const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&engine->midibuf_jack, i);
113 if (event)
115 if (!cbox_midi_buffer_can_store_msg(appsink, event->size))
116 break;
117 cbox_midi_buffer_copy_event(appsink, event, 0);
121 cbox_rt_handle_rt_commands(engine->rt);
123 // Combine various sources of events (song, non-RT thread, JACK input)
124 if (engine->scene && engine->scene->spb)
125 cbox_song_playback_render(engine->scene->spb, &engine->midibuf_song, nframes);
127 cbox_midi_merger_render(&engine->scene_input_merger);
129 if (engine->scene)
130 cbox_scene_render(engine->scene, nframes, &engine->midibuf_total, io->output_buffers);
132 // Process "master" effect
133 if (effect)
135 for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE)
137 cbox_sample_t left[CBOX_BLOCK_SIZE], right[CBOX_BLOCK_SIZE];
138 cbox_sample_t *in_bufs[2] = {io->output_buffers[0] + i, io->output_buffers[1] + i};
139 cbox_sample_t *out_bufs[2] = {left, right};
140 (*effect->process_block)(effect, in_bufs, out_bufs);
141 for (j = 0; j < CBOX_BLOCK_SIZE; j++)
143 io->output_buffers[0][i + j] = left[j];
144 io->output_buffers[1][i + j] = right[j];
151 ////////////////////////////////////////////////////////////////////////////////////////
153 struct cbox_scene *cbox_engine_set_scene(struct cbox_engine *engine, struct cbox_scene *scene)
155 if (scene)
156 scene->spb = engine->master->spb;
157 return cbox_rt_swap_pointers(engine->rt, (void **)&engine->scene, scene);
160 ////////////////////////////////////////////////////////////////////////////////////////
162 #define cbox_engine_set_song_playback_args(ARG) ARG(struct cbox_song_playback *, new_song) ARG(int, new_time_ppqn)
164 DEFINE_RT_VOID_FUNC(cbox_engine, engine, cbox_engine_set_song_playback)
166 // If there's no new song, silence all ongoing notes. Otherwise, copy the
167 // ongoing notes to the new playback structure so that the notes get released
168 // when playback is stopped (or possibly earlier).
169 if (engine->scene && engine->scene->spb)
171 if (new_song)
172 cbox_song_playback_apply_old_state(new_song);
174 if (cbox_song_playback_active_notes_release(engine->scene->spb, &engine->midibuf_aux) < 0)
176 RT_CALL_AGAIN_LATER();
177 return;
180 struct cbox_song_playback *old_song = engine->master->spb;
181 if (engine->scene)
182 engine->scene->spb = new_song;
183 engine->master->spb = new_song;
184 if (new_song)
186 if (new_time_ppqn == -1)
188 int old_time_ppqn = old_song ? old_song->song_pos_ppqn : 0;
189 cbox_song_playback_seek_samples(engine->master->spb, old_song ? old_song->song_pos_samples : 0);
190 // If tempo change occurred anywhere before playback point, then
191 // sample-based position corresponds to a completely different location.
192 // So, if new sample-based position corresponds to different PPQN
193 // position, seek again using PPQN position.
194 if (old_song && abs(new_song->song_pos_ppqn - old_time_ppqn) > 1)
195 cbox_song_playback_seek_ppqn(engine->master->spb, old_time_ppqn, FALSE);
197 else
198 cbox_song_playback_seek_ppqn(engine->master->spb, new_time_ppqn, FALSE);
202 void cbox_engine_update_song(struct cbox_engine *engine, int new_pos_ppqn)
204 struct cbox_song_playback *old_song, *new_song;
205 old_song = engine->scene ? engine->scene->spb : NULL;
206 new_song = cbox_song_playback_new(engine->master->song, engine->master, engine, old_song );
207 cbox_engine_set_song_playback(engine, new_song, new_pos_ppqn);
208 if (old_song)
209 cbox_song_playback_destroy(old_song);
212 ////////////////////////////////////////////////////////////////////////////////////////
214 void cbox_engine_update_song_playback(struct cbox_engine *engine)
216 cbox_engine_update_song(engine, -1);
219 ////////////////////////////////////////////////////////////////////////////////////////
221 void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer)
223 if (!engine || !buffer)
224 return;
225 if (merger)
226 cbox_midi_merger_push(merger, buffer, engine->rt);
227 else
229 cbox_midi_merger_push(&engine->scene_input_merger, buffer, engine->rt);
230 for (GSList *p = engine->rt->io->midi_outputs; p; p = p->next)
232 struct cbox_midi_output *midiout = p->data;
233 cbox_midi_merger_push(&midiout->merger, buffer, engine->rt);
238 ////////////////////////////////////////////////////////////////////////////////////////
240 #define cbox_engine_get_input_midi_data__args(ARG)
242 DEFINE_RT_FUNC(const struct cbox_midi_buffer *, cbox_engine, engine, cbox_engine_get_input_midi_data_)
244 const struct cbox_midi_buffer *ret = NULL;
245 if (engine->midibufs_appsink[engine->current_appsink_buffer].count)
247 // return the current buffer, switch to the new, empty one
248 ret = &engine->midibufs_appsink[engine->current_appsink_buffer];
249 engine->current_appsink_buffer = 1 - engine->current_appsink_buffer;
250 cbox_midi_buffer_clear(&engine->midibufs_appsink[engine->current_appsink_buffer]);
253 return ret;
256 const struct cbox_midi_buffer *cbox_engine_get_input_midi_data(struct cbox_engine *engine)
258 // This checks the counter from the 'wrong' thread, but that's OK, it's
259 // just to avoid doing any RT work when input buffer is completely empty.
260 // Any further access/manipulation is done via RT cmd.
261 if (!engine->midibufs_appsink[engine->current_appsink_buffer].count)
262 return NULL;
263 return cbox_engine_get_input_midi_data_(engine);