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