WinMME based midi input/output for portaudio backend
[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
26 #include "rt_thread.h"
27 #include "win_utils.h"
28 #include "midi_util.h"
29
30 #include "debug.h"
31
32 // remove dup with input_device
33 static const uint32_t MIDI_BUFFER_SIZE = 32768;
34 static const uint32_t MAX_MIDI_MSG_SIZE = 256; // fix this for sysex
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 = midiOutReset (m_handle);
121         if (result != MMSYSERR_NOERROR) {
122                 error_msg = get_error_string (result);
123                 DEBUG_MIDI (error_msg);
124                 success = false;
125         }
126         result = midiOutClose (m_handle);
127         if (result != MMSYSERR_NOERROR) {
128                 error_msg = get_error_string (result);
129                 DEBUG_MIDI (error_msg);
130                 success = false;
131         }
132
133         if (m_sysex_semaphore) {
134                 if (!CloseHandle (m_sysex_semaphore)) {
135                         DEBUG_MIDI ("WinMMEMidiOut Unable to close sysex semaphore\n");
136                         success = false;
137                 } else {
138                         m_sysex_semaphore = 0;
139                 }
140         }
141         if (m_queue_semaphore) {
142                 if (!CloseHandle (m_queue_semaphore)) {
143                         DEBUG_MIDI ("WinMMEMidiOut Unable to close queue semaphore\n");
144                         success = false;
145                 } else {
146                         m_queue_semaphore = 0;
147                 }
148         }
149
150         m_handle = 0;
151         return success;
152 }
153
154 bool
155 WinMMEMidiOutputDevice::set_device_name (UINT index)
156 {
157         MIDIOUTCAPS capabilities;
158         MMRESULT result =
159             midiOutGetDevCaps (index, &capabilities, sizeof(capabilities));
160
161         if (result != MMSYSERR_NOERROR) {
162                 DEBUG_MIDI (get_error_string (result));
163                 m_name = "Unknown Midi Output Device";
164                 return false;
165         } else {
166                 m_name = capabilities.szPname;
167         }
168         return true;
169 }
170
171 std::string
172 WinMMEMidiOutputDevice::get_error_string (MMRESULT error_code)
173 {
174         char error_msg[MAXERRORLENGTH];
175         MMRESULT result = midiOutGetErrorText (error_code, error_msg, MAXERRORLENGTH);
176         if (result != MMSYSERR_NOERROR) {
177                 return error_msg;
178         }
179         return "WinMMEMidiOutput: Unknown Error code";
180 }
181
182 bool
183 WinMMEMidiOutputDevice::start ()
184 {
185         if (m_thread_running) {
186                 DEBUG_MIDI (
187                     string_compose ("WinMMEMidiOutput: device %1 already started\n", m_name));
188                 return true;
189         }
190
191         m_timer = CreateWaitableTimer (NULL, FALSE, NULL);
192
193         if (!m_timer) {
194                 DEBUG_MIDI ("WinMMEMidiOutput: unable to create waitable timer\n");
195                 return false;
196         }
197
198         if (!start_midi_output_thread ()) {
199                 DEBUG_MIDI ("WinMMEMidiOutput: Failed to start MIDI output thread\n");
200
201                 if (!CloseHandle (m_timer)) {
202                         DEBUG_MIDI ("WinMMEMidiOutput: unable to close waitable timer\n");
203                 }
204                 return false;
205         }
206         return true;
207 }
208
209 bool
210 WinMMEMidiOutputDevice::stop ()
211 {
212         if (!m_thread_running) {
213                 DEBUG_MIDI ("WinMMEMidiOutputDevice: device already stopped\n");
214                 return true;
215         }
216
217         if (!stop_midi_output_thread ()) {
218                 DEBUG_MIDI ("WinMMEMidiOutput: Failed to start MIDI output thread\n");
219                 return false;
220         }
221
222         if (!CloseHandle (m_timer)) {
223                 DEBUG_MIDI ("WinMMEMidiOutput: unable to close waitable timer\n");
224                 return false;
225         }
226         m_timer = 0;
227         return true;
228 }
229
230 bool
231 WinMMEMidiOutputDevice::start_midi_output_thread ()
232 {
233         m_thread_quit = false;
234
235         //pthread_attr_t attr;
236         size_t stacksize = 100000;
237
238         // TODO Use native threads
239         if (_realtime_pthread_create (SCHED_FIFO, -21, stacksize,
240                                 &m_output_thread_handle, midi_output_thread, this)) {
241                 return false;
242         }
243
244         int timeout = 5000;
245         while (!m_thread_running && --timeout > 0) { Glib::usleep (1000); }
246         if (timeout == 0 || !m_thread_running) {
247                 DEBUG_MIDI (string_compose ("Unable to start midi output device thread: %1\n",
248                                             m_name));
249                 return false;
250         }
251         return true;
252 }
253
254 bool
255 WinMMEMidiOutputDevice::stop_midi_output_thread ()
256 {
257         int timeout = 5000;
258         m_thread_quit = true;
259
260         while (m_thread_running && --timeout > 0) { Glib::usleep (1000); }
261         if (timeout == 0 || m_thread_running) {
262                 DEBUG_MIDI (string_compose ("Unable to stop midi output device thread: %1\n",
263                                              m_name));
264                 return false;
265         }
266
267         void *status;
268         if (pthread_join (m_output_thread_handle, &status)) {
269                 DEBUG_MIDI (string_compose ("Unable to join midi output device thread: %1\n",
270                                             m_name));
271                 return false;
272         }
273         return true;
274 }
275
276 bool
277 WinMMEMidiOutputDevice::signal (HANDLE semaphore)
278 {
279         bool result = (bool)ReleaseSemaphore (semaphore, 1, NULL);
280         if (!result) {
281                 DEBUG_MIDI ("WinMMEMidiOutDevice: Cannot release semaphore\n");
282         }
283         return result;
284 }
285
286 bool
287 WinMMEMidiOutputDevice::wait (HANDLE semaphore)
288 {
289         DWORD result = WaitForSingleObject (semaphore, INFINITE);
290         switch (result) {
291         case WAIT_FAILED:
292                 DEBUG_MIDI ("WinMMEMidiOutDevice: WaitForSingleObject Failed\n");
293                 break;
294         case WAIT_OBJECT_0:
295                 return true;
296         default:
297                 DEBUG_MIDI ("WinMMEMidiOutDevice: Unexpected result from WaitForSingleObject\n");
298         }
299         return false;
300 }
301
302 void CALLBACK
303 WinMMEMidiOutputDevice::winmm_output_callback (HMIDIOUT handle,
304                                                UINT msg,
305                                                DWORD_PTR instance,
306                                                DWORD_PTR midi_data,
307                                                DWORD_PTR timestamp)
308 {
309         ((WinMMEMidiOutputDevice*)instance)
310             ->midi_output_callback (msg, midi_data, timestamp);
311 }
312
313 void
314 WinMMEMidiOutputDevice::midi_output_callback (UINT message,
315                                               DWORD_PTR midi_data,
316                                               DWORD_PTR timestamp)
317 {
318         switch (message) {
319         case MOM_CLOSE:
320                 DEBUG_MIDI ("WinMMEMidiOutput - MIDI device closed\n");
321                 break;
322         case MOM_DONE:
323                 signal (m_sysex_semaphore);
324                 break;
325         case MOM_OPEN:
326                 DEBUG_MIDI ("WinMMEMidiOutput - MIDI device opened\n");
327                 break;
328         case MOM_POSITIONCB:
329                 LPMIDIHDR header = (LPMIDIHDR)midi_data;
330                 DEBUG_MIDI (string_compose ("WinMMEMidiOut - %1 bytes out of %2 bytes of "
331                                             "the current sysex message have been sent.\n",
332                                             header->dwOffset,
333                                             header->dwBytesRecorded));
334         }
335 }
336
337 void*
338 WinMMEMidiOutputDevice::midi_output_thread (void *arg)
339 {
340         WinMMEMidiOutputDevice* output_device = reinterpret_cast<WinMMEMidiOutputDevice*> (arg);
341         output_device->midi_output_thread ();
342         return 0;
343 }
344
345 void
346 WinMMEMidiOutputDevice::midi_output_thread ()
347 {
348         m_thread_running = true;
349
350         while (!m_thread_quit) {
351                 if (!wait (m_queue_semaphore)) {
352                         break;
353                 }
354
355                 MidiEventHeader h (0, 0);
356                 uint8_t data[MAX_MIDI_MSG_SIZE];
357
358                 const uint32_t read_space = m_midi_buffer->read_space ();
359
360                 if (read_space > sizeof(MidiEventHeader)) {
361                         if (m_midi_buffer->read ((uint8_t*)&h, sizeof(MidiEventHeader)) !=
362                             sizeof(MidiEventHeader)) {
363                                 DEBUG_MIDI ("WinMMEMidiOut: Garbled MIDI EVENT HEADER!!\n");
364                                 break;
365                         }
366                         assert (read_space >= h.size);
367
368                         if (h.size > MAX_MIDI_MSG_SIZE) {
369                                 m_midi_buffer->increment_read_idx (h.size);
370                                 DEBUG_MIDI ("WinMMEMidiOut: MIDI event too large!\n");
371                                 continue;
372                         }
373                         if (m_midi_buffer->read (&data[0], h.size) != h.size) {
374                                 DEBUG_MIDI ("WinMMEMidiOut: Garbled MIDI EVENT DATA!!\n");
375                                 break;
376                         }
377                 } else {
378                         // error/assert?
379                         DEBUG_MIDI ("WinMMEMidiOut: MIDI buffer underrun, shouldn't occur\n");
380                         continue;
381                 }
382                 uint64_t current_time = utils::get_microseconds ();
383
384                 DEBUG_TIMING (string_compose (
385                     "WinMMEMidiOut: h.time = %1, current_time = %2\n", h.time, current_time));
386
387                 if (h.time > current_time) {
388
389                         DEBUG_TIMING (string_compose ("WinMMEMidiOut: waiting at %1 for %2 "
390                                                       "milliseconds before sending message\n",
391                                                       ((double)current_time) / 1000.0,
392                                                       ((double)(h.time - current_time)) / 1000.0));
393
394                         if (!wait_for_microseconds (h.time - current_time))
395                         {
396                                 DEBUG_MIDI ("WinMMEMidiOut: Error waiting for timer\n");
397                                 break;
398                         }
399
400                         uint64_t wakeup_time = utils::get_microseconds ();
401                         DEBUG_TIMING (string_compose ("WinMMEMidiOut: woke up at %1(ms)\n",
402                                                       ((double)wakeup_time) / 1000.0));
403                         if (wakeup_time > h.time) {
404                                 DEBUG_TIMING (string_compose ("WinMMEMidiOut: overslept by %1(ms)\n",
405                                                               ((double)(wakeup_time - h.time)) / 1000.0));
406                         } else if (wakeup_time < h.time) {
407                                 DEBUG_TIMING (string_compose ("WinMMEMidiOut: woke up %1(ms) too early\n",
408                                                               ((double)(h.time - wakeup_time)) / 1000.0));
409                         }
410
411                 } else if (h.time < current_time) {
412                         DEBUG_TIMING (string_compose (
413                             "WinMMEMidiOut: MIDI event at sent to driver %1(ms) late\n",
414                             ((double)(current_time - h.time)) / 1000.0));
415                 }
416
417                 DWORD message = 0;
418                 MMRESULT result;
419                 switch (h.size) {
420                 case 3:
421                         message |= (((DWORD)data[2]) << 16);
422                 // Fallthrough on purpose.
423                 case 2:
424                         message |= (((DWORD)data[1]) << 8);
425                 // Fallthrough on purpose.
426                 case 1:
427                         message |= (DWORD)data[0];
428                         result = midiOutShortMsg (m_handle, message);
429                         if (result != MMSYSERR_NOERROR) {
430                                 DEBUG_MIDI (
431                                     string_compose ("WinMMEMidiOutput: %1\n", get_error_string (result)));
432                         }
433                         continue;
434                 }
435
436 #if ENABLE_SYSEX
437                 MIDIHDR header;
438                 header.dwBufferLength = h.size;
439                 header.dwFlags = 0;
440                 header.lpData = (LPSTR)data;
441
442                 result = midiOutPrepareHeader (m_handle, &header, sizeof(MIDIHDR));
443                 if (result != MMSYSERR_NOERROR) {
444                         DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutPrepareHeader %1\n",
445                                                     get_error_string (result)));
446                         continue;
447                 }
448
449                 result = midiOutLongMsg (m_handle, &header, sizeof(MIDIHDR));
450                 if (result != MMSYSERR_NOERROR) {
451                         DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutLongMsg %1\n",
452                                                     get_error_string (result)));
453                         continue;
454                 }
455
456                 // Sysex messages may be sent synchronously or asynchronously.  The
457                 // choice is up to the WinMME driver.  So, we wait until the message is
458                 // sent, regardless of the driver's choice.
459                 if (!wait (m_sysex_semaphore)) {
460                         break;
461                 }
462
463                 result = midiOutUnprepareHeader (m_handle, &header, sizeof(MIDIHDR));
464                 if (result != MMSYSERR_NOERROR) {
465                         DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutUnprepareHeader %1\n",
466                                                     get_error_string (result)));
467                         break;
468                 }
469 #endif
470         }
471
472         m_thread_running = false;
473 }
474
475 bool
476 WinMMEMidiOutputDevice::wait_for_microseconds (int64_t wait_us)
477 {
478         LARGE_INTEGER due_time;
479
480         // 100 ns resolution
481         due_time.QuadPart = -((LONGLONG)(wait_us * 10));
482         if (!SetWaitableTimer (m_timer, &due_time, 0, NULL, NULL, 0)) {
483                 DEBUG_MIDI ("WinMMEMidiOut: Error waiting for timer\n");
484                 return false;
485         }
486
487         if (!wait (m_timer)) {
488                 return false;
489         }
490
491         return true;
492 }
493
494 } // namespace ARDOUR