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 }
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
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
00222 case 2:
00223 message_buffer[1] = (param1 >> 8) & 0xff;
00224
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
00259
00260
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 }