Jack2 1.9.8
|
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 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 // We have a large system exclusive event. We'll have to send it 00100 // out in multiple packets. 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 // We use 256 because the MIDIPacket struct defines the 00110 // size of the 'data' member to be 256 bytes. I believe 00111 // this prevents packets from being dynamically allocated 00112 // by 'MIDIPacketListAdd', but I might be wrong. 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 // An error occurred. The error message has already been 00126 // output. We lick our wounds and move along. 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 // OSX only, values read in RT CoreMIDI thread 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 }