CoreMidi driver starting to work.
[jack2.git] / macosx / coremidi / JackCoreMidiOutputPort.cpp
blob05e1786f9591c3c0e1045908e232bf1090dd8f35
1 /*
2 Copyright (C) 2011 Devin Anderson
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.
20 #include <cassert>
21 #include <cerrno>
22 #include <cstring>
23 #include <new>
24 #include <stdexcept>
26 #include "JackCoreMidiOutputPort.h"
27 #include "JackMidiUtil.h"
29 using Jack::JackCoreMidiOutputPort;
31 JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio,
32 size_t max_bytes,
33 size_t max_messages):
34 JackCoreMidiPort(time_ratio)
36 read_queue = new JackMidiBufferReadQueue();
37 std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
38 thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
39 std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
40 thread = new JackThread(this);
41 std::auto_ptr<JackThread> thread_ptr(thread);
42 sprintf(semaphore_name, "coremidi_%p", this);
43 thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0);
44 if (thread_queue_semaphore == (sem_t *) SEM_FAILED) {
45 throw std::runtime_error(strerror(errno));
47 thread_ptr.release();
48 thread_queue_ptr.release();
49 read_queue_ptr.release();
52 JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
54 Stop();
55 delete thread;
56 sem_destroy(thread_queue_semaphore);
57 sem_unlink(semaphore_name);
58 delete read_queue;
59 delete thread_queue;
62 bool
63 JackCoreMidiOutputPort::Execute()
65 jack_midi_event_t *event = 0;
66 MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer;
67 for (;;) {
68 MIDIPacket *packet = MIDIPacketListInit(packet_list);
69 assert(packet);
70 assert(thread_queue);
71 if (! event) {
72 event = GetCoreMidiEvent(true);
74 jack_midi_data_t *data = event->buffer;
76 // This is the latest time that the packet list can be sent out. We
77 // may want to consider subtracting some frames to leave room for the
78 // CoreMIDI driver/client to handle all of the events. There's a
79 // property called 'kMIDIPropertyAdvanceScheduleTimeMuSec' that might
80 // be useful in this case.
81 jack_nframes_t send_time = event->time;
83 size_t size = event->size;
84 MIDITimeStamp timestamp = GetTimeStampFromFrames(send_time);
85 packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet,
86 timestamp, size, data);
87 if (packet) {
88 while (GetCurrentFrame() < send_time) {
89 event = GetCoreMidiEvent(false);
90 if (! event) {
91 break;
93 packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer),
94 packet,
95 GetTimeStampFromFrames(event->time),
96 event->size, event->buffer);
97 if (! packet) {
98 break;
101 SendPacketList(packet_list);
102 } else {
104 // We have a large system exclusive event. We'll have to send it
105 // out in multiple packets.
106 size_t bytes_sent = 0;
107 do {
108 packet = MIDIPacketListInit(packet_list);
109 assert(packet);
110 size_t num_bytes = 0;
111 for (; bytes_sent < size; bytes_sent += num_bytes) {
112 size_t num_bytes = size - bytes_sent;
114 // We use 256 because the MIDIPacket struct defines the
115 // size of the 'data' member to be 256 bytes. I believe
116 // this prevents packets from being dynamically allocated
117 // by 'MIDIPacketListAdd', but I might be wrong.
118 if (num_bytes > 256) {
119 num_bytes = 256;
121 packet = MIDIPacketListAdd(packet_list,
122 sizeof(packet_buffer), packet,
123 timestamp, num_bytes,
124 data + bytes_sent);
125 if (! packet) {
126 break;
129 if (! SendPacketList(packet_list)) {
130 // An error occurred. The error message has already been
131 // output. We lick our wounds and move along.
132 break;
134 } while (bytes_sent < size);
135 event = 0;
138 return false;
141 jack_midi_event_t *
142 JackCoreMidiOutputPort::GetCoreMidiEvent(bool block)
144 if (! block) {
145 if (sem_trywait(thread_queue_semaphore)) {
146 if (errno != ETIMEDOUT) {
147 jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s",
148 strerror(errno));
150 return 0;
152 } else {
153 while (sem_wait(thread_queue_semaphore)) {
154 if (errno != EINTR) {
155 jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s",
156 strerror(errno));
157 return 0;
161 return thread_queue->DequeueEvent();
164 MIDITimeStamp
165 JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames)
167 return GetTimeFromFrames(frames) / time_ratio;
170 bool
171 JackCoreMidiOutputPort::Init()
173 set_threaded_log_function();
175 // OSX only...
176 UInt64 period = 0;
177 UInt64 computation = 500 * 1000;
178 UInt64 constraint = 500 * 1000;
179 thread->SetParams(period, computation, constraint);
181 // Use the server priority : y
182 if (thread->AcquireSelfRealTime()) {
183 jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
184 "scheduling. Continuing anyway.");
186 return true;
189 void
190 JackCoreMidiOutputPort::Initialize(const char *alias_name,
191 const char *client_name,
192 const char *driver_name, int index,
193 MIDIEndpointRef endpoint)
195 JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index,
196 endpoint, true);
199 void
200 JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
201 jack_nframes_t frames)
203 read_queue->ResetMidiBuffer(port_buffer);
204 for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
205 event = read_queue->DequeueEvent()) {
206 switch (thread_queue->EnqueueEvent(event, frames)) {
207 case JackMidiWriteQueue::BUFFER_FULL:
208 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
209 "queue buffer is full. Dropping event.");
210 break;
211 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
212 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
213 "queue couldn't enqueue a %d-byte event. Dropping "
214 "event.", event->size);
215 break;
216 default:
217 if (sem_post(thread_queue_semaphore)) {
218 jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected "
219 "error while posting to thread queue semaphore: %s",
220 strerror(errno));
226 bool
227 JackCoreMidiOutputPort::Start()
229 bool result = thread->GetStatus() != JackThread::kIdle;
230 if (! result) {
231 result = ! thread->StartSync();
232 if (! result) {
233 jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
234 "processing thread.");
237 return result;
240 bool
241 JackCoreMidiOutputPort::Stop()
243 bool result = thread->GetStatus() == JackThread::kIdle;
244 if (! result) {
245 result = ! thread->Kill();
246 if (! result) {
247 jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
248 "processing thread.");
251 return result;