00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
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
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
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
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
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
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
00224 case 2:
00225 message_buffer[1] = (param1 >> 8) & 0xff;
00226
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
00261
00262
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 }