Jack2  1.9.9
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 
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_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
209  break;
210  case MIM_MOREDATA:
211  jack_info("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_info("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_info("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 }
EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
SERVER_EXPORT void jack_info(const char *fmt,...)
Definition: JackError.cpp:99
void ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames)
virtual jack_midi_event_t * DequeueEvent()
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:107
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)