Jack2 1.9.10
|
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 }