Remove non-jackdbus man pages
[jackdbus.git] / macosx / coremidi / JackCoreMidiOutputPort.mm
blobf62ebaffb5a7199d363f2f5397fde645c6117a8a
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::unique_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
40     thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
41     std::unique_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
42     thread = new JackThread(this);
43     std::unique_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));
48     }
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);
74         }
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;
88                 }
89                 event = GetCoreMidiEvent(false);
90                 if (! event) {
91                     break;
92                 }
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;
117                     }
118                     packet = MIDIPacketListAdd(packet_list,
119                                                sizeof(packet_buffer), packet,
120                                                timestamp, num_bytes,
121                                                data + bytes_sent);
122                     if (! packet) {
123                         break;
124                     }
125                 }
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;
130                 }
131             } while (bytes_sent < size);
132             event = 0;
133         }
134     }
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));
146             }
147             return 0;
148         }
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;
155             }
156         }
157     }
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.");
181     }
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));
220             }
221         }
222     }
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.");
234         }
235     }
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.");
248         }
249     }
250     return result;