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))))
142 if (!strcmp(cmd
->command
, "/new_scene") && !strcmp(cmd
->arg_types
, ""))
144 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
147 struct cbox_scene
*s
= cbox_scene_new(CBOX_GET_DOCUMENT(engine
), engine
);
149 return s
? cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, s
) : FALSE
;
152 if (!strcmp(cmd
->command
, "/new_recorder") && !strcmp(cmd
->arg_types
, "s"))
154 if (!cbox_check_fb_channel(fb
, cmd
->command
, error
))
157 struct cbox_recorder
*rec
= cbox_recorder_new_stream(engine
, engine
->rt
, CBOX_ARG_S(cmd
, 0));
159 return rec
? cbox_execute_on(fb
, NULL
, "/uuid", "o", error
, rec
) : FALSE
;
162 return cbox_object_default_process_cmd(ct
, fb
, cmd
, error
);
165 void cbox_engine_process(struct cbox_engine
*engine
, struct cbox_io
*io
, uint32_t nframes
, float **output_buffers
)
167 struct cbox_module
*effect
= engine
->effect
;
170 cbox_midi_buffer_clear(&engine
->midibuf_aux
);
171 cbox_midi_buffer_clear(&engine
->midibuf_song
);
173 cbox_io_get_midi_data(io
, &engine
->midibuf_jack
);
175 cbox_midi_buffer_clear(&engine
->midibuf_jack
);
177 // Copy MIDI input to the app-sink with no timing information
178 cbox_midi_appsink_supply(&engine
->appsink
, &engine
->midibuf_jack
);
181 cbox_rt_handle_rt_commands(engine
->rt
);
183 // Combine various sources of events (song, non-RT thread, JACK input)
185 cbox_song_playback_render(engine
->spb
, &engine
->midibuf_song
, nframes
);
187 for (int i
= 0; i
< engine
->scene_count
; i
++)
188 cbox_scene_render(engine
->scenes
[i
], nframes
, output_buffers
);
190 // Process "master" effect
193 for (i
= 0; i
< nframes
; i
+= CBOX_BLOCK_SIZE
)
195 cbox_sample_t left
[CBOX_BLOCK_SIZE
], right
[CBOX_BLOCK_SIZE
];
196 cbox_sample_t
*in_bufs
[2] = {output_buffers
[0] + i
, output_buffers
[1] + i
};
197 cbox_sample_t
*out_bufs
[2] = {left
, right
};
198 (*effect
->process_block
)(effect
, in_bufs
, out_bufs
);
199 for (j
= 0; j
< CBOX_BLOCK_SIZE
; j
++)
201 output_buffers
[0][i
+ j
] = left
[j
];
202 output_buffers
[1][i
+ j
] = right
[j
];
209 ////////////////////////////////////////////////////////////////////////////////////////
211 void cbox_engine_add_scene(struct cbox_engine
*engine
, struct cbox_scene
*scene
)
213 assert(scene
->engine
== engine
);
215 cbox_rt_array_insert(engine
->rt
, (void ***)&engine
->scenes
, &engine
->scene_count
, -1, scene
);
218 void cbox_engine_remove_scene(struct cbox_engine
*engine
, struct cbox_scene
*scene
)
220 assert(scene
->engine
== engine
);
221 cbox_rt_array_remove_by_value(engine
->rt
, (void ***)&engine
->scenes
, &engine
->scene_count
, scene
);
224 ////////////////////////////////////////////////////////////////////////////////////////
226 #define cbox_engine_set_song_playback_args(ARG) ARG(struct cbox_song_playback *, new_song) ARG(int, new_time_ppqn)
228 DEFINE_RT_VOID_FUNC(cbox_engine
, engine
, cbox_engine_set_song_playback
)
230 // If there's no new song, silence all ongoing notes. Otherwise, copy the
231 // ongoing notes to the new playback structure so that the notes get released
232 // when playback is stopped (or possibly earlier).
236 cbox_song_playback_apply_old_state(new_song
);
238 if (cbox_song_playback_active_notes_release(engine
->spb
, &engine
->midibuf_aux
) < 0)
240 RT_CALL_AGAIN_LATER();
244 struct cbox_song_playback
*old_song
= engine
->spb
;
245 engine
->spb
= new_song
;
246 engine
->master
->spb
= new_song
;
249 if (new_time_ppqn
== -1)
251 int old_time_ppqn
= old_song
? old_song
->song_pos_ppqn
: 0;
252 cbox_song_playback_seek_samples(engine
->master
->spb
, old_song
? old_song
->song_pos_samples
: 0);
253 // If tempo change occurred anywhere before playback point, then
254 // sample-based position corresponds to a completely different location.
255 // So, if new sample-based position corresponds to different PPQN
256 // position, seek again using PPQN position.
257 if (old_song
&& abs(new_song
->song_pos_ppqn
- old_time_ppqn
) > 1)
258 cbox_song_playback_seek_ppqn(engine
->master
->spb
, old_time_ppqn
, FALSE
);
261 cbox_song_playback_seek_ppqn(engine
->master
->spb
, new_time_ppqn
, FALSE
);
265 void cbox_engine_update_song(struct cbox_engine
*engine
, int new_pos_ppqn
)
267 struct cbox_song_playback
*old_song
, *new_song
;
268 old_song
= engine
->spb
;
269 new_song
= cbox_song_playback_new(engine
->master
->song
, engine
->master
, engine
, old_song
);
270 cbox_engine_set_song_playback(engine
, new_song
, new_pos_ppqn
);
272 cbox_song_playback_destroy(old_song
);
275 ////////////////////////////////////////////////////////////////////////////////////////
277 void cbox_engine_update_song_playback(struct cbox_engine
*engine
)
279 cbox_engine_update_song(engine
, -1);
282 ////////////////////////////////////////////////////////////////////////////////////////
284 void cbox_engine_update_input_connections(struct cbox_engine
*engine
)
286 for (int i
= 0; i
< engine
->scene_count
; i
++)
287 cbox_scene_update_connected_inputs(engine
->scenes
[i
]);
290 ////////////////////////////////////////////////////////////////////////////////////////
292 void cbox_engine_send_events_to(struct cbox_engine
*engine
, struct cbox_midi_merger
*merger
, struct cbox_midi_buffer
*buffer
)
294 if (!engine
|| !buffer
)
297 cbox_midi_merger_push(merger
, buffer
, engine
->rt
);
300 for (int i
= 0; i
< engine
->scene_count
; i
++)
301 cbox_midi_merger_push(&engine
->scenes
[i
]->scene_input_merger
, buffer
, engine
->rt
);
302 if (!engine
->rt
|| !engine
->rt
->io
)
304 for (GSList
*p
= engine
->rt
->io
->midi_outputs
; p
; p
= p
->next
)
306 struct cbox_midi_output
*midiout
= p
->data
;
307 cbox_midi_merger_push(&midiout
->merger
, buffer
, engine
->rt
);
312 ////////////////////////////////////////////////////////////////////////////////////////
314 gboolean
cbox_engine_on_transport_sync(struct cbox_engine
*engine
, enum cbox_transport_state state
, uint32_t frame
)
316 if (state
== ts_stopping
)
318 if (engine
->master
->state
== CMTS_ROLLING
)
319 engine
->master
->state
= engine
->spb
? CMTS_STOPPING
: CMTS_STOP
;
321 return engine
->master
->state
== CMTS_STOP
;
323 if (state
== ts_starting
)
325 if (engine
->master
->state
== CMTS_STOPPING
)
327 if (engine
->master
->state
== CMTS_ROLLING
)
329 if (engine
->spb
->song_pos_samples
== frame
)
331 engine
->master
->state
= CMTS_STOPPING
;
334 if (engine
->spb
&& engine
->spb
->song_pos_samples
!= frame
)
336 cbox_song_playback_seek_samples(engine
->spb
, frame
);
340 if (state
== ts_rolling
)
342 engine
->master
->state
= CMTS_ROLLING
;
345 if (state
== ts_stopped
)
347 if (engine
->master
->state
== CMTS_ROLLING
)
348 engine
->master
->state
= CMTS_STOPPING
;
350 if (engine
->master
->state
== CMTS_STOP
&& engine
->spb
&& engine
->spb
->song_pos_samples
!= frame
)
352 cbox_song_playback_seek_samples(engine
->spb
, frame
);
355 return engine
->master
->state
== CMTS_STOP
;
360 ////////////////////////////////////////////////////////////////////////////////////////
362 struct cbox_midi_merger
*cbox_engine_get_midi_output(struct cbox_engine
*engine
, struct cbox_uuid
*uuid
)
364 struct cbox_objhdr
*objhdr
= cbox_document_get_object_by_uuid(CBOX_GET_DOCUMENT(engine
), uuid
);
367 struct cbox_scene
*scene
= (struct cbox_scene
*)objhdr
;
368 if (!CBOX_IS_A(scene
, cbox_scene
))
370 return &scene
->scene_input_merger
;