Only show user-presets in favorite sidebar
[ardour.git] / libs / backends / coreaudio / coremidi_io.cc
1 /*
2  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <sstream>
20 #include <CoreAudio/HostTime.h>
21
22 #include "coremidi_io.h"
23 #include "coreaudio_backend.h"
24
25 using namespace ARDOUR;
26
27 #ifndef NDEBUG
28 static int _debug_mode = 0;
29 #endif
30
31
32 /** 
33  * MIDI Data flow
34  * 
35  * (A) INPUT (incoming from outside the application)
36  * 
37  *    - midiInputCallback (in its own thread, async WRT process callback):
38  *       takes OS X MIDIPacket, copies into lock-free ringbuffer
39  *
40  *    - processCallback (in its own thread):
41  *
42  *   (1) loop on all input ports:
43  *       1A) call recv_event() to read from ringbuffer into stack buffer, also assign-timestamp, 
44  *       1B) call parse_events() using stack buffer, when appropriate
45  *          pushes CoreMidiEvent into std::vector<CoreMidiEvent>
46  *
47  *   (2) in MidiPort::cycle_start() (also part of the process callback call tree), MidiPort::get_midi_buffer()
48  *       calls CoreAudioBackend::midi_event_get () returns a pointer to the  data of the specified CoreMidiEvent
49  */
50
51 static void notifyProc (const MIDINotification *message, void *refCon) {
52         CoreMidiIo *self = static_cast<CoreMidiIo*>(refCon);
53         self->notify_proc(message);
54 }
55
56 #ifndef NDEBUG
57 static void print_packet (const MIDIPacket *p) {
58         fprintf (stderr, "CoreMIDI: Packet %d bytes [ ", p->length);
59         for (int bb = 0; bb < p->length; ++bb) {
60                 fprintf (stderr, "%02x ", ((const uint8_t*)p->data)[bb]);
61         }
62         fprintf (stderr, "]\n");
63 }
64
65 static void dump_packet_list (const UInt32 numPackets, MIDIPacket const *p) {
66         for (UInt32 i = 0; i < numPackets; ++i) {
67                 print_packet (p);
68                 p = MIDIPacketNext (p);
69         }
70 }
71 #endif
72
73 static void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) {
74         CoreMidiIo *self = static_cast<CoreMidiIo*> (procRef);
75         if (!self || !self->enabled()) {
76                 // skip while freewheeling
77 #ifndef NDEBUG
78                 if (_debug_mode & 2) {
79                         fprintf (stderr, "Ignored Midi Packet while freewheeling:\n");
80                         dump_packet_list (list->numPackets, &list->packet[0]);
81                 }
82 #endif
83                 return;
84         }
85         PBD::RingBuffer<uint8_t> * rb  = static_cast<PBD::RingBuffer < uint8_t > *> (srcRef);
86         if (!rb) {
87 #ifndef NDEBUG
88                 if (_debug_mode & 4) {
89                         fprintf (stderr, "Ignored Midi Packet - no ringbuffer:\n");
90                         dump_packet_list (list->numPackets, &list->packet[0]);
91                 }
92 #endif
93                 return;
94         }
95         MIDIPacket const *p = &list->packet[0];
96         for (UInt32 i = 0; i < list->numPackets; ++i) {
97                 uint32_t len = ((p->length + 3)&~3) + sizeof(MIDITimeStamp) + sizeof(UInt16);
98 #ifndef NDEBUG
99                 if (_debug_mode & 1) {
100                         print_packet (p);
101                 }
102 #endif
103                 if (rb->write_space() > sizeof(uint32_t) + len) {
104                         rb->write ((uint8_t*)&len, sizeof(uint32_t));
105                         rb->write ((uint8_t*)p, len);
106                 }
107 #ifndef NDEBUG
108                 else {
109                         fprintf (stderr, "CoreMIDI: dropped MIDI event\n");
110                 }
111 #endif
112                 p = MIDIPacketNext (p);
113         }
114 }
115
116 static std::string getPropertyString (MIDIObjectRef object, CFStringRef key)
117 {
118         CFStringRef name = NULL;
119         std::string rv = "";
120         if (noErr == MIDIObjectGetStringProperty(object, key, &name)) {
121                 const CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8);
122                 char *tmp = (char*) malloc(size);
123                 if (CFStringGetCString(name, tmp, size, kCFStringEncodingUTF8)) {
124                         rv = tmp;
125                 }
126                 free(tmp);
127                 CFRelease(name);
128         }
129         return rv;
130 }
131
132 static std::string getDisplayName (MIDIObjectRef object) {
133         return getPropertyString(object, kMIDIPropertyDisplayName);
134 }
135
136 CoreMidiIo::CoreMidiIo()
137         : _midi_client (0)
138         , _input_endpoints (0)
139         , _output_endpoints (0)
140         , _input_ports (0)
141         , _output_ports (0)
142         , _input_queue (0)
143         , _rb (0)
144         , _n_midi_in (0)
145         , _n_midi_out (0)
146         , _time_at_cycle_start (0)
147         , _active (false)
148         , _enabled (true)
149         , _run (false)
150         , _changed_callback (0)
151         , _changed_arg (0)
152 {
153         pthread_mutex_init (&_discovery_lock, 0);
154
155 #ifndef NDEBUG
156         const char *p = getenv ("COREMIDIDEBUG");
157         if (p && *p) _debug_mode = atoi (p);
158 #endif
159 }
160
161 CoreMidiIo::~CoreMidiIo()
162 {
163         pthread_mutex_lock (&_discovery_lock);
164         cleanup();
165         if (_midi_client) {
166                 MIDIClientDispose(_midi_client);
167                 _midi_client = 0;
168         }
169         pthread_mutex_unlock (&_discovery_lock);
170         pthread_mutex_destroy (&_discovery_lock);
171 }
172
173 void
174 CoreMidiIo::cleanup()
175 {
176         _active = false;
177         for (uint32_t i = 0 ; i < _n_midi_in ; ++i) {
178                 MIDIPortDispose(_input_ports[i]);
179                 _input_queue[i].clear();
180                 delete _rb[i];
181         }
182         for (uint32_t i = 0 ; i < _n_midi_out ; ++i) {
183                 MIDIPortDispose(_output_ports[i]);
184         }
185
186         free(_input_ports); _input_ports = 0;
187         free(_input_endpoints); _input_endpoints = 0;
188         free(_input_queue); _input_queue = 0;
189         free(_output_ports); _output_ports = 0;
190         free(_output_endpoints); _output_endpoints = 0;
191         free(_rb); _rb = 0;
192
193         _n_midi_in = 0;
194         _n_midi_out = 0;
195 }
196
197 void
198 CoreMidiIo::start_cycle()
199 {
200         _time_at_cycle_start = AudioGetCurrentHostTime();
201 }
202
203 void
204 CoreMidiIo::notify_proc(const MIDINotification *message)
205 {
206         switch(message->messageID) {
207                 case kMIDIMsgSetupChanged:
208                         /* this one catches all of the added/removed/changed below */
209                         //printf("kMIDIMsgSetupChanged\n");
210                         discover();
211                         break;
212                 case kMIDIMsgObjectAdded:
213                         {
214                         //const MIDIObjectAddRemoveNotification *n = (const MIDIObjectAddRemoveNotification*) message;
215                         //printf("kMIDIMsgObjectAdded\n");
216                         }
217                         break;
218                 case kMIDIMsgObjectRemoved:
219                         {
220                         //const MIDIObjectAddRemoveNotification *n = (const MIDIObjectAddRemoveNotification*) message;
221                         //printf("kMIDIMsgObjectRemoved\n");
222                         }
223                         break;
224                 case kMIDIMsgPropertyChanged:
225                         {
226                         //const MIDIObjectPropertyChangeNotification *n = (const MIDIObjectPropertyChangeNotification*) message;
227                         //printf("kMIDIMsgObjectRemoved\n");
228                         }
229                         break;
230                 case kMIDIMsgThruConnectionsChanged:
231                         //printf("kMIDIMsgThruConnectionsChanged\n");
232                         break;
233                 case kMIDIMsgSerialPortOwnerChanged:
234                         //printf("kMIDIMsgSerialPortOwnerChanged\n");
235                         break;
236                 case kMIDIMsgIOError:
237                         fprintf(stderr, "kMIDIMsgIOError\n");
238                         discover();
239                         break;
240         }
241 }
242
243 size_t
244 CoreMidiIo::recv_event (uint32_t port, double cycle_time_us, uint64_t &time, uint8_t *d, size_t &s)
245 {
246         if (!_active || _time_at_cycle_start == 0) {
247                 return 0;
248         }
249         assert(port < _n_midi_in);
250
251         const size_t minsize = 1 + sizeof(uint32_t) + sizeof(MIDITimeStamp) + sizeof(UInt16);
252
253         while (_rb[port]->read_space() > minsize) {
254                 MIDIPacket packet;
255                 size_t rv;
256                 uint32_t s = 0;
257                 rv = _rb[port]->read((uint8_t*)&s, sizeof(uint32_t));
258                 assert(rv == sizeof(uint32_t));
259                 rv = _rb[port]->read((uint8_t*)&packet, s);
260                 assert(rv == s);
261                 _input_queue[port].push_back(boost::shared_ptr<CoreMIDIPacket>(new _CoreMIDIPacket (&packet)));
262         }
263
264         UInt64 start = _time_at_cycle_start;
265         UInt64 end = AudioConvertNanosToHostTime(AudioConvertHostTimeToNanos(_time_at_cycle_start) + cycle_time_us * 1e3);
266
267         for (CoreMIDIQueue::iterator it = _input_queue[port].begin (); it != _input_queue[port].end (); ) {
268                 if ((*it)->timeStamp < end) {
269                         if ((*it)->timeStamp < start) {
270                                 uint64_t dt = AudioConvertHostTimeToNanos(start - (*it)->timeStamp);
271                                 if (dt > 1e7 && (*it)->timeStamp != 0) { // 10ms slack and a timestamp is given
272 #ifndef NDEBUG
273                                         printf("Dropped Stale Midi Event. dt:%.2fms\n", dt * 1e-6);
274 #endif
275                                         it = _input_queue[port].erase(it);
276                                         continue;
277                                 } else {
278                                         /* events without a valid timestamp, or events that arrived
279                                          * less than 10ms in the past are allowed and
280                                          * queued at the beginning of the cycle:
281                                          * time (relative to cycle start) = 0
282                                          *
283                                          * The latter use needed for the "Avid Artist" Control Surface
284                                          * the OSX driver sends no timestamps.
285                                          */
286 #if 0
287                                         printf("Stale Midi Event. dt:%.2fms\n", dt * 1e-6);
288 #endif
289                                 }
290                                 time = 0;
291                         } else {
292                                 time = AudioConvertHostTimeToNanos((*it)->timeStamp - start);
293                         }
294                         s = std::min(s, (size_t) (*it)->length);
295                         if (s > 0) {
296                                 memcpy(d, (*it)->data, s);
297                         }
298                         _input_queue[port].erase(it);
299                         return s;
300                 }
301                 ++it;
302
303         }
304         return 0;
305 }
306
307 int
308 CoreMidiIo::send_events (uint32_t port, double timescale, const void *b)
309 {
310         if (!_active || _time_at_cycle_start == 0) {
311                 return 0;
312         }
313
314         assert(port < _n_midi_out);
315         const UInt64 ts = AudioConvertHostTimeToNanos(_time_at_cycle_start);
316
317         const CoreMidiBuffer *src = static_cast<CoreMidiBuffer const *>(b);
318
319         int32_t bytes[8192]; // int for alignment
320         MIDIPacketList *mpl = (MIDIPacketList*)bytes;
321         MIDIPacket *cur = MIDIPacketListInit(mpl);
322
323         for (CoreMidiBuffer::const_iterator mit = src->begin (); mit != src->end (); ++mit) {
324                 assert(mit->size() < 256);
325                 cur = MIDIPacketListAdd(mpl, sizeof(bytes), cur,
326                                 AudioConvertNanosToHostTime(ts + mit->timestamp() / timescale),
327                                 mit->size(), mit->data());
328                 if (!cur) {
329 #ifndef DEBUG
330                         printf("CoreMidi: Packet list overflow, dropped events\n");
331 #endif
332                         break;
333                 }
334         }
335         if (mpl->numPackets > 0) {
336                 MIDISend(_output_ports[port], _output_endpoints[port], mpl);
337         }
338         return 0;
339 }
340
341 int
342 CoreMidiIo::send_event (uint32_t port, double reltime_us, const uint8_t *d, const size_t s)
343 {
344         if (!_active || _time_at_cycle_start == 0) {
345                 return 0;
346         }
347
348         assert(port < _n_midi_out);
349         UInt64 ts = AudioConvertHostTimeToNanos(_time_at_cycle_start);
350         ts += reltime_us * 1e3;
351
352         // TODO use a single packet list.. queue all events first..
353         MIDIPacketList pl;
354
355         pl.numPackets = 1;
356         MIDIPacket *mp = &(pl.packet[0]);
357
358         mp->timeStamp = AudioConvertNanosToHostTime(ts);
359         mp->length = s;
360         assert(s < 256);
361         memcpy(mp->data, d, s);
362
363         MIDISend(_output_ports[port], _output_endpoints[port], &pl);
364         return 0;
365 }
366
367
368 std::string
369 CoreMidiIo::port_id (uint32_t port, bool input)
370 {
371         std::stringstream ss;
372         if (input) {
373                 ss << "system:midi_capture_";
374                 SInt32 id;
375                 if (noErr == MIDIObjectGetIntegerProperty(_input_endpoints[port], kMIDIPropertyUniqueID, &id)) {
376                         ss << (unsigned int)id;
377                 } else {
378                         ss << port;
379                 }
380         } else {
381                 ss << "system:midi_playback_";
382                 SInt32 id;
383                 if (noErr == MIDIObjectGetIntegerProperty(_output_endpoints[port], kMIDIPropertyUniqueID, &id)) {
384                         ss << (unsigned int)id;
385                 } else {
386                         ss << port;
387                 }
388         }
389         return ss.str();
390 }
391
392 std::string
393 CoreMidiIo::port_name (uint32_t port, bool input)
394 {
395         if (input) {
396                 if (port < _n_midi_in) {
397                         return getDisplayName(_input_endpoints[port]);
398                 }
399         } else {
400                 if (port < _n_midi_out) {
401                         return getDisplayName(_output_endpoints[port]);
402                 }
403         }
404         return "";
405 }
406
407 void
408 CoreMidiIo::start () {
409         _run = true;
410         if (!_midi_client) {
411                 OSStatus err;
412                 err = MIDIClientCreate(CFSTR("Ardour"), &notifyProc, this, &_midi_client);
413                 if (noErr != err) {
414                         fprintf(stderr, "Creating Midi Client failed\n");
415                 }
416         }
417         discover();
418 }
419
420 void
421 CoreMidiIo::stop ()
422 {
423         _run = false;
424         pthread_mutex_lock (&_discovery_lock);
425         cleanup();
426         pthread_mutex_unlock (&_discovery_lock);
427 #if 0
428         if (_midi_client) {
429                 MIDIClientDispose(_midi_client);
430                 _midi_client = 0;
431         }
432 #endif
433 }
434
435 void
436 CoreMidiIo::discover()
437 {
438         if (!_run || !_midi_client) { return; }
439
440         if (pthread_mutex_trylock (&_discovery_lock)) {
441                 return;
442         }
443
444         cleanup();
445
446         ItemCount srcCount = MIDIGetNumberOfSources();
447         ItemCount dstCount = MIDIGetNumberOfDestinations();
448
449         if (srcCount > 0) {
450                 _input_ports = (MIDIPortRef *) malloc (srcCount * sizeof(MIDIPortRef));
451                 _input_endpoints = (MIDIEndpointRef*) malloc (srcCount * sizeof(MIDIEndpointRef));
452                 _input_queue = (CoreMIDIQueue*) calloc (srcCount, sizeof(CoreMIDIQueue));
453                 _rb = (PBD::RingBuffer<uint8_t> **) malloc (srcCount * sizeof(PBD::RingBuffer<uint8_t>*));
454         }
455         if (dstCount > 0) {
456                 _output_ports = (MIDIPortRef *) malloc (dstCount * sizeof(MIDIPortRef));
457                 _output_endpoints = (MIDIEndpointRef*) malloc (dstCount * sizeof(MIDIEndpointRef));
458         }
459
460         for (ItemCount i = 0; i < srcCount; i++) {
461                 OSStatus err;
462                 MIDIEndpointRef src = MIDIGetSource(i);
463                 if (!src) continue;
464 #ifndef NDEBUG
465                 printf("MIDI IN DEVICE: %s\n", getDisplayName(src).c_str());
466 #endif
467
468                 CFStringRef port_name;
469                 port_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("midi_capture_%lu"), i);
470
471                 err = MIDIInputPortCreate (_midi_client, port_name, midiInputCallback, this, &_input_ports[_n_midi_in]);
472                 if (noErr != err) {
473                         fprintf(stderr, "Cannot create Midi Output\n");
474                         continue;
475                 }
476                 _rb[_n_midi_in] = new PBD::RingBuffer<uint8_t>(32768);
477                 _input_queue[_n_midi_in] = CoreMIDIQueue();
478                 MIDIPortConnectSource(_input_ports[_n_midi_in], src, (void*) _rb[_n_midi_in]);
479                 CFRelease(port_name);
480                 _input_endpoints[_n_midi_in] = src;
481                 ++_n_midi_in;
482         }
483
484         for (ItemCount i = 0; i < dstCount; i++) {
485                 MIDIEndpointRef dst = MIDIGetDestination(i);
486                 CFStringRef port_name;
487                 port_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("midi_playback_%lu"), i);
488
489                 OSStatus err;
490                 err = MIDIOutputPortCreate (_midi_client, port_name, &_output_ports[_n_midi_out]);
491                 if (noErr != err) {
492                         fprintf(stderr, "Cannot create Midi Output\n");
493                         continue;
494                 }
495
496 #ifndef NDEBUG
497                 printf("MIDI OUT DEVICE: %s\n", getDisplayName(dst).c_str());
498 #endif
499
500                 MIDIPortConnectSource(_output_ports[_n_midi_out], dst, NULL);
501                 CFRelease(port_name);
502                 _output_endpoints[_n_midi_out] = dst;
503                 ++_n_midi_out;
504         }
505
506         if (_changed_callback) {
507                 _changed_callback(_changed_arg);
508         }
509
510         _active = true;
511         pthread_mutex_unlock (&_discovery_lock);
512 }