add tempo adjustment
[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 "bb.h"
15 #include "gui.h"
16
17 using std::cerr;
18 using std::endl;
19
20
21 static bool
22 second_simultaneous_midi_byte_is_first (uint8_t a, uint8_t b)
23 {
24         bool b_first = false;
25
26         /* two events at identical times. we need to determine
27            the order in which they should occur.
28
29            the rule is:
30
31            Controller messages
32            Program Change
33            Note Off
34            Note On
35            Note Pressure
36            Channel Pressure
37            Pitch Bend
38         */
39
40         if ((a) >= 0xf0 || (b) >= 0xf0 || ((a & 0xf) != (b & 0xf))) {
41
42                 /* if either message is not a channel message, or if the channels are
43                  * different, we don't care about the type.
44                  */
45
46                 b_first = true;
47
48         } else {
49
50                 switch (b & 0xf0) {
51                 case MIDI_CMD_CONTROL:
52                         b_first = true;
53                         break;
54
55                 case MIDI_CMD_PGM_CHANGE:
56                         switch (a & 0xf0) {
57                         case MIDI_CMD_CONTROL:
58                                 break;
59                         case MIDI_CMD_PGM_CHANGE:
60                         case MIDI_CMD_NOTE_OFF:
61                         case MIDI_CMD_NOTE_ON:
62                         case MIDI_CMD_NOTE_PRESSURE:
63                         case MIDI_CMD_CHANNEL_PRESSURE:
64                         case MIDI_CMD_BENDER:
65                                 b_first = true;
66                         }
67                         break;
68
69                 case MIDI_CMD_NOTE_OFF:
70                         switch (a & 0xf0) {
71                         case MIDI_CMD_CONTROL:
72                         case MIDI_CMD_PGM_CHANGE:
73                                 break;
74                         case MIDI_CMD_NOTE_OFF:
75                         case MIDI_CMD_NOTE_ON:
76                         case MIDI_CMD_NOTE_PRESSURE:
77                         case MIDI_CMD_CHANNEL_PRESSURE:
78                         case MIDI_CMD_BENDER:
79                                 b_first = true;
80                         }
81                         break;
82
83                 case MIDI_CMD_NOTE_ON:
84                         switch (a & 0xf0) {
85                         case MIDI_CMD_CONTROL:
86                         case MIDI_CMD_PGM_CHANGE:
87                         case MIDI_CMD_NOTE_OFF:
88                                 break;
89                         case MIDI_CMD_NOTE_ON:
90                         case MIDI_CMD_NOTE_PRESSURE:
91                         case MIDI_CMD_CHANNEL_PRESSURE:
92                         case MIDI_CMD_BENDER:
93                                 b_first = true;
94                         }
95                         break;
96                 case MIDI_CMD_NOTE_PRESSURE:
97                         switch (a & 0xf0) {
98                         case MIDI_CMD_CONTROL:
99                         case MIDI_CMD_PGM_CHANGE:
100                         case MIDI_CMD_NOTE_OFF:
101                         case MIDI_CMD_NOTE_ON:
102                                 break;
103                         case MIDI_CMD_NOTE_PRESSURE:
104                         case MIDI_CMD_CHANNEL_PRESSURE:
105                         case MIDI_CMD_BENDER:
106                                 b_first = true;
107                         }
108                         break;
109
110                 case MIDI_CMD_CHANNEL_PRESSURE:
111                         switch (a & 0xf0) {
112                         case MIDI_CMD_CONTROL:
113                         case MIDI_CMD_PGM_CHANGE:
114                         case MIDI_CMD_NOTE_OFF:
115                         case MIDI_CMD_NOTE_ON:
116                         case MIDI_CMD_NOTE_PRESSURE:
117                                 break;
118                         case MIDI_CMD_CHANNEL_PRESSURE:
119                         case MIDI_CMD_BENDER:
120                                 b_first = true;
121                         }
122                         break;
123                 case MIDI_CMD_BENDER:
124                         switch (a & 0xf0) {
125                         case MIDI_CMD_CONTROL:
126                         case MIDI_CMD_PGM_CHANGE:
127                         case MIDI_CMD_NOTE_OFF:
128                         case MIDI_CMD_NOTE_ON:
129                         case MIDI_CMD_NOTE_PRESSURE:
130                         case MIDI_CMD_CHANNEL_PRESSURE:
131                                 break;
132                         case MIDI_CMD_BENDER:
133                                 b_first = true;
134                         }
135                         break;
136                 }
137         }
138
139         return b_first;
140 }
141
142 BeatBox::BeatBox (int sr)
143         : _start_requested (false)
144         , _running (false)
145         , _measures (2)
146         , _tempo (120)
147         , _tempo_request (0)
148         , _meter_beats (4)
149         , _meter_beat_type (4)
150         , _input (0)
151         , _output (0)
152         , superclock_cnt (0)
153         , last_start (0)
154         , _sample_rate (sr)
155         , whole_note_superclocks (0)
156         , beat_superclocks (0)
157         , measure_superclocks (0)
158         , _quantize_divisor (4)
159         , clear_pending (false)
160 {
161         for (uint32_t n = 0; n < 1024; ++n) {
162                 event_pool.push_back (new Event());
163         }
164 }
165
166 BeatBox::~BeatBox ()
167 {
168 }
169
170 int
171 BeatBox::register_ports (jack_client_t* jack)
172 {
173         if ((_input = jack_port_register (jack, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0)) == 0) {
174                 cerr << "no input port\n";
175                 return -1;
176         }
177         if ((_output = jack_port_register (jack, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0)) == 0) {
178                 cerr << "no output port\n";
179                 jack_port_unregister (jack, _input);
180                 return -1;
181         }
182
183         return 0;
184 }
185
186 void
187 BeatBox::compute_tempo_clocks ()
188 {
189         whole_note_superclocks = (superclock_ticks_per_second * 60) / (_tempo / _meter_beat_type);
190         beat_superclocks = whole_note_superclocks / _meter_beat_type;
191         measure_superclocks = beat_superclocks * _meter_beats;
192 }
193
194 void
195 BeatBox::start ()
196 {
197         /* compute tempo, beat steps etc. */
198
199         compute_tempo_clocks ();
200
201         /* we can start */
202
203         _start_requested = true;
204 }
205
206 void
207 BeatBox::stop ()
208 {
209         _start_requested = false;
210 }
211
212 void
213 BeatBox::set_tempo (float bpm)
214 {
215         _tempo_request = bpm;
216 }
217
218 int
219 BeatBox::process (int nsamples)
220 {
221         if (!_running) {
222                 if (_start_requested) {
223                         _running = true;
224                         last_start = superclock_cnt;
225                 }
226
227         } else {
228                 if (!_start_requested) {
229                         _running = false;
230                 }
231         }
232
233         superclock_t superclocks = samples_to_superclock (nsamples, _sample_rate);
234
235         if (!_running) {
236                 superclock_cnt += superclocks;
237                 return 0;
238         }
239
240         if (_tempo_request) {
241                 double ratio = _tempo / _tempo_request;
242                 _tempo = _tempo_request;
243                 _tempo_request = 0;
244
245                 compute_tempo_clocks ();
246
247                 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
248                         (*ee)->time = llrintf ((*ee)->time * ratio);
249                 }
250         }
251
252         superclock_t process_start = superclock_cnt - last_start;
253         superclock_t process_end = process_start + superclocks;
254         const superclock_t loop_length = _measures * measure_superclocks;
255         const superclock_t orig_superclocks = superclocks;
256
257         process_start %= loop_length;
258         process_end   %= loop_length;
259
260         bool two_pass_required;
261         superclock_t offset = 0;
262
263         if (process_end < process_start) {
264                 two_pass_required = true;
265                 process_end = loop_length;
266                 superclocks = process_end - process_start;
267         } else {
268                 two_pass_required = false;
269         }
270
271         unsigned char* buffer;
272         void* out_buf;
273         void* in_buf;
274         jack_midi_event_t in_event;
275         jack_nframes_t event_index;
276         jack_nframes_t event_count;
277
278         /* do this on the first pass only */
279         out_buf = jack_port_get_buffer (_output, nsamples);
280         jack_midi_clear_buffer (out_buf);
281
282   second_pass:
283
284         /* Output */
285
286         if (clear_pending) {
287
288                 for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
289                         event_pool.push_back (*ee);
290                 }
291                 _current_events.clear ();
292                 clear_pending = false;
293         }
294
295         for (Events::iterator ee = _current_events.begin(); ee != _current_events.end(); ++ee) {
296                 Event* e = (*ee);
297
298                 if (e->size && (e->time >= process_start && e->time < process_end)) {
299                         if ((buffer = jack_midi_event_reserve (out_buf, superclock_to_samples (offset + e->time - process_start, _sample_rate), e->size)) != 0) {
300                                 memcpy (buffer, e->buf, e->size);
301                         } else {
302                                 cerr << "Could not reserve space for output event @ " << e << " of size " << e->size << " @ " << offset + e->time - process_start
303                                      << " (samples: " << superclock_to_samples (offset + e->time - process_start, _sample_rate) << ") offset is " << offset
304                                      << ")\n";
305                         }
306                 }
307
308                 if (e->time >= process_end) {
309                         break;
310                 }
311         }
312
313         /* input */
314
315         in_buf = jack_port_get_buffer (_input, nsamples);
316         event_index = 0;
317
318         while (jack_midi_event_get (&in_event, in_buf, event_index++) == 0) {
319
320                 superclock_t event_time = superclock_cnt + samples_to_superclock (in_event.time, _sample_rate);
321                 superclock_t elapsed_time = event_time - last_start;
322                 superclock_t in_loop_time = elapsed_time % loop_length;
323                 superclock_t quantized_time;
324
325                 if (_quantize_divisor != 0) {
326                         const superclock_t time_per_beat = whole_note_superclocks / _quantize_divisor;
327                         quantized_time = (in_loop_time / time_per_beat) * time_per_beat;
328                 } else {
329                         quantized_time = elapsed_time;
330                 }
331
332                 if (in_event.size > 24) {
333                         cerr << "Ignored large MIDI event\n";
334                         continue;
335                 }
336
337                 if (event_pool.empty()) {
338                         cerr << "No more events, grow pool\n";
339                         continue;
340                 }
341
342                 Event* e = event_pool.back();
343                 event_pool.pop_back ();
344
345                 e->time = quantized_time;
346                 e->whole_note_superclocks = whole_note_superclocks;
347                 e->size = in_event.size;
348                 memcpy (e->buf, in_event.buffer, in_event.size);
349
350                 _current_events.insert (e);
351         }
352
353         superclock_cnt += superclocks;
354
355         if (two_pass_required) {
356                 offset = superclocks;
357                 superclocks = orig_superclocks - superclocks;
358                 process_start = 0;
359                 process_end = superclocks;
360                 two_pass_required = false;
361                 goto second_pass;
362         }
363
364         return 0;
365 }
366
367 void
368 BeatBox::set_quantize (int divisor)
369 {
370         _quantize_divisor = divisor;
371 }
372
373 void
374 BeatBox::clear ()
375 {
376         clear_pending = true;
377 }
378
379 bool
380 BeatBox::EventComparator::operator() (Event const * a, Event const *b) const
381 {
382         if (a->time == b->time) {
383                 if (a->buf[0] == b->buf[0]) {
384                         return a < b;
385                 }
386                 return !second_simultaneous_midi_byte_is_first (a->buf[0], b->buf[0]);
387         }
388         return a->time < b->time;
389 }
390
391 static int
392 process (jack_nframes_t nsamples, void* arg)
393 {
394         BeatBox* bbox = static_cast<BeatBox*> (arg);
395         return bbox->process (nsamples);
396 }
397
398 int
399 main (int argc, char* argv[])
400 {
401         jack_client_t* jack;
402         const char *server_name = NULL;
403         jack_options_t options = JackNullOption;
404         jack_status_t status;
405
406         if ((jack = jack_client_open ("beatbox", options, &status, server_name)) == 0) {
407                 cerr << "Could not connect to JACK\n";
408                 return -1;
409         }
410
411         BeatBox* bbox = new BeatBox (jack_get_sample_rate (jack));
412         BBGUI* gui = new BBGUI (&argc, &argv, jack, bbox);
413
414         bbox->register_ports (jack);
415
416         jack_set_process_callback (jack, process, bbox);
417         jack_activate (jack);
418
419         bbox->start ();
420
421         gui->run ();
422 }