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/>.
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
));
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
);
71 struct cbox_objhdr
*cbox_engine_newfunc(struct cbox_class
*class_ptr
, struct cbox_document
*doc
)
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
);
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
;
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
);
115 if (!cbox_midi_buffer_can_store_msg(appsink
, event
->size
))
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
);
130 cbox_scene_render(engine
->scene
, nframes
, &engine
->midibuf_total
, io
->output_buffers
);
132 // Process "master" 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
)
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
)
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();
180 struct cbox_song_playback
*old_song
= engine
->master
->spb
;
182 engine
->scene
->spb
= new_song
;
183 engine
->master
->spb
= 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
);
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
);
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
)
226 cbox_midi_merger_push(merger
, buffer
, engine
->rt
);
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
]);
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
)
263 return cbox_engine_get_input_midi_data_(engine
);