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 <memory> 00021 #include <stdexcept> 00022 00023 #include "JackMidiUtil.h" 00024 #include "JackTime.h" 00025 #include "JackWinMMEOutputPort.h" 00026 #include "JackGlobals.h" 00027 #include "JackEngineControl.h" 00028 00029 using Jack::JackWinMMEOutputPort; 00030 00032 // Static callbacks 00034 00035 void CALLBACK 00036 JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle, UINT message, 00037 DWORD_PTR port, DWORD_PTR param1, 00038 DWORD_PTR param2) 00039 { 00040 ((JackWinMMEOutputPort *) port)->HandleMessage(message, param1, param2); 00041 } 00042 00044 // Class 00046 00047 JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, 00048 const char *client_name, 00049 const char *driver_name, 00050 UINT index, 00051 size_t max_bytes, 00052 size_t max_messages) 00053 { 00054 read_queue = new JackMidiBufferReadQueue(); 00055 std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue); 00056 thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); 00057 std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue); 00058 thread = new JackThread(this); 00059 std::auto_ptr<JackThread> thread_ptr(thread); 00060 char error_message[MAXERRORLENGTH]; 00061 MMRESULT result = midiOutOpen(&handle, index, (DWORD_PTR)HandleMessageEvent, 00062 (DWORD_PTR)this, CALLBACK_FUNCTION); 00063 if (result != MMSYSERR_NOERROR) { 00064 GetOutErrorString(result, error_message); 00065 goto raise_exception; 00066 } 00067 thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL); 00068 if (thread_queue_semaphore == NULL) { 00069 GetOSErrorString(error_message); 00070 goto close_handle; 00071 } 00072 sysex_semaphore = CreateSemaphore(NULL, 0, 1, NULL); 00073 if (sysex_semaphore == NULL) { 00074 GetOSErrorString(error_message); 00075 goto destroy_thread_queue_semaphore; 00076 } 00077 MIDIOUTCAPS capabilities; 00078 char *name_tmp; 00079 result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities)); 00080 if (result != MMSYSERR_NOERROR) { 00081 WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", 00082 result); 00083 name_tmp = (char*)driver_name; 00084 } else { 00085 name_tmp = capabilities.szPname; 00086 } 00087 snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, name_tmp, 00088 index + 1); 00089 snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); 00090 read_queue_ptr.release(); 00091 thread_queue_ptr.release(); 00092 thread_ptr.release(); 00093 return; 00094 00095 destroy_thread_queue_semaphore: 00096 if (! CloseHandle(thread_queue_semaphore)) { 00097 WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle"); 00098 } 00099 close_handle: 00100 result = midiOutClose(handle); 00101 if (result != MMSYSERR_NOERROR) { 00102 WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutClose", 00103 result); 00104 } 00105 raise_exception: 00106 throw std::runtime_error(error_message); 00107 } 00108 00109 JackWinMMEOutputPort::~JackWinMMEOutputPort() 00110 { 00111 MMRESULT result = midiOutReset(handle); 00112 if (result != MMSYSERR_NOERROR) { 00113 WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutReset", 00114 result); 00115 } 00116 result = midiOutClose(handle); 00117 if (result != MMSYSERR_NOERROR) { 00118 WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutClose", 00119 result); 00120 } 00121 if (! CloseHandle(sysex_semaphore)) { 00122 WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle"); 00123 } 00124 if (! CloseHandle(thread_queue_semaphore)) { 00125 WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle"); 00126 } 00127 delete read_queue; 00128 delete thread_queue; 00129 delete thread; 00130 } 00131 00132 bool 00133 JackWinMMEOutputPort::Execute() 00134 { 00135 for (;;) { 00136 if (! Wait(thread_queue_semaphore)) { 00137 jack_log("JackWinMMEOutputPort::Execute BREAK"); 00138 00139 break; 00140 } 00141 jack_midi_event_t *event = thread_queue->DequeueEvent(); 00142 if (! event) { 00143 break; 00144 } 00145 jack_time_t frame_time = GetTimeFromFrames(event->time); 00146 jack_time_t current_time = GetMicroSeconds(); 00147 if (frame_time > current_time) { 00148 LARGE_INTEGER due_time; 00149 00150 // 100 ns resolution 00151 due_time.QuadPart = 00152 -((LONGLONG) ((frame_time - current_time) * 10)); 00153 if (! SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0)) { 00154 WriteOSError("JackWinMMEOutputPort::Execute", 00155 "SetWaitableTimer"); 00156 break; 00157 } 00158 00159 // Debugging code 00160 jack_log("JackWinMMEOutputPort::Execute - waiting at %f for %f " 00161 "milliseconds before sending message (current frame: %d, " 00162 "send frame: %d)", 00163 ((double) current_time) / 1000.0, 00164 ((double) (frame_time - current_time)) / 1000.0, 00165 GetFramesFromTime(current_time), event->time); 00166 // End debugging code 00167 00168 if (! Wait(timer)) { 00169 break; 00170 } 00171 00172 // Debugging code 00173 jack_time_t wakeup_time = GetMicroSeconds(); 00174 jack_log("JackWinMMEOutputPort::Execute - woke up at %f.", 00175 ((double) wakeup_time) / 1000.0); 00176 if (wakeup_time > frame_time) { 00177 jack_log("JackWinMMEOutputPort::Execute - overslept by %f " 00178 "milliseconds.", 00179 ((double) (wakeup_time - frame_time)) / 1000.0); 00180 } else if (wakeup_time < frame_time) { 00181 jack_log("JackWinMMEOutputPort::Execute - woke up %f " 00182 "milliseconds too early.", 00183 ((double) (frame_time - wakeup_time)) / 1000.0); 00184 } 00185 // End debugging code 00186 00187 } 00188 jack_midi_data_t *data = event->buffer; 00189 DWORD message = 0; 00190 MMRESULT result; 00191 size_t size = event->size; 00192 switch (size) { 00193 case 3: 00194 message |= (((DWORD) data[2]) << 16); 00195 // Fallthrough on purpose. 00196 case 2: 00197 message |= (((DWORD) data[1]) << 8); 00198 // Fallthrough on purpose. 00199 case 1: 00200 message |= (DWORD) data[0]; 00201 result = midiOutShortMsg(handle, message); 00202 if (result != MMSYSERR_NOERROR) { 00203 WriteOutError("JackWinMMEOutputPort::Execute", 00204 "midiOutShortMsg", result); 00205 } 00206 continue; 00207 } 00208 MIDIHDR header; 00209 header.dwBufferLength = size; 00210 header.dwFlags = 0; 00211 header.lpData = (LPSTR) data; 00212 result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); 00213 if (result != MMSYSERR_NOERROR) { 00214 WriteOutError("JackWinMMEOutputPort::Execute", 00215 "midiOutPrepareHeader", result); 00216 continue; 00217 } 00218 result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR)); 00219 if (result != MMSYSERR_NOERROR) { 00220 WriteOutError("JackWinMMEOutputPort::Execute", "midiOutLongMsg", 00221 result); 00222 continue; 00223 } 00224 00225 // System exclusive messages may be sent synchronously or 00226 // asynchronously. The choice is up to the WinMME driver. So, we wait 00227 // until the message is sent, regardless of the driver's choice. 00228 if (! Wait(sysex_semaphore)) { 00229 break; 00230 } 00231 00232 result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR)); 00233 if (result != MMSYSERR_NOERROR) { 00234 WriteOutError("JackWinMMEOutputPort::Execute", 00235 "midiOutUnprepareHeader", result); 00236 break; 00237 } 00238 } 00239 return false; 00240 } 00241 00242 void 00243 JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text) 00244 { 00245 MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH); 00246 if (result != MMSYSERR_NOERROR) { 00247 snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error); 00248 } 00249 } 00250 00251 void 00252 JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, 00253 DWORD_PTR param2) 00254 { 00255 set_threaded_log_function(); 00256 switch (message) { 00257 case MOM_CLOSE: 00258 jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed."); 00259 break; 00260 case MOM_DONE: 00261 Signal(sysex_semaphore); 00262 break; 00263 case MOM_OPEN: 00264 jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); 00265 break; 00266 case MOM_POSITIONCB: 00267 LPMIDIHDR header = (LPMIDIHDR) param1; 00268 jack_info("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d " 00269 "bytes of the current sysex message have been sent.", 00270 header->dwOffset, header->dwBytesRecorded); 00271 } 00272 } 00273 00274 bool 00275 JackWinMMEOutputPort::Init() 00276 { 00277 set_threaded_log_function(); 00278 // XX: Can more be done? Ideally, this thread should have the JACK server 00279 // thread priority + 1. 00280 if (thread->AcquireSelfRealTime(GetEngineControl()->fServerPriority)) { 00281 jack_error("JackWinMMEOutputPort::Init - could not acquire realtime " 00282 "scheduling. Continuing anyway."); 00283 } 00284 return true; 00285 } 00286 00287 void 00288 JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer, 00289 jack_nframes_t frames) 00290 { 00291 read_queue->ResetMidiBuffer(port_buffer); 00292 for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; 00293 event = read_queue->DequeueEvent()) { 00294 00295 switch (thread_queue->EnqueueEvent(event, frames)) { 00296 case JackMidiWriteQueue::BUFFER_FULL: 00297 jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue " 00298 "buffer is full. Dropping event."); 00299 break; 00300 case JackMidiWriteQueue::BUFFER_TOO_SMALL: 00301 jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue " 00302 "couldn't enqueue a %d-byte event. Dropping event.", 00303 event->size); 00304 break; 00305 default: 00306 Signal(thread_queue_semaphore); 00307 } 00308 } 00309 } 00310 00311 bool 00312 JackWinMMEOutputPort::Signal(HANDLE semaphore) 00313 { 00314 bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL); 00315 if (! result) { 00316 WriteOSError("JackWinMMEOutputPort::Signal", "ReleaseSemaphore"); 00317 } 00318 return result; 00319 } 00320 00321 bool 00322 JackWinMMEOutputPort::Start() 00323 { 00324 if (thread->GetStatus() != JackThread::kIdle) { 00325 return true; 00326 } 00327 timer = CreateWaitableTimer(NULL, FALSE, NULL); 00328 if (! timer) { 00329 WriteOSError("JackWinMMEOutputPort::Start", "CreateWaitableTimer"); 00330 return false; 00331 } 00332 if (! thread->StartSync()) { 00333 return true; 00334 } 00335 jack_error("JackWinMMEOutputPort::Start - failed to start MIDI processing " 00336 "thread."); 00337 if (! CloseHandle(timer)) { 00338 WriteOSError("JackWinMMEOutputPort::Start", "CloseHandle"); 00339 } 00340 return false; 00341 } 00342 00343 bool 00344 JackWinMMEOutputPort::Stop() 00345 { 00346 jack_info("JackWinMMEOutputPort::Stop - stopping MIDI output port " 00347 "processing thread."); 00348 00349 int result; 00350 const char *verb; 00351 switch (thread->GetStatus()) { 00352 case JackThread::kIniting: 00353 case JackThread::kStarting: 00354 result = thread->Kill(); 00355 verb = "kill"; 00356 break; 00357 case JackThread::kRunning: 00358 Signal(thread_queue_semaphore); 00359 result = thread->Stop(); 00360 verb = "stop"; 00361 break; 00362 default: 00363 return true; 00364 } 00365 if (result) { 00366 jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing " 00367 "thread.", verb); 00368 } 00369 if (! CloseHandle(timer)) { 00370 WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle"); 00371 result = -1; 00372 } 00373 return ! result; 00374 } 00375 00376 bool 00377 JackWinMMEOutputPort::Wait(HANDLE semaphore) 00378 { 00379 DWORD result = WaitForSingleObject(semaphore, INFINITE); 00380 switch (result) { 00381 case WAIT_FAILED: 00382 WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject"); 00383 break; 00384 case WAIT_OBJECT_0: 00385 return true; 00386 default: 00387 jack_error("JackWinMMEOutputPort::Wait - unexpected result from " 00388 "'WaitForSingleObject'."); 00389 } 00390 return false; 00391 } 00392 00393 void 00394 JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func, 00395 MMRESULT result) 00396 { 00397 char error_message[MAXERRORLENGTH]; 00398 GetOutErrorString(result, error_message); 00399 jack_error("%s - %s: %s", jack_func, mm_func, error_message); 00400 }