Merge pull request #23 from jackaudio/device_reservation_fixes
[jack2.git] / macosx / coremidi / JackCoreMidiOutputPort.cpp
blob9fcee2a45f7469b273dfed31f8d35b2da4b487e9
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"
28 #include "JackTime.h"
29 #include "JackError.h"
31 using Jack::JackCoreMidiOutputPort;
33 JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio,
34 size_t max_bytes,
35 size_t max_messages):
36 JackCoreMidiPort(time_ratio)
38 read_queue = new JackMidiBufferReadQueue();
39 std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
40 thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
41 std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
42 thread = new JackThread(this);
43 std::auto_ptr<JackThread> thread_ptr(thread);
44 snprintf(semaphore_name, sizeof(semaphore_name), "coremidi_%p", this);
45 thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0);
46 if (thread_queue_semaphore == (sem_t *) SEM_FAILED) {
47 throw std::runtime_error(strerror(errno));
49 advance_schedule_time = 0;
50 thread_ptr.release();
51 thread_queue_ptr.release();
52 read_queue_ptr.release();
55 JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
57 delete thread;
58 sem_close(thread_queue_semaphore);
59 sem_unlink(semaphore_name);
60 delete read_queue;
61 delete thread_queue;
64 bool
65 JackCoreMidiOutputPort::Execute()
67 jack_midi_event_t *event = 0;
68 MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer;
69 for (;;) {
70 MIDIPacket *packet = MIDIPacketListInit(packet_list);
71 assert(packet);
72 if (! event) {
73 event = GetCoreMidiEvent(true);
75 jack_midi_data_t *data = event->buffer;
76 jack_nframes_t send_frame = event->time;
77 jack_time_t send_time =
78 GetTimeFromFrames(send_frame) - advance_schedule_time;
79 size_t size = event->size;
80 MIDITimeStamp timestamp = GetTimeStampFromFrames(send_frame);
81 packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet,
82 timestamp, size, data);
83 if (packet) {
84 do {
85 if (GetMicroSeconds() >= send_time) {
86 event = 0;
87 break;
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 } while (packet);
98 SendPacketList(packet_list);
99 } else {
101 // We have a large system exclusive event. We'll have to send it
102 // out in multiple packets.
103 size_t bytes_sent = 0;
104 do {
105 packet = MIDIPacketListInit(packet_list);
106 assert(packet);
107 size_t num_bytes = 0;
108 for (; bytes_sent < size; bytes_sent += num_bytes) {
109 size_t num_bytes = size - bytes_sent;
111 // We use 256 because the MIDIPacket struct defines the
112 // size of the 'data' member to be 256 bytes. I believe
113 // this prevents packets from being dynamically allocated
114 // by 'MIDIPacketListAdd', but I might be wrong.
115 if (num_bytes > 256) {
116 num_bytes = 256;
118 packet = MIDIPacketListAdd(packet_list,
119 sizeof(packet_buffer), packet,
120 timestamp, num_bytes,
121 data + bytes_sent);
122 if (! packet) {
123 break;
126 if (! SendPacketList(packet_list)) {
127 // An error occurred. The error message has already been
128 // output. We lick our wounds and move along.
129 break;
131 } while (bytes_sent < size);
132 event = 0;
135 return false;
138 jack_midi_event_t *
139 JackCoreMidiOutputPort::GetCoreMidiEvent(bool block)
141 if (! block) {
142 if (sem_trywait(thread_queue_semaphore)) {
143 if (errno != EAGAIN) {
144 jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s",
145 strerror(errno));
147 return 0;
149 } else {
150 while (sem_wait(thread_queue_semaphore)) {
151 if (errno != EINTR) {
152 jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s",
153 strerror(errno));
154 return 0;
158 return thread_queue->DequeueEvent();
161 MIDITimeStamp
162 JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames)
164 return GetTimeFromFrames(frames) / time_ratio;
167 bool
168 JackCoreMidiOutputPort::Init()
170 set_threaded_log_function();
172 // OSX only, values read in RT CoreMIDI thread
173 UInt64 period = 0;
174 UInt64 computation = 250 * 1000;
175 UInt64 constraint = 500 * 1000;
176 thread->SetParams(period, computation, constraint);
178 if (thread->AcquireSelfRealTime()) {
179 jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
180 "scheduling. Continuing anyway.");
182 return true;
185 void
186 JackCoreMidiOutputPort::Initialize(const char *alias_name,
187 const char *client_name,
188 const char *driver_name, int index,
189 MIDIEndpointRef endpoint,
190 SInt32 advance_schedule_time)
192 JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index,
193 endpoint, true);
194 assert(advance_schedule_time >= 0);
195 this->advance_schedule_time = advance_schedule_time;
198 void
199 JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
200 jack_nframes_t frames)
202 read_queue->ResetMidiBuffer(port_buffer);
203 for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
204 event = read_queue->DequeueEvent()) {
205 switch (thread_queue->EnqueueEvent(event, frames)) {
206 case JackMidiWriteQueue::BUFFER_FULL:
207 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
208 "queue buffer is full. Dropping event.");
209 break;
210 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
211 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
212 "queue couldn't enqueue a %d-byte event. Dropping "
213 "event.", event->size);
214 break;
215 default:
216 if (sem_post(thread_queue_semaphore)) {
217 jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected "
218 "error while posting to thread queue semaphore: %s",
219 strerror(errno));
225 bool
226 JackCoreMidiOutputPort::Start()
228 bool result = thread->GetStatus() != JackThread::kIdle;
229 if (! result) {
230 result = ! thread->StartSync();
231 if (! result) {
232 jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
233 "processing thread.");
236 return result;
239 bool
240 JackCoreMidiOutputPort::Stop()
242 bool result = thread->GetStatus() == JackThread::kIdle;
243 if (! result) {
244 result = ! thread->Kill();
245 if (! result) {
246 jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
247 "processing thread.");
250 return result;