10 #include <jack/midiport.h>
12 #include "evoral/midi_events.h"
14 #include "ardour/midi_buffer.h"
22 using namespace ARDOUR;
24 BeatBox::BeatBox (int sr)
25 : _start_requested (false)
31 , _meter_beat_type (4)
37 , whole_note_superclocks (0)
38 , beat_superclocks (0)
39 , measure_superclocks (0)
40 , _quantize_divisor (4)
41 , clear_pending (false)
43 for (uint32_t n = 0; n < 1024; ++n) {
44 event_pool.push_back (new Event());
53 BeatBox::register_ports (jack_client_t* jack)
55 if ((_input = jack_port_register (jack, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0)) == 0) {
56 cerr << "no input port\n";
59 if ((_output = jack_port_register (jack, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0)) == 0) {
60 cerr << "no output port\n";
61 jack_port_unregister (jack, _input);
69 BeatBox::compute_tempo_clocks ()
71 whole_note_superclocks = (superclock_ticks_per_second * 60) / (_tempo / _meter_beat_type);
72 beat_superclocks = whole_note_superclocks / _meter_beat_type;
73 measure_superclocks = beat_superclocks * _meter_beats;
79 /* compute tempo, beat steps etc. */
81 compute_tempo_clocks ();
85 _start_requested = true;
91 _start_requested = false;
95 BeatBox::set_tempo (float bpm)
101 BeatBox::process (int nsamples)
104 if (_start_requested) {
106 last_start = superclock_cnt;
110 if (!_start_requested) {
115 superclock_t superclocks = samples_to_superclock (nsamples, _sample_rate);
117 if (_tempo_request) {
118 double ratio = _tempo / _tempo_request;
119 _tempo = _tempo_request;
122 compute_tempo_clocks ();
124 /* recompute all the event times based on the ratio between the
128 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
129 (*ee)->time = llrintf ((*ee)->time * ratio);
134 superclock_cnt += superclocks;
138 superclock_t process_start = superclock_cnt - last_start;
139 superclock_t process_end = process_start + superclocks;
140 const superclock_t loop_length = _measures * measure_superclocks;
141 const superclock_t orig_superclocks = superclocks;
143 process_start %= loop_length;
144 process_end %= loop_length;
146 bool two_pass_required;
147 superclock_t offset = 0;
149 if (process_end < process_start) {
150 two_pass_required = true;
151 process_end = loop_length;
152 superclocks = process_end - process_start;
154 two_pass_required = false;
157 unsigned char* buffer;
160 jack_midi_event_t in_event;
161 jack_nframes_t event_index;
163 /* do this on the first pass only */
164 out_buf = jack_port_get_buffer (_output, nsamples);
165 jack_midi_clear_buffer (out_buf);
173 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
174 event_pool.push_back (*ee);
176 _current_events.clear ();
177 _incomplete_notes.clear ();
178 clear_pending = false;
181 samplepos_t last_output_time = 0;
183 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
186 if (e->size && (e->time >= process_start && e->time < process_end)) {
187 samplepos_t sample_offset_in_buffer = superclock_to_samples (offset + e->time - process_start, _sample_rate);
188 if ((buffer = jack_midi_event_reserve (out_buf, sample_offset_in_buffer, e->size)) != 0) {
189 memcpy (buffer, e->buf, e->size);
190 outbound_tracker.track (e->buf);
191 last_output_time = sample_offset_in_buffer;
193 cerr << "Could not reserve space for output event @ " << e << " of size " << e->size << " @ " << offset + e->time - process_start
194 << " (samples: " << superclock_to_samples (offset + e->time - process_start, _sample_rate) << ") offset is " << offset
199 if (e->time >= process_end) {
206 in_buf = jack_port_get_buffer (_input, nsamples);
209 Events::iterator loop_iterator;
211 while (jack_midi_event_get (&in_event, in_buf, event_index++) == 0) {
213 superclock_t event_time = superclock_cnt + samples_to_superclock (in_event.time, _sample_rate);
214 superclock_t elapsed_time = event_time - last_start;
215 superclock_t in_loop_time = elapsed_time % loop_length;
216 superclock_t quantized_time;
218 if (_quantize_divisor != 0) {
219 const superclock_t time_per_grid_unit = whole_note_superclocks / _quantize_divisor;
221 if ((in_event.buffer[0] & 0xf) == MIDI_CMD_NOTE_OFF) {
223 /* note off is special - it must be quantized
224 * to at least 1 quantization "spacing" after
225 * the corresponding note on.
228 /* look for the note on */
230 IncompleteNotes::iterator ee;
232 for (ee = _incomplete_notes.begin(); ee != _incomplete_notes.end(); ++ee) {
233 /* check for same note and channel */
234 if (((*ee)->buf[1] == in_event.buffer[1]) && ((*ee)->buf[0] & 0xf) == (in_event.buffer[0] & 0xf)) {
235 quantized_time = (*ee)->time + time_per_grid_unit;
236 _incomplete_notes.erase (ee);
241 if (ee == _incomplete_notes.end()) {
242 cerr << "Note off for " << (int) (*ee)->buf[1] << " seen without corresponding note on among " << _incomplete_notes.size() << endl;
247 quantized_time = (in_loop_time / time_per_grid_unit) * time_per_grid_unit;
251 quantized_time = elapsed_time;
254 if (in_event.size > 24) {
255 cerr << "Ignored large MIDI event\n";
259 if (event_pool.empty()) {
260 cerr << "No more events, grow pool\n";
264 Event* e = event_pool.back();
265 event_pool.pop_back ();
267 e->time = quantized_time;
268 e->whole_note_superclocks = whole_note_superclocks;
269 e->size = in_event.size;
270 memcpy (e->buf, in_event.buffer, in_event.size);
272 inbound_tracker.track (e->buf);
274 _current_events.insert (e);
276 if ((e->buf[0] & 0xf) == MIDI_CMD_NOTE_ON) {
277 _incomplete_notes.push_back (e);
280 /* play it to out outputs so that we can hear it immediately */
281 /* XXX this smooshes together all inbound notes ... tricky */
282 if ((buffer = jack_midi_event_reserve (out_buf, last_output_time++, e->size)) != 0) {
283 memcpy (buffer, e->buf, e->size);
284 outbound_tracker.track (e->buf);
288 superclock_cnt += superclocks;
290 if (two_pass_required) {
291 offset = superclocks;
292 superclocks = orig_superclocks - superclocks;
294 process_end = superclocks;
295 two_pass_required = false;
303 BeatBox::set_quantize (int divisor)
305 _quantize_divisor = divisor;
311 clear_pending = true;
315 BeatBox::EventComparator::operator() (Event const * a, Event const *b) const
317 if (a->time == b->time) {
318 if (a->buf[0] == b->buf[0]) {
321 return !ARDOUR::MidiBuffer::second_simultaneous_midi_byte_is_first (a->buf[0], b->buf[0]);
323 return a->time < b->time;
327 process (jack_nframes_t nsamples, void* arg)
329 BeatBox* bbox = static_cast<BeatBox*> (arg);
330 return bbox->process (nsamples);
334 main (int argc, char* argv[])
337 const char *server_name = NULL;
338 jack_options_t options = JackNullOption;
339 jack_status_t status;
341 if ((jack = jack_client_open ("beatbox", options, &status, server_name)) == 0) {
342 cerr << "Could not connect to JACK\n";
346 BeatBox* bbox = new BeatBox (jack_get_sample_rate (jack));
347 BBGUI* gui = new BBGUI (&argc, &argv, jack, bbox);
349 bbox->register_ports (jack);
351 jack_set_process_callback (jack, process, bbox);
352 jack_activate (jack);