24 #include "JackError.h"
26 #include "JackMidiUtil.h"
27 #include "JackWinMMEInputPort.h"
28 #include "JackMidiWriteQueue.h"
37 JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message,
38 DWORD port, DWORD param1,
48 JackWinMMEInputPort::JackWinMMEInputPort(
const char *alias_name,
49 const char *client_name,
50 const char *driver_name, UINT index,
51 size_t max_bytes,
size_t max_messages)
53 thread_queue =
new JackMidiAsyncQueue(max_bytes, max_messages);
54 std::unique_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
55 write_queue =
new JackMidiBufferWriteQueue();
56 std::unique_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
57 sysex_buffer =
new jack_midi_data_t[max_bytes];
58 char error_message[MAXERRORLENGTH];
59 MMRESULT result = midiInOpen(&handle, index,
60 (DWORD_PTR) HandleMidiInputEvent,
62 CALLBACK_FUNCTION | MIDI_IO_STATUS);
63 if (result != MMSYSERR_NOERROR) {
64 GetInErrorString(result, error_message);
65 goto delete_sysex_buffer;
67 sysex_header.dwBufferLength = max_bytes;
68 sysex_header.dwFlags = 0;
69 sysex_header.lpData = (LPSTR)sysex_buffer;
70 result = midiInPrepareHeader(handle, &sysex_header,
sizeof(MIDIHDR));
71 if (result != MMSYSERR_NOERROR) {
72 GetInErrorString(result, error_message);
75 result = midiInAddBuffer(handle, &sysex_header,
sizeof(MIDIHDR));
76 if (result != MMSYSERR_NOERROR) {
77 GetInErrorString(result, error_message);
78 goto unprepare_header;
81 MIDIINCAPS capabilities;
83 result = midiInGetDevCaps(index, &capabilities,
sizeof(capabilities));
84 if (result != MMSYSERR_NOERROR) {
85 WriteInError(
"JackWinMMEInputPort [constructor]",
"midiInGetDevCaps",
87 name_tmp = (
char*) driver_name;
89 name_tmp = capabilities.szPname;
92 snprintf(alias,
sizeof(alias) - 1,
"%s:%s:in%d", alias_name, name_tmp,
94 snprintf(name,
sizeof(name) - 1,
"%s:capture_%d", client_name, index + 1);
95 strncpy(device_name, name_tmp,
sizeof(device_name) - 1);
98 write_queue_ptr.release();
99 thread_queue_ptr.release();
103 result = midiInUnprepareHeader(handle, &sysex_header,
sizeof(MIDIHDR));
104 if (result != MMSYSERR_NOERROR) {
105 WriteInError(
"JackWinMMEInputPort [constructor]",
106 "midiInUnprepareHeader", result);
109 result = midiInClose(handle);
110 if (result != MMSYSERR_NOERROR) {
111 WriteInError(
"JackWinMMEInputPort [constructor]",
"midiInClose",
115 delete[] sysex_buffer;
116 throw std::runtime_error(error_message);
119 JackWinMMEInputPort::~JackWinMMEInputPort()
121 MMRESULT result = midiInReset(handle);
122 if (result != MMSYSERR_NOERROR) {
123 WriteInError(
"JackWinMMEInputPort [destructor]",
"midiInReset", result);
125 result = midiInUnprepareHeader(handle, &sysex_header,
sizeof(MIDIHDR));
126 if (result != MMSYSERR_NOERROR) {
127 WriteInError(
"JackWinMMEInputPort [destructor]",
128 "midiInUnprepareHeader", result);
130 result = midiInClose(handle);
131 if (result != MMSYSERR_NOERROR) {
132 WriteInError(
"JackWinMMEInputPort [destructor]",
"midiInClose", result);
134 delete[] sysex_buffer;
140 JackWinMMEInputPort::EnqueueMessage(DWORD timestamp,
size_t length,
141 jack_midi_data_t *data)
143 jack_nframes_t frame =
144 GetFramesFromTime(start_time + (((jack_time_t) timestamp) * 1000));
147 jack_time_t current_time = GetMicroSeconds();
148 jack_log(
"JackWinMMEInputPort::EnqueueMessage - enqueueing event at %f "
149 "(frame: %d) with start offset '%d' scheduled for frame '%d'",
150 ((
double) current_time) / 1000.0, GetFramesFromTime(current_time),
154 switch (thread_queue->
EnqueueEvent(frame, length, data)) {
155 case JackMidiWriteQueue::BUFFER_FULL:
156 jack_error(
"JackWinMMEInputPort::EnqueueMessage - The thread queue "
157 "cannot currently accept a %d-byte event. Dropping event.",
160 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
161 jack_error(
"JackWinMMEInputPort::EnqueueMessage - The thread queue "
162 "buffer is too small to enqueue a %d-byte event. Dropping "
171 JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text)
173 MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH);
174 if (result != MMSYSERR_NOERROR) {
175 snprintf(text, MAXERRORLENGTH,
"Unknown error code '%d'", error);
180 JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer,
181 jack_nframes_t frames)
187 for (; jack_event; jack_event = thread_queue->
DequeueEvent()) {
188 switch (write_queue->
EnqueueEvent(jack_event, frames)) {
189 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
190 jack_error(
"JackWinMMEMidiInputPort::Process - The buffer write "
191 "queue couldn't enqueue a %d-byte event. Dropping "
192 "event.", jack_event->
size);
194 case JackMidiWriteQueue::OK:
204 JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2)
206 set_threaded_log_function();
209 jack_log(
"JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
212 jack_log(
"JackWinMMEInputPort::ProcessWinMME - The MIDI input device "
213 "driver thinks that JACK is not processing messages fast "
217 jack_midi_data_t message_buffer[3];
218 jack_midi_data_t status = param1 & 0xff;
219 int length = GetMessageLength(status);
223 message_buffer[2] = (param1 >> 16) & 0xff;
226 message_buffer[1] = (param1 >> 8) & 0xff;
229 message_buffer[0] = status;
232 jack_error(
"JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
233 "input driver sent an MIM_DATA message with a sysex "
237 jack_error(
"JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
238 "input driver sent an MIM_DATA message with an invalid "
242 EnqueueMessage(param2, (
size_t) length, message_buffer);
246 LPMIDIHDR header = (LPMIDIHDR) param1;
247 size_t byte_count = header->dwBytesRecorded;
249 jack_log(
"JackWinMMEInputPort::ProcessWinMME - WinMME driver has "
250 "returned sysex header to us with no bytes. The JACK "
251 "driver is probably being stopped.");
254 jack_midi_data_t *data = (jack_midi_data_t *) header->lpData;
255 if ((data[0] != 0xf0) || (data[byte_count - 1] != 0xf7)) {
256 jack_error(
"JackWinMMEInputPort::ProcessWinMME - Discarding "
257 "%d-byte sysex chunk.", byte_count);
259 EnqueueMessage(param2, byte_count, data);
264 MMRESULT result = midiInAddBuffer(handle, &sysex_header,
266 if (result != MMSYSERR_NOERROR) {
267 WriteInError(
"JackWinMMEInputPort::ProcessWinMME",
268 "midiInAddBuffer", result);
273 jack_error(
"JackWinMMEInputPort::ProcessWinMME - Invalid or "
274 "incomplete sysex message received.");
277 jack_log(
"JackWinMMEInputPort::ProcessWinMME - MIDI device opened.");
282 JackWinMMEInputPort::Start()
285 start_time = GetMicroSeconds();
286 MMRESULT result = midiInStart(handle);
287 started = result == MMSYSERR_NOERROR;
289 WriteInError(
"JackWinMMEInputPort::Start",
"midiInStart", result);
296 JackWinMMEInputPort::Stop()
299 MMRESULT result = midiInStop(handle);
300 started = result != MMSYSERR_NOERROR;
302 WriteInError(
"JackWinMMEInputPort::Stop",
"midiInStop", result);
309 JackWinMMEInputPort::WriteInError(
const char *jack_func,
const char *mm_func,
312 char error_message[MAXERRORLENGTH];
313 GetInErrorString(result, error_message);
314 jack_error(
"%s - %s: %s", jack_func, mm_func, error_message);
EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
SERVER_EXPORT void jack_error(const char *fmt,...)
void ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames)
virtual jack_midi_event_t * DequeueEvent()
SERVER_EXPORT void jack_log(const char *fmt,...)
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)