eb31fc0868e0bd88a155a544e3877c1a5c504831
[ardour.git] / tools / bb / bb.cc
1 #include <iostream>
2 #include <cstdio>
3 #include <cmath>
4 #include <cstring>
5
6 #include <unistd.h>
7 #include <stdint.h>
8
9 #include <jack/jack.h>
10 #include <jack/midiport.h>
11
12 #include "evoral/midi_events.h"
13
14 #include "ardour/midi_buffer.h"
15
16 #include "bb.h"
17 #include "gui.h"
18
19 using std::cerr;
20 using std::endl;
21
22 using namespace ARDOUR;
23
24 BeatBox::BeatBox (int sr)
25         : _start_requested (false)
26         , _running (false)
27         , _measures (2)
28         , _tempo (120)
29         , _tempo_request (0)
30         , _meter_beats (4)
31         , _meter_beat_type (4)
32         , _input (0)
33         , _output (0)
34         , superclock_cnt (0)
35         , last_start (0)
36         , _sample_rate (sr)
37         , whole_note_superclocks (0)
38         , beat_superclocks (0)
39         , measure_superclocks (0)
40         , _quantize_divisor (4)
41         , clear_pending (false)
42 {
43         for (uint32_t n = 0; n < 1024; ++n) {
44                 event_pool.push_back (new Event());
45         }
46 }
47
48 BeatBox::~BeatBox ()
49 {
50 }
51
52 int
53 BeatBox::register_ports (jack_client_t* jack)
54 {
55         if ((_input = jack_port_register (jack, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0)) == 0) {
56                 cerr << "no input port\n";
57                 return -1;
58         }
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);
62                 return -1;
63         }
64
65         return 0;
66 }
67
68 void
69 BeatBox::compute_tempo_clocks ()
70 {
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;
74 }
75
76 void
77 BeatBox::start ()
78 {
79         /* compute tempo, beat steps etc. */
80
81         compute_tempo_clocks ();
82
83         /* we can start */
84
85         _start_requested = true;
86 }
87
88 void
89 BeatBox::stop ()
90 {
91         _start_requested = false;
92 }
93
94 void
95 BeatBox::set_tempo (float bpm)
96 {
97         _tempo_request = bpm;
98 }
99
100 int
101 BeatBox::process (int nsamples)
102 {
103         if (!_running) {
104                 if (_start_requested) {
105                         _running = true;
106                         last_start = superclock_cnt;
107                 }
108
109         } else {
110                 if (!_start_requested) {
111                         _running = false;
112                 }
113         }
114
115         superclock_t superclocks = samples_to_superclock (nsamples, _sample_rate);
116
117         if (_tempo_request) {
118                 double ratio = _tempo / _tempo_request;
119                 _tempo = _tempo_request;
120                 _tempo_request = 0;
121
122                 compute_tempo_clocks ();
123
124                 /* recompute all the event times based on the ratio between the
125                  * new and old tempo.
126                  */
127
128                 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
129                         (*ee)->time = llrintf ((*ee)->time * ratio);
130                 }
131         }
132
133         if (!_running) {
134                 superclock_cnt += superclocks;
135                 return 0;
136         }
137
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;
142
143         process_start %= loop_length;
144         process_end   %= loop_length;
145
146         bool two_pass_required;
147         superclock_t offset = 0;
148
149         if (process_end < process_start) {
150                 two_pass_required = true;
151                 process_end = loop_length;
152                 superclocks = process_end - process_start;
153         } else {
154                 two_pass_required = false;
155         }
156
157         unsigned char* buffer;
158         void* out_buf;
159         void* in_buf;
160         jack_midi_event_t in_event;
161         jack_nframes_t event_index;
162
163         /* do this on the first pass only */
164         out_buf = jack_port_get_buffer (_output, nsamples);
165         jack_midi_clear_buffer (out_buf);
166
167   second_pass:
168
169         /* Output */
170
171         if (clear_pending) {
172
173                 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
174                         event_pool.push_back (*ee);
175                 }
176                 _current_events.clear ();
177                 _incomplete_notes.clear ();
178                 clear_pending = false;
179         }
180
181         framepos_t last_output_time = 0;
182
183         for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
184                 Event* e = (*ee);
185
186                 if (e->size && (e->time >= process_start && e->time < process_end)) {
187                         framepos_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;
192                         } else {
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
195                                      << ")\n";
196                         }
197                 }
198
199                 if (e->time >= process_end) {
200                         break;
201                 }
202         }
203
204         /* input */
205
206         in_buf = jack_port_get_buffer (_input, nsamples);
207         event_index = 0;
208
209         Events::iterator loop_iterator;
210
211         while (jack_midi_event_get (&in_event, in_buf, event_index++) == 0) {
212
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;
217
218                 if (_quantize_divisor != 0) {
219                         const superclock_t time_per_grid_unit = whole_note_superclocks / _quantize_divisor;
220
221                         if ((in_event.buffer[0] & 0xf) == MIDI_CMD_NOTE_OFF) {
222
223                                 /* note off is special - it must be quantized
224                                  * to at least 1 quantization "spacing" after
225                                  * the corresponding note on.
226                                  */
227
228                                 /* compute nominal time */
229
230                                 quantized_time = (in_loop_time / time_per_grid_unit) * time_per_grid_unit;
231
232                                 /* look for the note on */
233
234                                 IncompleteNotes::iterator ee;
235
236                                 for (ee = _incomplete_notes.begin(); ee != _incomplete_notes.end(); ++ee) {
237                                         /* check for same note and channel */
238                                         if (((*ee)->buf[1] == in_event.buffer[1]) && ((*ee)->buf[0] & 0xf) == (in_event.buffer[0] & 0xf)) {
239                                                 quantized_time = (*ee)->time + time_per_grid_unit;
240                                                 _incomplete_notes.erase (ee);
241                                                 break;
242                                         }
243                                 }
244
245                                 if (ee == _incomplete_notes.end()) {
246                                         cerr << "Note off for " << (int) (*ee)->buf[1] << " seen without corresponding note on among " << _incomplete_notes.size() << endl;
247                                         continue;
248                                 }
249
250                         } else {
251                                 quantized_time = (in_loop_time / time_per_grid_unit) * time_per_grid_unit;
252                         }
253
254                 } else {
255                         quantized_time = elapsed_time;
256                 }
257
258                 if (in_event.size > 24) {
259                         cerr << "Ignored large MIDI event\n";
260                         continue;
261                 }
262
263                 if (event_pool.empty()) {
264                         cerr << "No more events, grow pool\n";
265                         continue;
266                 }
267
268                 Event* e = event_pool.back();
269                 event_pool.pop_back ();
270
271                 e->time = quantized_time;
272                 e->whole_note_superclocks = whole_note_superclocks;
273                 e->size = in_event.size;
274                 memcpy (e->buf, in_event.buffer, in_event.size);
275
276                 inbound_tracker.track (e->buf);
277
278                 _current_events.insert (e);
279
280                 if ((e->buf[0] & 0xf) == MIDI_CMD_NOTE_ON) {
281                         _incomplete_notes.push_back (e);
282                 }
283
284                 /* play it to out outputs so that we can hear it immediately */
285                 /* XXX this smooshes together all inbound notes ... tricky */
286                 if ((buffer = jack_midi_event_reserve (out_buf, last_output_time++, e->size)) != 0) {
287                         memcpy (buffer, e->buf, e->size);
288                         outbound_tracker.track (e->buf);
289                 }
290         }
291
292         superclock_cnt += superclocks;
293
294         if (two_pass_required) {
295                 offset = superclocks;
296                 superclocks = orig_superclocks - superclocks;
297                 process_start = 0;
298                 process_end = superclocks;
299                 two_pass_required = false;
300                 goto second_pass;
301         }
302
303         return 0;
304 }
305
306 void
307 BeatBox::set_quantize (int divisor)
308 {
309         _quantize_divisor = divisor;
310 }
311
312 void
313 BeatBox::clear ()
314 {
315         clear_pending = true;
316 }
317
318 bool
319 BeatBox::EventComparator::operator() (Event const * a, Event const *b) const
320 {
321         if (a->time == b->time) {
322                 if (a->buf[0] == b->buf[0]) {
323                         return a < b;
324                 }
325                 return !ARDOUR::MidiBuffer::second_simultaneous_midi_byte_is_first (a->buf[0], b->buf[0]);
326         }
327         return a->time < b->time;
328 }
329
330 static int
331 process (jack_nframes_t nsamples, void* arg)
332 {
333         BeatBox* bbox = static_cast<BeatBox*> (arg);
334         return bbox->process (nsamples);
335 }
336
337 int
338 main (int argc, char* argv[])
339 {
340         jack_client_t* jack;
341         const char *server_name = NULL;
342         jack_options_t options = JackNullOption;
343         jack_status_t status;
344
345         if ((jack = jack_client_open ("beatbox", options, &status, server_name)) == 0) {
346                 cerr << "Could not connect to JACK\n";
347                 return -1;
348         }
349
350         BeatBox* bbox = new BeatBox (jack_get_sample_rate (jack));
351         BBGUI* gui = new BBGUI (&argc, &argv, jack, bbox);
352
353         bbox->register_ports (jack);
354
355         jack_set_process_callback (jack, process, bbox);
356         jack_activate (jack);
357
358         bbox->start ();
359
360         gui->run ();
361 }