2 * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
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.
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.
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.
19 #include "coremidi_io.h"
20 #include <CoreAudio/HostTime.h>
22 static void notifyProc (const MIDINotification *message, void *refCon) {
23 CoreMidiIo *self = static_cast<CoreMidiIo*>(refCon);
24 self->notify_proc(message);
27 static void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) {
28 // TODO skip while freewheeling
29 RingBuffer<uint8_t> * rb = static_cast<RingBuffer < uint8_t > *> (srcRef);
31 for (UInt32 i = 0; i < list->numPackets; i++) {
32 const MIDIPacket *packet = &list->packet[i];
33 if (rb->write_space() < sizeof(MIDIPacket)) {
34 fprintf(stderr, "CoreMIDI: dropped MIDI event\n");
37 rb->write((uint8_t*)packet, sizeof(MIDIPacket));
42 CoreMidiIo::CoreMidiIo()
45 , _outputEndPoints (0)
52 , _time_at_cycle_start (0)
54 , _changed_callback (0)
58 err = MIDIClientCreate(CFSTR("Ardour"), ¬ifyProc, this, &_midiClient);
60 fprintf(stderr, "Creating Midi Client failed\n");
65 CoreMidiIo::~CoreMidiIo()
68 MIDIClientDispose(_midiClient); _midiClient = 0;
75 for (uint32_t i = 0 ; i < _n_midi_in ; ++i) {
76 MIDIPortDispose(_inputPorts[i]);
77 _inputQueue[i].clear();
80 for (uint32_t i = 0 ; i < _n_midi_out ; ++i) {
81 MIDIPortDispose(_outputPorts[i]);
84 free(_inputPorts); _inputPorts = 0;
85 free(_inputEndPoints); _inputEndPoints = 0;
86 free(_inputQueue); _inputQueue = 0;
87 free(_outputPorts); _outputPorts = 0;
88 free(_outputEndPoints); _outputEndPoints = 0;
96 CoreMidiIo::start_cycle()
98 _time_at_cycle_start = AudioGetCurrentHostTime();
102 CoreMidiIo::notify_proc(const MIDINotification *message)
104 switch(message->messageID) {
105 case kMIDIMsgSetupChanged:
106 printf("kMIDIMsgSetupChanged\n");
109 case kMIDIMsgObjectAdded:
111 const MIDIObjectAddRemoveNotification *n = (const MIDIObjectAddRemoveNotification*) message;
112 printf("kMIDIMsgObjectAdded\n");
115 case kMIDIMsgObjectRemoved:
117 const MIDIObjectAddRemoveNotification *n = (const MIDIObjectAddRemoveNotification*) message;
118 printf("kMIDIMsgObjectRemoved\n");
121 case kMIDIMsgPropertyChanged:
123 const MIDIObjectPropertyChangeNotification *n = (const MIDIObjectPropertyChangeNotification*) message;
124 printf("kMIDIMsgObjectRemoved\n");
127 case kMIDIMsgThruConnectionsChanged:
128 printf("kMIDIMsgThruConnectionsChanged\n");
130 case kMIDIMsgSerialPortOwnerChanged:
131 printf("kMIDIMsgSerialPortOwnerChanged\n");
133 case kMIDIMsgIOError:
134 printf("kMIDIMsgIOError\n");
142 CoreMidiIo::recv_event (uint32_t port, double cycle_time_us, uint64_t &time, uint8_t *d, size_t &s)
144 if (!_active || _time_at_cycle_start == 0) {
147 assert(port < _n_midi_in);
149 while (_rb[port]->read_space() >= sizeof(MIDIPacket)) {
151 size_t rv = _rb[port]->read((uint8_t*)&packet, sizeof(MIDIPacket));
152 assert(rv == sizeof(MIDIPacket));
153 _inputQueue[port].push_back(boost::shared_ptr<CoreMIDIPacket>(new _CoreMIDIPacket (&packet)));
156 UInt64 start = _time_at_cycle_start;
157 UInt64 end = AudioConvertNanosToHostTime(AudioConvertHostTimeToNanos(_time_at_cycle_start) + cycle_time_us * 1e3);
159 for (CoreMIDIQueue::iterator it = _inputQueue[port].begin (); it != _inputQueue[port].end (); ) {
160 if ((*it)->timeStamp < end) {
161 if ((*it)->timeStamp < start) {
162 uint64_t dt = AudioConvertHostTimeToNanos(start - (*it)->timeStamp);
163 //printf("Stale Midi Event dt:%.2fms\n", dt * 1e-6);
164 if (dt > 1e-4) { // 100ms, maybe too large
165 it = _inputQueue[port].erase(it);
170 time = AudioConvertHostTimeToNanos((*it)->timeStamp - start);
172 s = std::min(s, (size_t) (*it)->length);
174 memcpy(d, (*it)->data, s);
176 _inputQueue[port].erase(it);
186 CoreMidiIo::send_event (uint32_t port, double reltime_us, const uint8_t *d, const size_t s)
188 if (!_active || _time_at_cycle_start == 0) {
192 assert(port < _n_midi_out);
193 UInt64 ts = AudioConvertHostTimeToNanos(_time_at_cycle_start);
194 ts += reltime_us * 1e3;
196 // TODO use a single packet list.. queue all events first..
200 MIDIPacket *mp = &(pl.packet[0]);
202 mp->timeStamp = AudioConvertNanosToHostTime(ts);
205 memcpy(mp->data, d, s);
207 MIDISend(_outputPorts[port], _outputEndPoints[port], &pl);
212 CoreMidiIo::discover()
216 assert(!_active && _midiClient);
218 ItemCount srcCount = MIDIGetNumberOfSources();
219 ItemCount dstCount = MIDIGetNumberOfDestinations();
222 _inputPorts = (MIDIPortRef *) malloc (srcCount * sizeof(MIDIPortRef));
223 _inputEndPoints = (MIDIEndpointRef*) malloc (srcCount * sizeof(MIDIEndpointRef));
224 _inputQueue = (CoreMIDIQueue*) calloc (srcCount, sizeof(CoreMIDIQueue));
225 _rb = (RingBuffer<uint8_t> **) malloc (srcCount * sizeof(RingBuffer<uint8_t>*));
228 _outputPorts = (MIDIPortRef *) malloc (dstCount * sizeof(MIDIPortRef));
229 _outputEndPoints = (MIDIEndpointRef*) malloc (dstCount * sizeof(MIDIEndpointRef));
232 for (ItemCount i = 0; i < srcCount; i++) {
234 MIDIEndpointRef src = MIDIGetSource(i);
235 CFStringRef port_name;
236 port_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("midi_capture_%lu"), i);
238 err = MIDIInputPortCreate (_midiClient, port_name, midiInputCallback, this, &_inputPorts[_n_midi_in]);
240 fprintf(stderr, "Cannot create Midi Output\n");
241 // TODO handle errors
244 _rb[_n_midi_in] = new RingBuffer<uint8_t>(1024 * sizeof(MIDIPacket));
245 _inputQueue[_n_midi_in] = CoreMIDIQueue();
246 MIDIPortConnectSource(_inputPorts[_n_midi_in], src, (void*) _rb[_n_midi_in]);
247 CFRelease(port_name);
248 _inputEndPoints[_n_midi_in] = src;
252 for (ItemCount i = 0; i < dstCount; i++) {
253 MIDIEndpointRef dst = MIDIGetDestination(i);
254 CFStringRef port_name;
255 port_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("midi_playback_%lu"), i);
258 err = MIDIOutputPortCreate (_midiClient, port_name, &_outputPorts[_n_midi_out]);
260 fprintf(stderr, "Cannot create Midi Output\n");
261 // TODO handle errors
264 MIDIPortConnectSource(_outputPorts[_n_midi_out], dst, NULL);
265 CFRelease(port_name);
266 _outputEndPoints[_n_midi_out] = dst;
270 if (_changed_callback) {
271 _changed_callback(_changed_arg);