Jack2 1.9.12

JackWinMMEInputPort.cpp
1 /*
2 Copyright (C) 2011 Devin Anderson
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cassert>
21 #include <memory>
22 #include <stdexcept>
23
24 #include "JackError.h"
25 #include "JackTime.h"
26 #include "JackMidiUtil.h"
27 #include "JackWinMMEInputPort.h"
28 #include "JackMidiWriteQueue.h"
29
30 using Jack::JackWinMMEInputPort;
31
33 // Static callbacks
35
36 void CALLBACK
37 JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message,
38 DWORD port, DWORD param1,
39 DWORD param2)
40 {
41 ((JackWinMMEInputPort *) port)->ProcessWinMME(message, param1, param2);
42 }
43
45 // Class
47
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)
52 {
53 thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
54 std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
55 write_queue = new JackMidiBufferWriteQueue();
56 std::auto_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,
61 (DWORD_PTR)this,
62 CALLBACK_FUNCTION | MIDI_IO_STATUS);
63 if (result != MMSYSERR_NOERROR) {
64 GetInErrorString(result, error_message);
65 goto delete_sysex_buffer;
66 }
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);
73 goto close_handle;
74 }
75 result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR));
76 if (result != MMSYSERR_NOERROR) {
77 GetInErrorString(result, error_message);
78 goto unprepare_header;
79 }
80
81 MIDIINCAPS capabilities;
82 char *name_tmp;
83 result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
84 if (result != MMSYSERR_NOERROR) {
85 WriteInError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps",
86 result);
87 name_tmp = (char*) driver_name;
88 } else {
89 name_tmp = capabilities.szPname;
90 }
91
92 snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, name_tmp,
93 index + 1);
94 snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1);
95 jack_event = 0;
96 started = false;
97 write_queue_ptr.release();
98 thread_queue_ptr.release();
99 return;
100
101 unprepare_header:
102 result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
103 if (result != MMSYSERR_NOERROR) {
104 WriteInError("JackWinMMEInputPort [constructor]",
105 "midiInUnprepareHeader", result);
106 }
107 close_handle:
108 result = midiInClose(handle);
109 if (result != MMSYSERR_NOERROR) {
110 WriteInError("JackWinMMEInputPort [constructor]", "midiInClose",
111 result);
112 }
113 delete_sysex_buffer:
114 delete[] sysex_buffer;
115 throw std::runtime_error(error_message);
116 }
117
118 JackWinMMEInputPort::~JackWinMMEInputPort()
119 {
120 MMRESULT result = midiInReset(handle);
121 if (result != MMSYSERR_NOERROR) {
122 WriteInError("JackWinMMEInputPort [destructor]", "midiInReset", result);
123 }
124 result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
125 if (result != MMSYSERR_NOERROR) {
126 WriteInError("JackWinMMEInputPort [destructor]",
127 "midiInUnprepareHeader", result);
128 }
129 result = midiInClose(handle);
130 if (result != MMSYSERR_NOERROR) {
131 WriteInError("JackWinMMEInputPort [destructor]", "midiInClose", result);
132 }
133 delete[] sysex_buffer;
134 delete thread_queue;
135 delete write_queue;
136 }
137
138 void
139 JackWinMMEInputPort::EnqueueMessage(DWORD timestamp, size_t length,
140 jack_midi_data_t *data)
141 {
142 jack_nframes_t frame =
143 GetFramesFromTime(start_time + (((jack_time_t) timestamp) * 1000));
144
145 // Debugging code
146 jack_time_t current_time = GetMicroSeconds();
147 jack_log("JackWinMMEInputPort::EnqueueMessage - enqueueing event at %f "
148 "(frame: %d) with start offset '%d' scheduled for frame '%d'",
149 ((double) current_time) / 1000.0, GetFramesFromTime(current_time),
150 timestamp, frame);
151 // End debugging code
152
153 switch (thread_queue->EnqueueEvent(frame, length, data)) {
154 case JackMidiWriteQueue::BUFFER_FULL:
155 jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
156 "cannot currently accept a %d-byte event. Dropping event.",
157 length);
158 break;
159 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
160 jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
161 "buffer is too small to enqueue a %d-byte event. Dropping "
162 "event.", length);
163 break;
164 default:
165 ;
166 }
167 }
168
169 void
170 JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text)
171 {
172 MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH);
173 if (result != MMSYSERR_NOERROR) {
174 snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error);
175 }
176 }
177
178 void
179 JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer,
180 jack_nframes_t frames)
181 {
182 write_queue->ResetMidiBuffer(port_buffer, frames);
183 if (! jack_event) {
184 jack_event = thread_queue->DequeueEvent();
185 }
186 for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
187 switch (write_queue->EnqueueEvent(jack_event, frames)) {
188 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
189 jack_error("JackWinMMEMidiInputPort::Process - The buffer write "
190 "queue couldn't enqueue a %d-byte event. Dropping "
191 "event.", jack_event->size);
192 // Fallthrough on purpose
193 case JackMidiWriteQueue::OK:
194 continue;
195 default:
196 break;
197 }
198 break;
199 }
200 }
201
202 void
203 JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2)
204 {
205 set_threaded_log_function();
206 switch (message) {
207 case MIM_CLOSE:
208 jack_log("JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
209 break;
210 case MIM_MOREDATA:
211 jack_log("JackWinMMEInputPort::ProcessWinMME - The MIDI input device "
212 "driver thinks that JACK is not processing messages fast "
213 "enough.");
214 // Fallthrough on purpose.
215 case MIM_DATA: {
216 jack_midi_data_t message_buffer[3];
217 jack_midi_data_t status = param1 & 0xff;
218 int length = GetMessageLength(status);
219
220 switch (length) {
221 case 3:
222 message_buffer[2] = (param1 >> 16) & 0xff;
223 // Fallthrough on purpose.
224 case 2:
225 message_buffer[1] = (param1 >> 8) & 0xff;
226 // Fallthrough on purpose.
227 case 1:
228 message_buffer[0] = status;
229 break;
230 case 0:
231 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
232 "input driver sent an MIM_DATA message with a sysex "
233 "status byte.");
234 return;
235 case -1:
236 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
237 "input driver sent an MIM_DATA message with an invalid "
238 "status byte.");
239 return;
240 }
241 EnqueueMessage(param2, (size_t) length, message_buffer);
242 break;
243 }
244 case MIM_LONGDATA: {
245 LPMIDIHDR header = (LPMIDIHDR) param1;
246 size_t byte_count = header->dwBytesRecorded;
247 if (! byte_count) {
248 jack_log("JackWinMMEInputPort::ProcessWinMME - WinMME driver has "
249 "returned sysex header to us with no bytes. The JACK "
250 "driver is probably being stopped.");
251 break;
252 }
253 jack_midi_data_t *data = (jack_midi_data_t *) header->lpData;
254 if ((data[0] != 0xf0) || (data[byte_count - 1] != 0xf7)) {
255 jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding "
256 "%d-byte sysex chunk.", byte_count);
257 } else {
258 EnqueueMessage(param2, byte_count, data);
259 }
260 // Is this realtime-safe? This function isn't run in the JACK thread,
261 // but we still want it to perform as quickly as possible. Even if
262 // this isn't realtime safe, it may not be avoidable.
263 MMRESULT result = midiInAddBuffer(handle, &sysex_header,
264 sizeof(MIDIHDR));
265 if (result != MMSYSERR_NOERROR) {
266 WriteInError("JackWinMMEInputPort::ProcessWinMME",
267 "midiInAddBuffer", result);
268 }
269 break;
270 }
271 case MIM_LONGERROR:
272 jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or "
273 "incomplete sysex message received.");
274 break;
275 case MIM_OPEN:
276 jack_log("JackWinMMEInputPort::ProcessWinMME - MIDI device opened.");
277 }
278 }
279
280 bool
281 JackWinMMEInputPort::Start()
282 {
283 if (! started) {
284 start_time = GetMicroSeconds();
285 MMRESULT result = midiInStart(handle);
286 started = result == MMSYSERR_NOERROR;
287 if (! started) {
288 WriteInError("JackWinMMEInputPort::Start", "midiInStart", result);
289 }
290 }
291 return started;
292 }
293
294 bool
295 JackWinMMEInputPort::Stop()
296 {
297 if (started) {
298 MMRESULT result = midiInStop(handle);
299 started = result != MMSYSERR_NOERROR;
300 if (started) {
301 WriteInError("JackWinMMEInputPort::Stop", "midiInStop", result);
302 }
303 }
304 return ! started;
305 }
306
307 void
308 JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func,
309 MMRESULT result)
310 {
311 char error_message[MAXERRORLENGTH];
312 GetInErrorString(result, error_message);
313 jack_error("%s - %s: %s", jack_func, mm_func, error_message);
314 }
Jack::JackMidiBufferWriteQueue::EnqueueEvent
EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
Definition: JackMidiBufferWriteQueue.cpp:32
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
Jack::JackWinMMEInputPort
Definition: JackWinMMEInputPort.h:31
jack_midi_event_t::size
size_t size
Definition: JackMidiPort.h:36
Jack::JackMidiBufferWriteQueue::ResetMidiBuffer
void ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames)
Definition: JackMidiBufferWriteQueue.cpp:50
Jack::JackMidiAsyncQueue::DequeueEvent
virtual jack_midi_event_t * DequeueEvent()
Definition: JackMidiAsyncQueue.cpp:53
jack_log
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108
Jack::JackMidiAsyncQueue::EnqueueEvent
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
Definition: JackMidiAsyncQueue.cpp:72