00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <cassert>
00021 #include <cerrno>
00022 #include <cstring>
00023 #include <new>
00024 #include <stdexcept>
00025
00026 #include "JackCoreMidiOutputPort.h"
00027 #include "JackMidiUtil.h"
00028 #include "JackTime.h"
00029
00030 using Jack::JackCoreMidiOutputPort;
00031
00032 JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio,
00033 size_t max_bytes,
00034 size_t max_messages):
00035 JackCoreMidiPort(time_ratio)
00036 {
00037 read_queue = new JackMidiBufferReadQueue();
00038 std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
00039 thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
00040 std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
00041 thread = new JackThread(this);
00042 std::auto_ptr<JackThread> thread_ptr(thread);
00043 snprintf(semaphore_name, sizeof(semaphore_name), "coremidi_%p", this);
00044 thread_queue_semaphore = sem_open(semaphore_name, O_CREAT, 0777, 0);
00045 if (thread_queue_semaphore == (sem_t *) SEM_FAILED) {
00046 throw std::runtime_error(strerror(errno));
00047 }
00048 advance_schedule_time = 0;
00049 thread_ptr.release();
00050 thread_queue_ptr.release();
00051 read_queue_ptr.release();
00052 }
00053
00054 JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
00055 {
00056 delete thread;
00057 sem_close(thread_queue_semaphore);
00058 sem_unlink(semaphore_name);
00059 delete read_queue;
00060 delete thread_queue;
00061 }
00062
00063 bool
00064 JackCoreMidiOutputPort::Execute()
00065 {
00066 jack_midi_event_t *event = 0;
00067 MIDIPacketList *packet_list = (MIDIPacketList *) packet_buffer;
00068 for (;;) {
00069 MIDIPacket *packet = MIDIPacketListInit(packet_list);
00070 assert(packet);
00071 if (! event) {
00072 event = GetCoreMidiEvent(true);
00073 }
00074 jack_midi_data_t *data = event->buffer;
00075 jack_nframes_t send_frame = event->time;
00076 jack_time_t send_time =
00077 GetTimeFromFrames(send_frame) - advance_schedule_time;
00078 size_t size = event->size;
00079 MIDITimeStamp timestamp = GetTimeStampFromFrames(send_frame);
00080 packet = MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet,
00081 timestamp, size, data);
00082 if (packet) {
00083 while (GetMicroSeconds() < send_time) {
00084 event = GetCoreMidiEvent(false);
00085 if (! event) {
00086 break;
00087 }
00088 packet = MIDIPacketListAdd(packet_list, sizeof(packet_buffer),
00089 packet,
00090 GetTimeStampFromFrames(event->time),
00091 event->size, event->buffer);
00092 if (! packet) {
00093 break;
00094 }
00095 }
00096 SendPacketList(packet_list);
00097 } else {
00098
00099
00100
00101 size_t bytes_sent = 0;
00102 do {
00103 packet = MIDIPacketListInit(packet_list);
00104 assert(packet);
00105 size_t num_bytes = 0;
00106 for (; bytes_sent < size; bytes_sent += num_bytes) {
00107 size_t num_bytes = size - bytes_sent;
00108
00109
00110
00111
00112
00113 if (num_bytes > 256) {
00114 num_bytes = 256;
00115 }
00116 packet = MIDIPacketListAdd(packet_list,
00117 sizeof(packet_buffer), packet,
00118 timestamp, num_bytes,
00119 data + bytes_sent);
00120 if (! packet) {
00121 break;
00122 }
00123 }
00124 if (! SendPacketList(packet_list)) {
00125
00126
00127 break;
00128 }
00129 } while (bytes_sent < size);
00130 event = 0;
00131 }
00132 }
00133 return false;
00134 }
00135
00136 jack_midi_event_t *
00137 JackCoreMidiOutputPort::GetCoreMidiEvent(bool block)
00138 {
00139 if (! block) {
00140 if (sem_trywait(thread_queue_semaphore)) {
00141 if (errno != EAGAIN) {
00142 jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s",
00143 strerror(errno));
00144 }
00145 return 0;
00146 }
00147 } else {
00148 while (sem_wait(thread_queue_semaphore)) {
00149 if (errno != EINTR) {
00150 jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s",
00151 strerror(errno));
00152 return 0;
00153 }
00154 }
00155 }
00156 return thread_queue->DequeueEvent();
00157 }
00158
00159 MIDITimeStamp
00160 JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames)
00161 {
00162 return GetTimeFromFrames(frames) / time_ratio;
00163 }
00164
00165 bool
00166 JackCoreMidiOutputPort::Init()
00167 {
00168 set_threaded_log_function();
00169
00170
00171 UInt64 period = 0;
00172 UInt64 computation = 250 * 1000;
00173 UInt64 constraint = 500 * 1000;
00174 thread->SetParams(period, computation, constraint);
00175
00176 if (thread->AcquireSelfRealTime()) {
00177 jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
00178 "scheduling. Continuing anyway.");
00179 }
00180 return true;
00181 }
00182
00183 void
00184 JackCoreMidiOutputPort::Initialize(const char *alias_name,
00185 const char *client_name,
00186 const char *driver_name, int index,
00187 MIDIEndpointRef endpoint,
00188 SInt32 advance_schedule_time)
00189 {
00190 JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index,
00191 endpoint, true);
00192 assert(advance_schedule_time >= 0);
00193 this->advance_schedule_time = advance_schedule_time;
00194 }
00195
00196 void
00197 JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
00198 jack_nframes_t frames)
00199 {
00200 read_queue->ResetMidiBuffer(port_buffer);
00201 for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
00202 event = read_queue->DequeueEvent()) {
00203 switch (thread_queue->EnqueueEvent(event, frames)) {
00204 case JackMidiWriteQueue::BUFFER_FULL:
00205 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
00206 "queue buffer is full. Dropping event.");
00207 break;
00208 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
00209 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
00210 "queue couldn't enqueue a %d-byte event. Dropping "
00211 "event.", event->size);
00212 break;
00213 default:
00214 if (sem_post(thread_queue_semaphore)) {
00215 jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected "
00216 "error while posting to thread queue semaphore: %s",
00217 strerror(errno));
00218 }
00219 }
00220 }
00221 }
00222
00223 bool
00224 JackCoreMidiOutputPort::Start()
00225 {
00226 bool result = thread->GetStatus() != JackThread::kIdle;
00227 if (! result) {
00228 result = ! thread->StartSync();
00229 if (! result) {
00230 jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
00231 "processing thread.");
00232 }
00233 }
00234 return result;
00235 }
00236
00237 bool
00238 JackCoreMidiOutputPort::Stop()
00239 {
00240 bool result = thread->GetStatus() == JackThread::kIdle;
00241 if (! result) {
00242 result = ! thread->Kill();
00243 if (! result) {
00244 jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
00245 "processing thread.");
00246 }
00247 }
00248 return result;
00249 }