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