Jack2 1.9.10

JackCoreMidiOutputPort.cpp

00001 /*
00002 Copyright (C) 2011 Devin Anderson
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU General Public License as published by
00006 the Free Software Foundation; either version 2 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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             // We have a large system exclusive event.  We'll have to send it
00102             // out in multiple packets.
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                     // We use 256 because the MIDIPacket struct defines the
00112                     // size of the 'data' member to be 256 bytes.  I believe
00113                     // this prevents packets from being dynamically allocated
00114                     // by 'MIDIPacketListAdd', but I might be wrong.
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                     // An error occurred.  The error message has already been
00128                     // output.  We lick our wounds and move along.
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     // OSX only, values read in RT CoreMIDI thread
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 }