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/>.
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
);
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
);
56 cbox_io_env_copy(&engine
->io_env
, &rt
->io_env
);
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
);
76 struct cbox_objhdr
*cbox_engine_newfunc(struct cbox_class
*class_ptr
, struct cbox_document
*doc
)
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
;
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
]))
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
))
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.");
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
++)
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
];
137 if (!cbox_execute_on(fb
, NULL
, "/data", "b", error
, cbox_blob_new_acquire_data(data_i
, nframes
* 2 * sizeof(float))))
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
))
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
))
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
;
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
;
172 cbox_midi_buffer_clear(&engine
->midibuf_aux
);
173 cbox_midi_buffer_clear(&engine
->midibuf_song
);
175 cbox_io_get_midi_data(io
, &engine
->midibuf_jack
);
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
);
183 cbox_rt_handle_rt_commands(engine
->rt
);
185 // Combine various sources of events (song, non-RT thread, JACK input)
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
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).
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();
246 struct cbox_song_playback
*old_song
= engine
->spb
;
247 engine
->spb
= new_song
;
248 engine
->master
->spb
= 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
);
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
);
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
)
299 cbox_midi_merger_push(merger
, buffer
, engine
->rt
);
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
)
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
)
329 if (engine
->master
->state
== CMTS_ROLLING
)
331 if (engine
->spb
->song_pos_samples
== frame
)
333 engine
->master
->state
= CMTS_STOPPING
;
336 if (engine
->spb
&& engine
->spb
->song_pos_samples
!= frame
)
338 cbox_song_playback_seek_samples(engine
->spb
, frame
);
342 if (state
== ts_rolling
)
344 engine
->master
->state
= CMTS_ROLLING
;
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
;
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
);
369 struct cbox_scene
*scene
= (struct cbox_scene
*)objhdr
;
370 if (!CBOX_IS_A(scene
, cbox_scene
))
372 return &scene
->scene_input_merger
;