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