1 /* mclk -- LV2 midi clock generator
2 *
3 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 *
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 2, or (at your option)
8 * any later version.
9 *
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.
14 *
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/>.
17 */
18
19 #define _GNU_SOURCE
20
21 #define CLK_URI "http://gareus.org/oss/lv2/mclk"
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <math.h>
28
29 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
30 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
31 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
32 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
33 #include <lv2/lv2plug.in/ns/ext/midi/midi.h>
34 #include "lv2/lv2plug.in/ns/ext/time/time.h"
35 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
36
37 typedef struct {
38 LV2_URID atom_Blank;
39 LV2_URID atom_Object;
40 LV2_URID atom_Sequence;
41 LV2_URID midi_MidiEvent;
42 LV2_URID atom_Float;
43 LV2_URID atom_Int;
44 LV2_URID atom_Long;
45 LV2_URID time_Position;
46 LV2_URID time_bar;
47 LV2_URID time_barBeat;
48 LV2_URID time_beatUnit;
49 LV2_URID time_beatsPerBar;
50 LV2_URID time_beatsPerMinute;
51 LV2_URID time_speed;
52 LV2_URID time_frame;
53 } MclkURIs;
54
55 /* MIDI System Real-Time Messages */
56 #define MIDI_RT_CLOCK (0xF8)
57 #define MIDI_RT_START (0xFA)
58 #define MIDI_RT_CONTINUE (0xFB)
59 #define MIDI_RT_STOP (0xFC)
60
61 /* bitwise flags -- mode */
62 enum {
63 MSG_NO_TRANSPORT = 1, /**< do not send start/stop/continue messages */
64 MSG_NO_POSITION = 2, /**< do not send absolute song position */
65 MSG_NO_CLOCK = 4 /**< do not send MIDI Clock*/
66 };
67
68 typedef struct {
69 /* ports */
70 const LV2_Atom_Sequence* control;
71 LV2_Atom_Sequence* midiout;
72 float* p_mode;
73 float* p_sync;
74 float* p_bpm;
75 float* p_transport;
76 float* p_rewind;
77 float* p_hostbpm;
78 float* p_songpos;
79 float* p_bbt[3];
80
81 /* Cached Ports */
82 float c_mode;
83 float c_transport;
84 float c_bpm;
85
86 /* atom-forge and URI mapping */
87 LV2_Atom_Forge forge;
88 LV2_Atom_Forge_Frame frame;
89 MclkURIs uris;
90
91 /* LV2 Output */
92 LV2_Log_Log* log;
93 LV2_Log_Logger logger;
94
95 /* Host Time */
96 bool host_info;
97 float host_bpm;
98 double bar_beats;
99 float host_speed;
100 int host_div;
101 int64_t host_frame;
102
103 /* Settings */
104 double sample_rate;
105 int mode;
106
107 /* State */
108 bool rolling;
109 double bb;
110 int64_t last_bcnt;
111 int64_t sample_pos;
112 double mclk_last_tick; // in audio-samples
113 } Mclk;
114
115 /* *****************************************************************************
116 * helper functions
117 */
118
119 /** map uris */
120 static void
map_uris(LV2_URID_Map * map,MclkURIs * uris)121 map_uris (LV2_URID_Map* map, MclkURIs* uris)
122 {
123 uris->atom_Blank = map->map (map->handle, LV2_ATOM__Blank);
124 uris->atom_Object = map->map (map->handle, LV2_ATOM__Object);
125 uris->midi_MidiEvent = map->map (map->handle, LV2_MIDI__MidiEvent);
126 uris->atom_Sequence = map->map (map->handle, LV2_ATOM__Sequence);
127 uris->time_Position = map->map (map->handle, LV2_TIME__Position);
128 uris->atom_Long = map->map (map->handle, LV2_ATOM__Long);
129 uris->atom_Int = map->map (map->handle, LV2_ATOM__Int);
130 uris->atom_Float = map->map (map->handle, LV2_ATOM__Float);
131 uris->time_bar = map->map (map->handle, LV2_TIME__bar);
132 uris->time_barBeat = map->map (map->handle, LV2_TIME__barBeat);
133 uris->time_beatUnit = map->map (map->handle, LV2_TIME__beatUnit);
134 uris->time_beatsPerBar = map->map (map->handle, LV2_TIME__beatsPerBar);
135 uris->time_beatsPerMinute = map->map (map->handle, LV2_TIME__beatsPerMinute);
136 uris->time_speed = map->map (map->handle, LV2_TIME__speed);
137 uris->time_frame = map->map (map->handle, LV2_TIME__frame);
138 }
139
140 /**
141 * Update the current position based on a host message. This is called by
142 * run() when a time:Position is received.
143 */
144 static void
update_position(Mclk * self,const LV2_Atom_Object * obj)145 update_position (Mclk* self, const LV2_Atom_Object* obj)
146 {
147 const MclkURIs* uris = &self->uris;
148
149 LV2_Atom* bar = NULL;
150 LV2_Atom* beat = NULL;
151 LV2_Atom* bunit = NULL;
152 LV2_Atom* bpb = NULL;
153 LV2_Atom* bpm = NULL;
154 LV2_Atom* speed = NULL;
155 LV2_Atom* frame = NULL;
156
157 lv2_atom_object_get (
158 obj,
159 uris->time_bar, &bar,
160 uris->time_barBeat, &beat,
161 uris->time_beatUnit, &bunit,
162 uris->time_beatsPerBar, &bpb,
163 uris->time_beatsPerMinute, &bpm,
164 uris->time_speed, &speed,
165 uris->time_frame, &frame,
166 NULL);
167
168 if ( bpm && bpm->type == uris->atom_Float
169 && bpb && bpb->type == uris->atom_Float
170 && bar && bar->type == uris->atom_Long
171 && beat && beat->type == uris->atom_Float
172 && bunit && bunit->type == uris->atom_Int
173 && speed && speed->type == uris->atom_Float
174 && frame && frame->type == uris->atom_Long)
175 {
176 float _bpb = ((LV2_Atom_Float*)bpb)->body;
177 int64_t _bar = ((LV2_Atom_Long*)bar)->body;
178 float _beat = ((LV2_Atom_Float*)beat)->body;
179
180 self->host_div = ((LV2_Atom_Int*)bunit)->body;
181 self->host_bpm = ((LV2_Atom_Float*)bpm)->body;
182 self->host_speed = ((LV2_Atom_Float*)speed)->body;
183 self->host_frame = ((LV2_Atom_Long*)frame)->body;
184
185 self->bar_beats = _bar * _bpb + _beat * self->host_div / 4.0;
186 self->host_info = true;
187 if (self->host_frame < 0) {
188 self->host_info = false;
189 }
190 }
191 }
192
193 /**
194 * add a midi message to the output port
195 */
196 static void
forge_midimessage(Mclk * self,uint32_t tme,const uint8_t * const buffer,uint32_t size)197 forge_midimessage (Mclk* self,
198 uint32_t tme,
199 const uint8_t* const buffer,
200 uint32_t size)
201 {
202 LV2_Atom midiatom;
203 midiatom.type = self->uris.midi_MidiEvent;
204 midiatom.size = size;
205
206 if (0 == lv2_atom_forge_frame_time (&self->forge, tme)) return;
207 if (0 == lv2_atom_forge_raw (&self->forge, &midiatom, sizeof (LV2_Atom))) return;
208 if (0 == lv2_atom_forge_raw (&self->forge, buffer, size)) return;
209 lv2_atom_forge_pad (&self->forge, sizeof (LV2_Atom) + size);
210 }
211
212 /* *****************************************************************************
213 * Midi Clock
214 */
215
216 static int64_t
send_pos_message(Mclk * self,const int64_t bcnt)217 send_pos_message (Mclk* self, const int64_t bcnt)
218 {
219 if (self->mode & MSG_NO_POSITION) return -1;
220 /* send '0xf2' Song Position Pointer.
221 * This is an internal 14 bit register that holds the number of
222 * MIDI beats (1 beat = six MIDI clocks) since the start of the song.
223 */
224 if (bcnt < 0 || bcnt >= 16384) {
225 return bcnt;
226 }
227
228 uint8_t buffer[3];
229 buffer[0] = 0xf2;
230 buffer[1] = bcnt & 0x7f; // LSB
231 buffer[2] = (bcnt >> 7) & 0x7f; // MSB
232 forge_midimessage (self, 0, buffer, 3);
233 return bcnt;
234 }
235
236 /**
237 * send 1 byte MIDI Message
238 * @param port_buf buffer to write event to
239 * @param time sample offset of event
240 * @param rt_msg message byte
241 */
242 static void
send_rt_message(Mclk * self,uint32_t tme,uint8_t rt_msg)243 send_rt_message (Mclk* self, uint32_t tme, uint8_t rt_msg)
244 {
245 forge_midimessage (self, tme, &rt_msg, 1);
246 }
247
248 /**
249 * calculate song position (14 bit integer) from current BBT info.
250 *
251 * see "Song Position Pointer" at
252 * http://www.midi.org/techspecs/midimessages.php
253 *
254 * Because this value is also used internally to sync/send
255 * start/continue realtime messages, a 64 bit integer
256 * is used to cover the complete range.
257 */
258 static const int64_t
calc_song_pos(const double bar_beat,double bpm,int off)259 calc_song_pos (const double bar_beat, double bpm, int off)
260 {
261 const double resync_delay = 1.0; /**< seconds between 'pos' and 'continue' message */
262
263 if (off < 0) {
264 /* auto offset */
265 if (bar_beat == 0) off = 0;
266 else off = rint (bpm * 4.0 * resync_delay / 60.0);
267 }
268 return off + floor (4.0 * bar_beat);
269 }
270
271 /* *****************************************************************************
272 * LV2 Plugin
273 */
274
275 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)276 instantiate (const LV2_Descriptor* descriptor,
277 double rate,
278 const char* bundle_path,
279 const LV2_Feature* const* features)
280 {
281 Mclk* self = (Mclk*)calloc (1, sizeof (Mclk));
282 LV2_URID_Map* map = NULL;
283
284 int i;
285 for (i=0; features[i]; ++i) {
286 if (!strcmp (features[i]->URI, LV2_URID__map)) {
287 map = (LV2_URID_Map*)features[i]->data;
288 } else if (!strcmp (features[i]->URI, LV2_LOG__log)) {
289 self->log = (LV2_Log_Log*)features[i]->data;
290 }
291 }
292
293 lv2_log_logger_init (&self->logger, map, self->log);
294
295 if (!map) {
296 lv2_log_error (&self->logger, "Mclk.lv2 error: Host does not support urid:map\n");
297 free (self);
298 return NULL;
299 }
300
301 lv2_atom_forge_init (&self->forge, map);
302 map_uris (map, &self->uris);
303
304 self->sample_rate = rate;
305
306 self->bb = 0;
307 self->rolling = false;
308 self->last_bcnt = -1;
309 self->mclk_last_tick = 0;
310 self->sample_pos = 0;
311
312 return (LV2_Handle)self;
313 }
314
315 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)316 connect_port (LV2_Handle instance,
317 uint32_t port,
318 void* data)
319 {
320 Mclk* self = (Mclk*)instance;
321
322 switch (port) {
323 case 0:
324 self->control = (const LV2_Atom_Sequence*)data;
325 break;
326 case 1:
327 self->midiout = (LV2_Atom_Sequence*)data;
328 break;
329 case 2:
330 self->p_mode = (float*)data;
331 break;
332 case 3:
333 self->p_sync = (float*)data;
334 break;
335 case 4:
336 self->p_bpm = (float*)data;
337 break;
338 case 5:
339 self->p_transport = (float*)data;
340 break;
341 case 6:
342 self->p_rewind = (float*)data;
343 break;
344 case 7:
345 self->p_hostbpm = (float*)data;
346 break;
347 case 8:
348 self->p_songpos = (float*)data;
349 break;
350 case 9:
351 self->p_bbt[0] = (float*)data;
352 break;
353 case 10:
354 self->p_bbt[1] = (float*)data;
355 break;
356 case 11:
357 self->p_bbt[2] = (float*)data;
358 break;
359 default:
360 break;
361 }
362 }
363
364
365 static void
run(LV2_Handle instance,uint32_t n_samples)366 run (LV2_Handle instance, uint32_t n_samples)
367 {
368 Mclk* self = (Mclk*)instance;
369 if (!self->midiout || !self->control) {
370 return;
371 }
372
373 /* initialize output port */
374 const uint32_t capacity = self->midiout->atom.size;
375 lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->midiout, capacity);
376 lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0);
377
378 /* process control events */
379 LV2_Atom_Event* ev = lv2_atom_sequence_begin (&(self->control)->body);
380 while (!lv2_atom_sequence_is_end (&(self->control)->body, (self->control)->atom.size, ev)) {
381 if (ev->body.type == self->uris.atom_Blank || ev->body.type == self->uris.atom_Object) {
382 const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
383 if (obj->body.otype == self->uris.time_Position) {
384 update_position (self, obj);
385 }
386 }
387 ev = lv2_atom_sequence_next (ev);
388 }
389
390 bool rolling;
391 double bpm;
392 double bb;
393 double quarter_notes_per_beat = 1.0;
394 int64_t sample_position;
395
396 /* set mode */
397 switch ((int)rintf (*self->p_mode)) {
398 case 0:
399 self->mode = MSG_NO_POSITION | MSG_NO_CLOCK;
400 break;
401 case 1:
402 self->mode = MSG_NO_POSITION | MSG_NO_TRANSPORT;
403 break;
404 case 2:
405 self->mode = MSG_NO_POSITION;
406 break;
407 default:
408 case 3:
409 self->mode = 0;
410 break;
411 }
412
413 /* set BarBeat & transport state */
414 if (self->host_info && *self->p_sync > 0) {
415 *self->p_hostbpm = self->host_bpm;
416 bpm = self->host_bpm * self->host_speed;
417 sample_position = self->host_frame;
418 rolling = self->host_speed > 0;
419 bb = self->bar_beats;
420 if (self->host_speed < 0) {
421 goto noroll;
422 }
423 if (fabs(self->bb - self->bar_beats) > 1) {
424 /* located */
425 self->rolling = rolling = false;
426 self->bb = -1;
427 }
428 } else {
429 *self->p_hostbpm = self->host_info ? -1 : 0;
430 bpm = *self->p_bpm;
431 if (*self->p_rewind > 0) {
432 bb = self->bb = 0;
433 self->last_bcnt = -1;
434 rolling = false;
435 sample_position = self->sample_pos = 0;
436 } else {
437 rolling = *self->p_transport > 0;
438 bb = self->bb;
439 sample_position = self->sample_pos;
440 }
441 }
442
443 const double samples_per_beat = self->sample_rate * 60.0 / bpm;
444 const double samples_per_quarter_note = samples_per_beat / quarter_notes_per_beat;
445 const double clock_tick_interval = samples_per_quarter_note / 24.0;
446
447 /* send position updates if stopped and located */
448 if (!rolling && !self->rolling) {
449 if (bb != self->bb) {
450 self->last_bcnt = send_pos_message (self, calc_song_pos (bb, bpm, -1));
451 }
452 }
453
454 /* send RT messages start/stop/continue if transport state changed */
455 if (rolling != self->rolling) {
456 if (rolling) {
457 /* stop -> playing */
458 if (bb == 0 || 0 != (self->mode & MSG_NO_POSITION)) {
459 /* start playing now */
460 if (!(self->mode & MSG_NO_TRANSPORT)) {
461 send_rt_message (self, 0, MIDI_RT_START);
462 }
463 self->last_bcnt = -1; /* 'start' at 0, don't queue 'continue' message */
464 } else {
465 /* continue after pause
466 *
467 * only send continue message here if song-position is not used.
468 * w/song-pos it is queued just-in-time with clock
469 */
470 if (0 != (self->mode & MSG_NO_POSITION)) {
471 if (0 == (self->mode & MSG_NO_TRANSPORT)) {
472 send_rt_message (self, 0, MIDI_RT_CONTINUE);
473 }
474 }
475 }
476 /* initial beat tick */
477 if (0 == bb || 0 != (self->mode & (MSG_NO_POSITION))) {
478 if (0 == (self->mode & MSG_NO_CLOCK)) {
479 //send_rt_message (self, 0, MIDI_RT_CLOCK);
480 }
481 }
482 } else {
483 /* playing -> stop */
484 if (0 == (self->mode & MSG_NO_TRANSPORT)) {
485 send_rt_message (self, 0, MIDI_RT_STOP);
486 }
487 self->last_bcnt = send_pos_message (self, calc_song_pos (bb, bpm, -1));
488 }
489 self->mclk_last_tick = samples_per_beat * bb;
490 }
491
492 self->rolling = rolling;
493
494 if (!rolling || (self->mode & MSG_NO_CLOCK)) {
495 goto noroll;
496 }
497
498 /* send clock ticks for this cycle */
499 int ticks_sent_this_cycle = 0;
500 while (1) {
501 const double next_tick = self->mclk_last_tick + clock_tick_interval;
502 const int64_t next_tick_offset = llrint (next_tick) - sample_position;
503 if (next_tick_offset >= n_samples) break;
504
505 if (next_tick_offset >= 0) {
506 if (self->last_bcnt > 0 && !(self->mode & MSG_NO_POSITION)) {
507 /* send 'continue' realtime message on time */
508 const int64_t bcnt = calc_song_pos (bb, bpm, 0);
509 /* 4 MIDI-beats per quarter note (jack beat) */
510 if (bcnt + ticks_sent_this_cycle / 6 >= self->last_bcnt) {
511 if (!(self->mode & MSG_NO_TRANSPORT)) {
512 send_rt_message (self, next_tick_offset, MIDI_RT_CONTINUE);
513 }
514 self->last_bcnt = -1;
515 }
516 }
517 /* enqueue clock tick */
518 send_rt_message (self, next_tick_offset, MIDI_RT_CLOCK);
519 ticks_sent_this_cycle++;
520 }
521
522 self->mclk_last_tick = next_tick;
523 }
524
525 noroll:
526 *self->p_songpos = bb;
527
528 int bpb = 4;
529 if (self->host_info && *self->p_sync > 0) {
530 bpb = self->host_div;
531 }
532 *self->p_bbt[0] = 1.f + floor (bb / bpb); // limit to 4096 (song pos) ?
533 *self->p_bbt[1] = 1.f + ((int) floor (bb) % bpb);
534 *self->p_bbt[2] = floor (fmod (bb, 1.0) * 960.0);
535
536 /* keep track of host position.. */
537 if (self->host_info) {
538 self->bar_beats += n_samples * self->host_bpm * self->host_speed / (60.0 * self->sample_rate);
539 self->host_frame += n_samples * self->host_speed;
540 }
541
542 /* prepare for next cycle */
543 if (self->host_info && *self->p_sync > 0) {
544 self->bb = self->bar_beats;
545 self->sample_pos = self->host_frame;
546 } else if (rolling) {
547 self->bb += n_samples * bpm / (60.0 * self->sample_rate);
548 self->sample_pos += n_samples;
549 }
550 }
551
552 static void
cleanup(LV2_Handle instance)553 cleanup (LV2_Handle instance)
554 {
555 free (instance);
556 }
557
558 static const void*
extension_data(const char * uri)559 extension_data (const char* uri)
560 {
561 return NULL;
562 }
563
564 static const LV2_Descriptor descriptor = {
565 CLK_URI,
566 instantiate,
567 connect_port,
568 NULL,
569 run,
570 NULL,
571 cleanup,
572 extension_data
573 };
574
575 #undef LV2_SYMBOL_EXPORT
576 #ifdef _WIN32
577 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
578 #else
579 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
580 #endif
581 LV2_SYMBOL_EXPORT
582 const LV2_Descriptor*
lv2_descriptor(uint32_t index)583 lv2_descriptor (uint32_t index)
584 {
585 switch (index) {
586 case 0:
587 return &descriptor;
588 default:
589 return NULL;
590 }
591 }
592