mackie: more debug tracing
[ardour.git] / libs / backends / portaudio / winmmemidi_output_device.cc
1 /*
2  * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com>
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 #include "winmmemidi_output_device.h"
20
21 #include <glibmm.h>
22
23 #include "pbd/debug.h"
24 #include "pbd/compose.h"
25 #include "pbd/windows_timer_utils.h"
26 #include "pbd/windows_mmcss.h"
27
28 #include "rt_thread.h"
29 #include "midi_util.h"
30
31 #include "debug.h"
32
33 // remove dup with input_device
34 static const uint32_t MIDI_BUFFER_SIZE = 32768;
35 static const uint32_t MAX_MIDI_MSG_SIZE = 256; // fix this for sysex
36 static const uint32_t MAX_QUEUE_SIZE = 4096;
37
38 namespace ARDOUR {
39
40 WinMMEMidiOutputDevice::WinMMEMidiOutputDevice (int index)
41         : m_handle(0)
42         , m_queue_semaphore(0)
43         , m_sysex_semaphore(0)
44         , m_timer(0)
45         , m_started(false)
46         , m_enabled(false)
47         , m_thread_running(false)
48         , m_thread_quit(false)
49         , m_midi_buffer(new RingBuffer<uint8_t>(MIDI_BUFFER_SIZE))
50 {
51         DEBUG_MIDI (string_compose ("Creating midi output device index: %1\n", index));
52
53         std::string error_msg;
54
55         if (!open (index, error_msg)) {
56                 DEBUG_MIDI (error_msg);
57                 throw std::runtime_error (error_msg);
58         }
59
60         set_device_name (index);
61 }
62
63 WinMMEMidiOutputDevice::~WinMMEMidiOutputDevice ()
64 {
65         std::string error_msg;
66         if (!close (error_msg)) {
67                 DEBUG_MIDI (error_msg);
68         }
69 }
70
71 bool
72 WinMMEMidiOutputDevice::enqueue_midi_event (uint64_t timestamp,
73                                             const uint8_t* data,
74                                             size_t size)
75 {
76         const uint32_t total_bytes = sizeof(MidiEventHeader) + size;
77         if (m_midi_buffer->write_space () < total_bytes) {
78                 DEBUG_MIDI ("WinMMEMidiOutput: ring buffer overflow\n");
79                 return false;
80         }
81
82         MidiEventHeader h (timestamp, size);
83         m_midi_buffer->write ((uint8_t*)&h, sizeof(MidiEventHeader));
84         m_midi_buffer->write (data, size);
85
86         signal (m_queue_semaphore);
87         return true;
88 }
89
90 bool
91 WinMMEMidiOutputDevice::open (UINT index, std::string& error_msg)
92 {
93         MMRESULT result = midiOutOpen (&m_handle,
94                                        index,
95                                        (DWORD_PTR)winmm_output_callback,
96                                        (DWORD_PTR) this,
97                                        CALLBACK_FUNCTION);
98         if (result != MMSYSERR_NOERROR) {
99                 error_msg = get_error_string (result);
100                 return false;
101         }
102
103         m_queue_semaphore = CreateSemaphore (NULL, 0, MAX_QUEUE_SIZE, NULL);
104         if (m_queue_semaphore == NULL) {
105                 DEBUG_MIDI ("WinMMEMidiOutput: Unable to create queue semaphore\n");
106                 return false;
107         }
108         m_sysex_semaphore = CreateSemaphore (NULL, 0, 1, NULL);
109         if (m_sysex_semaphore == NULL) {
110                 DEBUG_MIDI ("WinMMEMidiOutput: Unable to create sysex semaphore\n");
111                 return false;
112         }
113         return true;
114 }
115
116 bool
117 WinMMEMidiOutputDevice::close (std::string& error_msg)
118 {
119         // return error message for first error encountered?
120         bool success = true;
121         MMRESULT result = midiOutClose (m_handle);
122         if (result != MMSYSERR_NOERROR) {
123                 error_msg = get_error_string (result);
124                 DEBUG_MIDI (error_msg);
125                 success = false;
126         }
127
128         if (m_sysex_semaphore) {
129                 if (!CloseHandle (m_sysex_semaphore)) {
130                         DEBUG_MIDI ("WinMMEMidiOut Unable to close sysex semaphore\n");
131                         success = false;
132                 } else {
133                         m_sysex_semaphore = 0;
134                 }
135         }
136         if (m_queue_semaphore) {
137                 if (!CloseHandle (m_queue_semaphore)) {
138                         DEBUG_MIDI ("WinMMEMidiOut Unable to close queue semaphore\n");
139                         success = false;
140                 } else {
141                         m_queue_semaphore = 0;
142                 }
143         }
144
145         m_handle = 0;
146         return success;
147 }
148
149 bool
150 WinMMEMidiOutputDevice::set_device_name (UINT index)
151 {
152         MIDIOUTCAPS capabilities;
153         MMRESULT result =
154             midiOutGetDevCaps (index, &capabilities, sizeof(capabilities));
155
156         if (result != MMSYSERR_NOERROR) {
157                 DEBUG_MIDI (get_error_string (result));
158                 m_name = "Unknown Midi Output Device";
159                 return false;
160         } else {
161                 m_name = capabilities.szPname;
162         }
163         return true;
164 }
165
166 std::string
167 WinMMEMidiOutputDevice::get_error_string (MMRESULT error_code)
168 {
169         char error_msg[MAXERRORLENGTH];
170         MMRESULT result = midiOutGetErrorText (error_code, error_msg, MAXERRORLENGTH);
171         if (result != MMSYSERR_NOERROR) {
172                 return error_msg;
173         }
174         return "WinMMEMidiOutput: Unknown Error code";
175 }
176
177 bool
178 WinMMEMidiOutputDevice::start ()
179 {
180         if (m_thread_running) {
181                 DEBUG_MIDI (
182                     string_compose ("WinMMEMidiOutput: device %1 already started\n", m_name));
183                 return true;
184         }
185
186         m_timer = CreateWaitableTimer (NULL, FALSE, NULL);
187
188         if (!m_timer) {
189                 DEBUG_MIDI ("WinMMEMidiOutput: unable to create waitable timer\n");
190                 return false;
191         }
192
193         if (!start_midi_output_thread ()) {
194                 DEBUG_MIDI ("WinMMEMidiOutput: Failed to start MIDI output thread\n");
195
196                 if (!CloseHandle (m_timer)) {
197                         DEBUG_MIDI ("WinMMEMidiOutput: unable to close waitable timer\n");
198                 }
199                 return false;
200         }
201         return true;
202 }
203
204 bool
205 WinMMEMidiOutputDevice::stop ()
206 {
207         if (!m_thread_running) {
208                 DEBUG_MIDI ("WinMMEMidiOutputDevice: device already stopped\n");
209                 return true;
210         }
211
212         if (!stop_midi_output_thread ()) {
213                 DEBUG_MIDI ("WinMMEMidiOutput: Failed to stop MIDI output thread\n");
214                 return false;
215         }
216
217         if (!CloseHandle (m_timer)) {
218                 DEBUG_MIDI ("WinMMEMidiOutput: unable to close waitable timer\n");
219                 return false;
220         }
221         m_timer = 0;
222         return true;
223 }
224
225 bool
226 WinMMEMidiOutputDevice::start_midi_output_thread ()
227 {
228         m_thread_quit = false;
229
230         //pthread_attr_t attr;
231         size_t stacksize = 100000;
232
233         // TODO Use native threads
234         if (_realtime_pthread_create (SCHED_FIFO, -21, stacksize,
235                                 &m_output_thread_handle, midi_output_thread, this)) {
236                 return false;
237         }
238
239         int timeout = 5000;
240         while (!m_thread_running && --timeout > 0) { Glib::usleep (1000); }
241         if (timeout == 0 || !m_thread_running) {
242                 DEBUG_MIDI (string_compose ("Unable to start midi output device thread: %1\n",
243                                             m_name));
244                 return false;
245         }
246         return true;
247 }
248
249 bool
250 WinMMEMidiOutputDevice::stop_midi_output_thread ()
251 {
252         int timeout = 5000;
253         m_thread_quit = true;
254         signal (m_queue_semaphore);
255
256         while (m_thread_running && --timeout > 0) { Glib::usleep (1000); }
257         if (timeout == 0 || m_thread_running) {
258                 DEBUG_MIDI (string_compose ("Unable to stop midi output device thread: %1\n",
259                                              m_name));
260                 return false;
261         }
262
263         void *status;
264         if (pthread_join (m_output_thread_handle, &status)) {
265                 DEBUG_MIDI (string_compose ("Unable to join midi output device thread: %1\n",
266                                             m_name));
267                 return false;
268         }
269         return true;
270 }
271
272 bool
273 WinMMEMidiOutputDevice::signal (HANDLE semaphore)
274 {
275         bool result = (bool)ReleaseSemaphore (semaphore, 1, NULL);
276         if (!result) {
277                 DEBUG_MIDI ("WinMMEMidiOutDevice: Cannot release semaphore\n");
278         }
279         return result;
280 }
281
282 bool
283 WinMMEMidiOutputDevice::wait (HANDLE semaphore)
284 {
285         DWORD result = WaitForSingleObject (semaphore, INFINITE);
286         switch (result) {
287         case WAIT_FAILED:
288                 DEBUG_MIDI ("WinMMEMidiOutDevice: WaitForSingleObject Failed\n");
289                 break;
290         case WAIT_OBJECT_0:
291                 return true;
292         default:
293                 DEBUG_MIDI ("WinMMEMidiOutDevice: Unexpected result from WaitForSingleObject\n");
294         }
295         return false;
296 }
297
298 void CALLBACK
299 WinMMEMidiOutputDevice::winmm_output_callback (HMIDIOUT handle,
300                                                UINT msg,
301                                                DWORD_PTR instance,
302                                                DWORD_PTR midi_data,
303                                                DWORD_PTR timestamp)
304 {
305         ((WinMMEMidiOutputDevice*)instance)
306             ->midi_output_callback (msg, midi_data, timestamp);
307 }
308
309 void
310 WinMMEMidiOutputDevice::midi_output_callback (UINT message,
311                                               DWORD_PTR midi_data,
312                                               DWORD_PTR timestamp)
313 {
314         switch (message) {
315         case MOM_CLOSE:
316                 DEBUG_MIDI ("WinMMEMidiOutput - MIDI device closed\n");
317                 break;
318         case MOM_DONE:
319                 signal (m_sysex_semaphore);
320                 break;
321         case MOM_OPEN:
322                 DEBUG_MIDI ("WinMMEMidiOutput - MIDI device opened\n");
323                 break;
324         case MOM_POSITIONCB:
325                 LPMIDIHDR header = (LPMIDIHDR)midi_data;
326                 DEBUG_MIDI (string_compose ("WinMMEMidiOut - %1 bytes out of %2 bytes of "
327                                             "the current sysex message have been sent.\n",
328                                             header->dwOffset,
329                                             header->dwBytesRecorded));
330         }
331 }
332
333 void*
334 WinMMEMidiOutputDevice::midi_output_thread (void *arg)
335 {
336         WinMMEMidiOutputDevice* output_device = reinterpret_cast<WinMMEMidiOutputDevice*> (arg);
337         output_device->midi_output_thread ();
338         return 0;
339 }
340
341 void
342 WinMMEMidiOutputDevice::midi_output_thread ()
343 {
344         m_thread_running = true;
345
346         DEBUG_MIDI ("WinMMEMidiOut: MIDI output thread started\n");
347
348 #ifdef USE_MMCSS_THREAD_PRIORITIES
349         HANDLE task_handle;
350
351         PBD::MMCSS::set_thread_characteristics ("Pro Audio", &task_handle);
352         PBD::MMCSS::set_thread_priority (task_handle, PBD::MMCSS::AVRT_PRIORITY_HIGH);
353 #endif
354
355         while (!m_thread_quit) {
356                 if (!wait (m_queue_semaphore)) {
357                         DEBUG_MIDI ("WinMMEMidiOut: output thread waiting for semaphore failed\n");
358                         break;
359                 }
360
361                 DEBUG_MIDI ("WinMMEMidiOut: output thread woken by semaphore\n");
362
363                 MidiEventHeader h (0, 0);
364                 uint8_t data[MAX_MIDI_MSG_SIZE];
365
366                 const uint32_t read_space = m_midi_buffer->read_space ();
367
368                 DEBUG_MIDI (string_compose ("WinMMEMidiOut: total readable MIDI data %1\n", read_space));
369
370                 if (read_space > sizeof(MidiEventHeader)) {
371                         if (m_midi_buffer->read ((uint8_t*)&h, sizeof(MidiEventHeader)) !=
372                             sizeof(MidiEventHeader)) {
373                                 DEBUG_MIDI ("WinMMEMidiOut: Garbled MIDI EVENT HEADER!!\n");
374                                 break;
375                         }
376                         assert (read_space >= h.size);
377
378                         if (h.size > MAX_MIDI_MSG_SIZE) {
379                                 m_midi_buffer->increment_read_idx (h.size);
380                                 DEBUG_MIDI ("WinMMEMidiOut: MIDI event too large!\n");
381                                 continue;
382                         }
383                         if (m_midi_buffer->read (&data[0], h.size) != h.size) {
384                                 DEBUG_MIDI ("WinMMEMidiOut: Garbled MIDI EVENT DATA!!\n");
385                                 break;
386                         }
387                 } else {
388                         // error/assert?
389                         DEBUG_MIDI ("WinMMEMidiOut: MIDI buffer underrun, shouldn't occur\n");
390                         continue;
391                 }
392                 uint64_t current_time = PBD::get_microseconds ();
393
394                 DEBUG_TIMING (string_compose (
395                     "WinMMEMidiOut: h.time = %1, current_time = %2\n", h.time, current_time));
396
397                 if (h.time > current_time) {
398
399                         DEBUG_TIMING (string_compose ("WinMMEMidiOut: waiting at %1 for %2 "
400                                                       "milliseconds before sending message\n",
401                                                       ((double)current_time) / 1000.0,
402                                                       ((double)(h.time - current_time)) / 1000.0));
403
404                         if (!wait_for_microseconds (h.time - current_time))
405                         {
406                                 DEBUG_MIDI ("WinMMEMidiOut: Error waiting for timer\n");
407                                 break;
408                         }
409
410                         uint64_t wakeup_time = PBD::get_microseconds ();
411                         DEBUG_TIMING (string_compose ("WinMMEMidiOut: woke up at %1(ms)\n",
412                                                       ((double)wakeup_time) / 1000.0));
413                         if (wakeup_time > h.time) {
414                                 DEBUG_TIMING (string_compose ("WinMMEMidiOut: overslept by %1(ms)\n",
415                                                               ((double)(wakeup_time - h.time)) / 1000.0));
416                         } else if (wakeup_time < h.time) {
417                                 DEBUG_TIMING (string_compose ("WinMMEMidiOut: woke up %1(ms) too early\n",
418                                                               ((double)(h.time - wakeup_time)) / 1000.0));
419                         }
420
421                 } else if (h.time < current_time) {
422                         DEBUG_TIMING (string_compose (
423                             "WinMMEMidiOut: MIDI event at sent to driver %1(ms) late\n",
424                             ((double)(current_time - h.time)) / 1000.0));
425                 }
426
427                 DEBUG_MIDI (string_compose ("WinMMEMidiOut: MIDI event size: %1 time %2 now %3\n", h.size, h.time, current_time));
428
429                 DWORD message = 0;
430                 MMRESULT result;
431                 switch (h.size) {
432                 case 3:
433                         message |= (((DWORD)data[2]) << 16);
434                 // Fallthrough on purpose.
435                 case 2:
436                         message |= (((DWORD)data[1]) << 8);
437                 // Fallthrough on purpose.
438                 case 1:
439                         message |= (DWORD)data[0];
440                         result = midiOutShortMsg (m_handle, message);
441                         if (result != MMSYSERR_NOERROR) {
442                                 DEBUG_MIDI (
443                                     string_compose ("WinMMEMidiOutput: %1\n", get_error_string (result)));
444                         }
445                         continue;
446                 }
447
448                 MIDIHDR header;
449                 header.dwBufferLength = h.size;
450                 header.dwFlags = 0;
451                 header.lpData = (LPSTR)data;
452
453                 result = midiOutPrepareHeader (m_handle, &header, sizeof(MIDIHDR));
454                 if (result != MMSYSERR_NOERROR) {
455                         DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutPrepareHeader %1\n",
456                                                     get_error_string (result)));
457                         continue;
458                 }
459
460                 result = midiOutLongMsg (m_handle, &header, sizeof(MIDIHDR));
461                 if (result != MMSYSERR_NOERROR) {
462                         DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutLongMsg %1\n",
463                                                     get_error_string (result)));
464                         continue;
465                 }
466
467                 // Sysex messages may be sent synchronously or asynchronously.  The
468                 // choice is up to the WinMME driver.  So, we wait until the message is
469                 // sent, regardless of the driver's choice.
470
471                 DEBUG_MIDI ("WinMMEMidiOut: wait for sysex semaphore\n");
472
473                 if (!wait (m_sysex_semaphore)) {
474                         DEBUG_MIDI ("WinMMEMidiOut: wait for sysex semaphore - failed!\n");
475                         break;
476                 }
477
478                 result = midiOutUnprepareHeader (m_handle, &header, sizeof(MIDIHDR));
479                 if (result != MMSYSERR_NOERROR) {
480                         DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutUnprepareHeader %1\n",
481                                                     get_error_string (result)));
482                         break;
483                 }
484         }
485
486 #ifdef USE_MMCSS_THREAD_PRIORITIES
487         PBD::MMCSS::revert_thread_characteristics (task_handle);
488 #endif
489
490         m_thread_running = false;
491 }
492
493 bool
494 WinMMEMidiOutputDevice::wait_for_microseconds (int64_t wait_us)
495 {
496         LARGE_INTEGER due_time;
497
498         // 100 ns resolution
499         due_time.QuadPart = -((LONGLONG)(wait_us * 10));
500         if (!SetWaitableTimer (m_timer, &due_time, 0, NULL, NULL, 0)) {
501                 DEBUG_MIDI ("WinMMEMidiOut: Error waiting for timer\n");
502                 return false;
503         }
504
505         if (!wait (m_timer)) {
506                 return false;
507         }
508
509         return true;
510 }
511
512 } // namespace ARDOUR