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