Jack2 1.9.10

JackWinMMEInputPort.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 <memory>
00022 #include <stdexcept>
00023 
00024 #include "JackError.h"
00025 #include "JackTime.h"
00026 #include "JackMidiUtil.h"
00027 #include "JackWinMMEInputPort.h"
00028 #include "JackMidiWriteQueue.h"
00029 
00030 using Jack::JackWinMMEInputPort;
00031 
00033 // Static callbacks
00035 
00036 void CALLBACK
00037 JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message,
00038                                           DWORD port, DWORD param1,
00039                                           DWORD param2)
00040 {
00041     ((JackWinMMEInputPort *) port)->ProcessWinMME(message, param1, param2);
00042 }
00043 
00045 // Class
00047 
00048 JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name,
00049                                          const char *client_name,
00050                                          const char *driver_name, UINT index,
00051                                          size_t max_bytes, size_t max_messages)
00052 {
00053     thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
00054     std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
00055     write_queue = new JackMidiBufferWriteQueue();
00056     std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
00057     sysex_buffer = new jack_midi_data_t[max_bytes];
00058     char error_message[MAXERRORLENGTH];
00059     MMRESULT result = midiInOpen(&handle, index,
00060                                  (DWORD_PTR) HandleMidiInputEvent,
00061                                  (DWORD_PTR)this,
00062                                  CALLBACK_FUNCTION | MIDI_IO_STATUS);
00063     if (result != MMSYSERR_NOERROR) {
00064         GetInErrorString(result, error_message);
00065         goto delete_sysex_buffer;
00066     }
00067     sysex_header.dwBufferLength = max_bytes;
00068     sysex_header.dwFlags = 0;
00069     sysex_header.lpData = (LPSTR)sysex_buffer;
00070     result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
00071     if (result != MMSYSERR_NOERROR) {
00072         GetInErrorString(result, error_message);
00073         goto close_handle;
00074     }
00075     result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR));
00076     if (result != MMSYSERR_NOERROR) {
00077         GetInErrorString(result, error_message);
00078         goto unprepare_header;
00079     }
00080 
00081     MIDIINCAPS capabilities;
00082     char *name_tmp;
00083     result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
00084     if (result != MMSYSERR_NOERROR) {
00085         WriteInError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps",
00086                    result);
00087         name_tmp = (char*) driver_name;
00088     } else {
00089         name_tmp = capabilities.szPname;
00090     }
00091 
00092     snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, name_tmp,
00093              index + 1);
00094     snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1);
00095     jack_event = 0;
00096     started = false;
00097     write_queue_ptr.release();
00098     thread_queue_ptr.release();
00099     return;
00100 
00101  unprepare_header:
00102     result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
00103     if (result != MMSYSERR_NOERROR) {
00104         WriteInError("JackWinMMEInputPort [constructor]",
00105                      "midiInUnprepareHeader", result);
00106     }
00107  close_handle:
00108     result = midiInClose(handle);
00109     if (result != MMSYSERR_NOERROR) {
00110         WriteInError("JackWinMMEInputPort [constructor]", "midiInClose",
00111                      result);
00112     }
00113  delete_sysex_buffer:
00114     delete[] sysex_buffer;
00115     throw std::runtime_error(error_message);
00116 }
00117 
00118 JackWinMMEInputPort::~JackWinMMEInputPort()
00119 {
00120     MMRESULT result = midiInReset(handle);
00121     if (result != MMSYSERR_NOERROR) {
00122         WriteInError("JackWinMMEInputPort [destructor]", "midiInReset", result);
00123     }
00124     result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
00125     if (result != MMSYSERR_NOERROR) {
00126         WriteInError("JackWinMMEInputPort [destructor]",
00127                      "midiInUnprepareHeader", result);
00128     }
00129     result = midiInClose(handle);
00130     if (result != MMSYSERR_NOERROR) {
00131         WriteInError("JackWinMMEInputPort [destructor]", "midiInClose", result);
00132     }
00133     delete[] sysex_buffer;
00134     delete thread_queue;
00135     delete write_queue;
00136 }
00137 
00138 void
00139 JackWinMMEInputPort::EnqueueMessage(DWORD timestamp, size_t length,
00140                                     jack_midi_data_t *data)
00141 {
00142     jack_nframes_t frame =
00143         GetFramesFromTime(start_time + (((jack_time_t) timestamp) * 1000));
00144 
00145     // Debugging code
00146     jack_time_t current_time = GetMicroSeconds();
00147     jack_log("JackWinMMEInputPort::EnqueueMessage - enqueueing event at %f "
00148              "(frame: %d) with start offset '%d' scheduled for frame '%d'",
00149              ((double) current_time) / 1000.0, GetFramesFromTime(current_time),
00150              timestamp, frame);
00151     // End debugging code
00152 
00153     switch (thread_queue->EnqueueEvent(frame, length, data)) {
00154     case JackMidiWriteQueue::BUFFER_FULL:
00155         jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
00156                    "cannot currently accept a %d-byte event.  Dropping event.",
00157                    length);
00158         break;
00159     case JackMidiWriteQueue::BUFFER_TOO_SMALL:
00160         jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
00161                    "buffer is too small to enqueue a %d-byte event.  Dropping "
00162                    "event.", length);
00163         break;
00164     default:
00165         ;
00166     }
00167 }
00168 
00169 void
00170 JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text)
00171 {
00172     MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH);
00173     if (result != MMSYSERR_NOERROR) {
00174         snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error);
00175     }
00176 }
00177 
00178 void
00179 JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer,
00180                                  jack_nframes_t frames)
00181 {
00182     write_queue->ResetMidiBuffer(port_buffer, frames);
00183     if (! jack_event) {
00184         jack_event = thread_queue->DequeueEvent();
00185     }
00186     for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
00187         switch (write_queue->EnqueueEvent(jack_event, frames)) {
00188         case JackMidiWriteQueue::BUFFER_TOO_SMALL:
00189             jack_error("JackWinMMEMidiInputPort::Process - The buffer write "
00190                        "queue couldn't enqueue a %d-byte event. Dropping "
00191                        "event.", jack_event->size);
00192             // Fallthrough on purpose
00193         case JackMidiWriteQueue::OK:
00194             continue;
00195         default:
00196             break;
00197         }
00198         break;
00199     }
00200 }
00201 
00202 void
00203 JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2)
00204 {
00205     set_threaded_log_function();
00206     switch (message) {
00207     case MIM_CLOSE:
00208         jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
00209         break;
00210     case MIM_MOREDATA:
00211         jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device "
00212                   "driver thinks that JACK is not processing messages fast "
00213                   "enough.");
00214         // Fallthrough on purpose.
00215     case MIM_DATA: {
00216         jack_midi_data_t message_buffer[3];
00217         jack_midi_data_t status = param1 & 0xff;
00218         int length = GetMessageLength(status);
00219 
00220         switch (length) {
00221         case 3:
00222              message_buffer[2] = (param1 >> 16)  & 0xff;
00223             // Fallthrough on purpose.
00224         case 2:
00225             message_buffer[1] = (param1 >> 8) & 0xff;
00226             // Fallthrough on purpose.
00227         case 1:
00228             message_buffer[0] = status;
00229             break;
00230         case 0:
00231             jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
00232                        "input driver sent an MIM_DATA message with a sysex "
00233                        "status byte.");
00234             return;
00235         case -1:
00236             jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
00237                        "input driver sent an MIM_DATA message with an invalid "
00238                        "status byte.");
00239             return;
00240         }
00241         EnqueueMessage(param2, (size_t) length, message_buffer);
00242         break;
00243     }
00244     case MIM_LONGDATA: {
00245         LPMIDIHDR header = (LPMIDIHDR) param1;
00246         size_t byte_count = header->dwBytesRecorded;
00247         if (! byte_count) {
00248             jack_info("JackWinMMEInputPort::ProcessWinMME - WinMME driver has "
00249                       "returned sysex header to us with no bytes.  The JACK "
00250                       "driver is probably being stopped.");
00251             break;
00252         }
00253         jack_midi_data_t *data = (jack_midi_data_t *) header->lpData;
00254         if ((data[0] != 0xf0) || (data[byte_count - 1] != 0xf7)) {
00255             jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding "
00256                        "%d-byte sysex chunk.", byte_count);
00257         } else {
00258             EnqueueMessage(param2, byte_count, data);
00259         }
00260         // Is this realtime-safe?  This function isn't run in the JACK thread,
00261         // but we still want it to perform as quickly as possible.  Even if
00262         // this isn't realtime safe, it may not be avoidable.
00263         MMRESULT result = midiInAddBuffer(handle, &sysex_header,
00264                                           sizeof(MIDIHDR));
00265         if (result != MMSYSERR_NOERROR) {
00266             WriteInError("JackWinMMEInputPort::ProcessWinMME",
00267                          "midiInAddBuffer", result);
00268         }
00269         break;
00270     }
00271     case MIM_LONGERROR:
00272         jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or "
00273                    "incomplete sysex message received.");
00274         break;
00275     case MIM_OPEN:
00276         jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened.");
00277     }
00278 }
00279 
00280 bool
00281 JackWinMMEInputPort::Start()
00282 {
00283     if (! started) {
00284         start_time = GetMicroSeconds();
00285         MMRESULT result = midiInStart(handle);
00286         started = result == MMSYSERR_NOERROR;
00287         if (! started) {
00288             WriteInError("JackWinMMEInputPort::Start", "midiInStart", result);
00289         }
00290     }
00291     return started;
00292 }
00293 
00294 bool
00295 JackWinMMEInputPort::Stop()
00296 {
00297     if (started) {
00298         MMRESULT result = midiInStop(handle);
00299         started = result != MMSYSERR_NOERROR;
00300         if (started) {
00301             WriteInError("JackWinMMEInputPort::Stop", "midiInStop", result);
00302         }
00303     }
00304     return ! started;
00305 }
00306 
00307 void
00308 JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func,
00309                                 MMRESULT result)
00310 {
00311     char error_message[MAXERRORLENGTH];
00312     GetInErrorString(result, error_message);
00313     jack_error("%s - %s: %s", jack_func, mm_func, error_message);
00314 }