Jack2  1.9.9
JackCoreMidiOutputPort.cpp
1 /*
2 Copyright (C) 2011 Devin Anderson
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 
20 #include <cassert>
21 #include <cerrno>
22 #include <cstring>
23 #include <new>
24 #include <stdexcept>
25 
26 #include "JackCoreMidiOutputPort.h"
27 #include "JackMidiUtil.h"
28 #include "JackTime.h"
29 #include "JackError.h"
30 
32 
33 JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio,
34  size_t max_bytes,
35  size_t max_messages):
36  JackCoreMidiPort(time_ratio)
37 {
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));
48  }
49  advance_schedule_time = 0;
50  thread_ptr.release();
51  thread_queue_ptr.release();
52  read_queue_ptr.release();
53 }
54 
55 JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
56 {
57  delete thread;
58  sem_close(thread_queue_semaphore);
59  sem_unlink(semaphore_name);
60  delete read_queue;
61  delete thread_queue;
62 }
63 
64 bool
65 JackCoreMidiOutputPort::Execute()
66 {
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 {
100 
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;
110 
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;
136 }
137 
139 JackCoreMidiOutputPort::GetCoreMidiEvent(bool block)
140 {
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();
159 }
160 
161 MIDITimeStamp
162 JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames)
163 {
164  return GetTimeFromFrames(frames) / time_ratio;
165 }
166 
167 bool
169 {
170  set_threaded_log_function();
171 
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);
177 
178  if (thread->AcquireSelfRealTime()) {
179  jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
180  "scheduling. Continuing anyway.");
181  }
182  return true;
183 }
184 
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)
191 {
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;
196 }
197 
198 void
199 JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
200  jack_nframes_t frames)
201 {
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  }
223 }
224 
225 bool
226 JackCoreMidiOutputPort::Start()
227 {
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;
237 }
238 
239 bool
240 JackCoreMidiOutputPort::Stop()
241 {
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;
251 }
void ResetMidiBuffer(JackMidiBuffer *buffer)
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
virtual jack_midi_event_t * DequeueEvent()
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)