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 <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 } 00196 break; 00197 } 00198 } 00199 00200 void 00201 JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) 00202 { 00203 set_threaded_log_function(); 00204 switch (message) { 00205 case MIM_CLOSE: 00206 jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed."); 00207 break; 00208 case MIM_MOREDATA: 00209 jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device " 00210 "driver thinks that JACK is not processing messages fast " 00211 "enough."); 00212 // Fallthrough on purpose. 00213 case MIM_DATA: { 00214 jack_midi_data_t message_buffer[3]; 00215 jack_midi_data_t status = param1 & 0xff; 00216 int length = GetMessageLength(status); 00217 00218 switch (length) { 00219 case 3: 00220 message_buffer[2] = (param1 >> 16) & 0xff; 00221 // Fallthrough on purpose. 00222 case 2: 00223 message_buffer[1] = (param1 >> 8) & 0xff; 00224 // Fallthrough on purpose. 00225 case 1: 00226 message_buffer[0] = status; 00227 break; 00228 case 0: 00229 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI " 00230 "input driver sent an MIM_DATA message with a sysex " 00231 "status byte."); 00232 return; 00233 case -1: 00234 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI " 00235 "input driver sent an MIM_DATA message with an invalid " 00236 "status byte."); 00237 return; 00238 } 00239 EnqueueMessage(param2, (size_t) length, message_buffer); 00240 break; 00241 } 00242 case MIM_LONGDATA: { 00243 LPMIDIHDR header = (LPMIDIHDR) param1; 00244 size_t byte_count = header->dwBytesRecorded; 00245 if (! byte_count) { 00246 jack_info("JackWinMMEInputPort::ProcessWinMME - WinMME driver has " 00247 "returned sysex header to us with no bytes. The JACK " 00248 "driver is probably being stopped."); 00249 break; 00250 } 00251 jack_midi_data_t *data = (jack_midi_data_t *) header->lpData; 00252 if ((data[0] != 0xf0) || (data[byte_count - 1] != 0xf7)) { 00253 jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding " 00254 "%d-byte sysex chunk.", byte_count); 00255 } else { 00256 EnqueueMessage(param2, byte_count, data); 00257 } 00258 // Is this realtime-safe? This function isn't run in the JACK thread, 00259 // but we still want it to perform as quickly as possible. Even if 00260 // this isn't realtime safe, it may not be avoidable. 00261 MMRESULT result = midiInAddBuffer(handle, &sysex_header, 00262 sizeof(MIDIHDR)); 00263 if (result != MMSYSERR_NOERROR) { 00264 WriteInError("JackWinMMEInputPort::ProcessWinMME", 00265 "midiInAddBuffer", result); 00266 } 00267 break; 00268 } 00269 case MIM_LONGERROR: 00270 jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or " 00271 "incomplete sysex message received."); 00272 break; 00273 case MIM_OPEN: 00274 jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened."); 00275 } 00276 } 00277 00278 bool 00279 JackWinMMEInputPort::Start() 00280 { 00281 if (! started) { 00282 start_time = GetMicroSeconds(); 00283 MMRESULT result = midiInStart(handle); 00284 started = result == MMSYSERR_NOERROR; 00285 if (! started) { 00286 WriteInError("JackWinMMEInputPort::Start", "midiInStart", result); 00287 } 00288 } 00289 return started; 00290 } 00291 00292 bool 00293 JackWinMMEInputPort::Stop() 00294 { 00295 if (started) { 00296 MMRESULT result = midiInStop(handle); 00297 started = result != MMSYSERR_NOERROR; 00298 if (started) { 00299 WriteInError("JackWinMMEInputPort::Stop", "midiInStop", result); 00300 } 00301 } 00302 return ! started; 00303 } 00304 00305 void 00306 JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func, 00307 MMRESULT result) 00308 { 00309 char error_message[MAXERRORLENGTH]; 00310 GetInErrorString(result, error_message); 00311 jack_error("%s - %s: %s", jack_func, mm_func, error_message); 00312 }