Jack2  1.9.9
JackWinMMEOutputPort.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 <memory>
21 #include <stdexcept>
22 
23 #include "JackMidiUtil.h"
24 #include "JackTime.h"
25 #include "JackWinMMEOutputPort.h"
26 #include "JackGlobals.h"
27 #include "JackEngineControl.h"
28 
30 
32 // Static callbacks
34 
35 void CALLBACK
36 JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle, UINT message,
37  DWORD_PTR port, DWORD_PTR param1,
38  DWORD_PTR param2)
39 {
40  ((JackWinMMEOutputPort *) port)->HandleMessage(message, param1, param2);
41 }
42 
44 // Class
46 
47 JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name,
48  const char *client_name,
49  const char *driver_name,
50  UINT index,
51  size_t max_bytes,
52  size_t max_messages)
53 {
54  read_queue = new JackMidiBufferReadQueue();
55  std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
56  thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
57  std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
58  thread = new JackThread(this);
59  std::auto_ptr<JackThread> thread_ptr(thread);
60  char error_message[MAXERRORLENGTH];
61  MMRESULT result = midiOutOpen(&handle, index, (DWORD_PTR)HandleMessageEvent,
62  (DWORD_PTR)this, CALLBACK_FUNCTION);
63  if (result != MMSYSERR_NOERROR) {
64  GetOutErrorString(result, error_message);
65  goto raise_exception;
66  }
67  thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL);
68  if (thread_queue_semaphore == NULL) {
69  GetOSErrorString(error_message);
70  goto close_handle;
71  }
72  sysex_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
73  if (sysex_semaphore == NULL) {
74  GetOSErrorString(error_message);
75  goto destroy_thread_queue_semaphore;
76  }
77  MIDIOUTCAPS capabilities;
78  char *name_tmp;
79  result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities));
80  if (result != MMSYSERR_NOERROR) {
81  WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps",
82  result);
83  name_tmp = (char*)driver_name;
84  } else {
85  name_tmp = capabilities.szPname;
86  }
87  snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, name_tmp,
88  index + 1);
89  snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1);
90  read_queue_ptr.release();
91  thread_queue_ptr.release();
92  thread_ptr.release();
93  return;
94 
95  destroy_thread_queue_semaphore:
96  if (! CloseHandle(thread_queue_semaphore)) {
97  WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle");
98  }
99  close_handle:
100  result = midiOutClose(handle);
101  if (result != MMSYSERR_NOERROR) {
102  WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutClose",
103  result);
104  }
105  raise_exception:
106  throw std::runtime_error(error_message);
107 }
108 
109 JackWinMMEOutputPort::~JackWinMMEOutputPort()
110 {
111  MMRESULT result = midiOutReset(handle);
112  if (result != MMSYSERR_NOERROR) {
113  WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutReset",
114  result);
115  }
116  result = midiOutClose(handle);
117  if (result != MMSYSERR_NOERROR) {
118  WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutClose",
119  result);
120  }
121  if (! CloseHandle(sysex_semaphore)) {
122  WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
123  }
124  if (! CloseHandle(thread_queue_semaphore)) {
125  WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
126  }
127  delete read_queue;
128  delete thread_queue;
129  delete thread;
130 }
131 
132 bool
133 JackWinMMEOutputPort::Execute()
134 {
135  for (;;) {
136  if (! Wait(thread_queue_semaphore)) {
137  jack_log("JackWinMMEOutputPort::Execute BREAK");
138 
139  break;
140  }
141  jack_midi_event_t *event = thread_queue->DequeueEvent();
142  if (! event) {
143  break;
144  }
145  jack_time_t frame_time = GetTimeFromFrames(event->time);
146  jack_time_t current_time = GetMicroSeconds();
147  if (frame_time > current_time) {
148  LARGE_INTEGER due_time;
149 
150  // 100 ns resolution
151  due_time.QuadPart =
152  -((LONGLONG) ((frame_time - current_time) * 10));
153  if (! SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0)) {
154  WriteOSError("JackWinMMEOutputPort::Execute",
155  "SetWaitableTimer");
156  break;
157  }
158 
159  // Debugging code
160  jack_log("JackWinMMEOutputPort::Execute - waiting at %f for %f "
161  "milliseconds before sending message (current frame: %d, "
162  "send frame: %d)",
163  ((double) current_time) / 1000.0,
164  ((double) (frame_time - current_time)) / 1000.0,
165  GetFramesFromTime(current_time), event->time);
166  // End debugging code
167 
168  if (! Wait(timer)) {
169  break;
170  }
171 
172  // Debugging code
173  jack_time_t wakeup_time = GetMicroSeconds();
174  jack_log("JackWinMMEOutputPort::Execute - woke up at %f.",
175  ((double) wakeup_time) / 1000.0);
176  if (wakeup_time > frame_time) {
177  jack_log("JackWinMMEOutputPort::Execute - overslept by %f "
178  "milliseconds.",
179  ((double) (wakeup_time - frame_time)) / 1000.0);
180  } else if (wakeup_time < frame_time) {
181  jack_log("JackWinMMEOutputPort::Execute - woke up %f "
182  "milliseconds too early.",
183  ((double) (frame_time - wakeup_time)) / 1000.0);
184  }
185  // End debugging code
186 
187  }
188  jack_midi_data_t *data = event->buffer;
189  DWORD message = 0;
190  MMRESULT result;
191  size_t size = event->size;
192  switch (size) {
193  case 3:
194  message |= (((DWORD) data[2]) << 16);
195  // Fallthrough on purpose.
196  case 2:
197  message |= (((DWORD) data[1]) << 8);
198  // Fallthrough on purpose.
199  case 1:
200  message |= (DWORD) data[0];
201  result = midiOutShortMsg(handle, message);
202  if (result != MMSYSERR_NOERROR) {
203  WriteOutError("JackWinMMEOutputPort::Execute",
204  "midiOutShortMsg", result);
205  }
206  continue;
207  }
208  MIDIHDR header;
209  header.dwBufferLength = size;
210  header.dwFlags = 0;
211  header.lpData = (LPSTR) data;
212  result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR));
213  if (result != MMSYSERR_NOERROR) {
214  WriteOutError("JackWinMMEOutputPort::Execute",
215  "midiOutPrepareHeader", result);
216  continue;
217  }
218  result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR));
219  if (result != MMSYSERR_NOERROR) {
220  WriteOutError("JackWinMMEOutputPort::Execute", "midiOutLongMsg",
221  result);
222  continue;
223  }
224 
225  // System exclusive messages may be sent synchronously or
226  // asynchronously. The choice is up to the WinMME driver. So, we wait
227  // until the message is sent, regardless of the driver's choice.
228  if (! Wait(sysex_semaphore)) {
229  break;
230  }
231 
232  result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR));
233  if (result != MMSYSERR_NOERROR) {
234  WriteOutError("JackWinMMEOutputPort::Execute",
235  "midiOutUnprepareHeader", result);
236  break;
237  }
238  }
239  return false;
240 }
241 
242 void
243 JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text)
244 {
245  MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH);
246  if (result != MMSYSERR_NOERROR) {
247  snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error);
248  }
249 }
250 
251 void
252 JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1,
253  DWORD_PTR param2)
254 {
255  set_threaded_log_function();
256  switch (message) {
257  case MOM_CLOSE:
258  jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed.");
259  break;
260  case MOM_DONE:
261  Signal(sysex_semaphore);
262  break;
263  case MOM_OPEN:
264  jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened.");
265  break;
266  case MOM_POSITIONCB:
267  LPMIDIHDR header = (LPMIDIHDR) param1;
268  jack_info("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d "
269  "bytes of the current sysex message have been sent.",
270  header->dwOffset, header->dwBytesRecorded);
271  }
272 }
273 
274 bool
276 {
277  set_threaded_log_function();
278  // XX: Can more be done? Ideally, this thread should have the JACK server
279  // thread priority + 1.
280  if (thread->AcquireSelfRealTime(GetEngineControl()->fServerPriority)) {
281  jack_error("JackWinMMEOutputPort::Init - could not acquire realtime "
282  "scheduling. Continuing anyway.");
283  }
284  return true;
285 }
286 
287 void
288 JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
289  jack_nframes_t frames)
290 {
291  read_queue->ResetMidiBuffer(port_buffer);
292  for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
293  event = read_queue->DequeueEvent()) {
294 
295  switch (thread_queue->EnqueueEvent(event, frames)) {
296  case JackMidiWriteQueue::BUFFER_FULL:
297  jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
298  "buffer is full. Dropping event.");
299  break;
300  case JackMidiWriteQueue::BUFFER_TOO_SMALL:
301  jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
302  "couldn't enqueue a %d-byte event. Dropping event.",
303  event->size);
304  break;
305  default:
306  Signal(thread_queue_semaphore);
307  }
308  }
309 }
310 
311 bool
312 JackWinMMEOutputPort::Signal(HANDLE semaphore)
313 {
314  bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL);
315  if (! result) {
316  WriteOSError("JackWinMMEOutputPort::Signal", "ReleaseSemaphore");
317  }
318  return result;
319 }
320 
321 bool
322 JackWinMMEOutputPort::Start()
323 {
324  if (thread->GetStatus() != JackThread::kIdle) {
325  return true;
326  }
327  timer = CreateWaitableTimer(NULL, FALSE, NULL);
328  if (! timer) {
329  WriteOSError("JackWinMMEOutputPort::Start", "CreateWaitableTimer");
330  return false;
331  }
332  if (! thread->StartSync()) {
333  return true;
334  }
335  jack_error("JackWinMMEOutputPort::Start - failed to start MIDI processing "
336  "thread.");
337  if (! CloseHandle(timer)) {
338  WriteOSError("JackWinMMEOutputPort::Start", "CloseHandle");
339  }
340  return false;
341 }
342 
343 bool
344 JackWinMMEOutputPort::Stop()
345 {
346  jack_info("JackWinMMEOutputPort::Stop - stopping MIDI output port "
347  "processing thread.");
348 
349  int result;
350  const char *verb;
351  switch (thread->GetStatus()) {
352  case JackThread::kIniting:
353  case JackThread::kStarting:
354  result = thread->Kill();
355  verb = "kill";
356  break;
357  case JackThread::kRunning:
358  Signal(thread_queue_semaphore);
359  result = thread->Stop();
360  verb = "stop";
361  break;
362  default:
363  return true;
364  }
365  if (result) {
366  jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing "
367  "thread.", verb);
368  }
369  if (! CloseHandle(timer)) {
370  WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle");
371  result = -1;
372  }
373  return ! result;
374 }
375 
376 bool
377 JackWinMMEOutputPort::Wait(HANDLE semaphore)
378 {
379  DWORD result = WaitForSingleObject(semaphore, INFINITE);
380  switch (result) {
381  case WAIT_FAILED:
382  WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject");
383  break;
384  case WAIT_OBJECT_0:
385  return true;
386  default:
387  jack_error("JackWinMMEOutputPort::Wait - unexpected result from "
388  "'WaitForSingleObject'.");
389  }
390  return false;
391 }
392 
393 void
394 JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func,
395  MMRESULT result)
396 {
397  char error_message[MAXERRORLENGTH];
398  GetOutErrorString(result, error_message);
399  jack_error("%s - %s: %s", jack_func, mm_func, error_message);
400 }
void ResetMidiBuffer(JackMidiBuffer *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
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)