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