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>
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.
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.
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.
21 #include "winmmemidi_output_device.h"
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"
31 #include "midi_util.h"
35 // remove dup with input_device
36 static const uint32_t MIDI_BUFFER_SIZE = 32768;
37 static const uint32_t MAX_QUEUE_SIZE = 4096;
41 WinMMEMidiOutputDevice::WinMMEMidiOutputDevice (int index)
43 , m_queue_semaphore(0)
44 , m_sysex_semaphore(0)
48 , m_thread_running(false)
49 , m_thread_quit(false)
50 , m_midi_buffer(new PBD::RingBuffer<uint8_t>(MIDI_BUFFER_SIZE))
52 DEBUG_MIDI (string_compose ("Creating midi output device index: %1\n", index));
54 std::string error_msg;
56 if (!open (index, error_msg)) {
57 DEBUG_MIDI (error_msg);
58 throw std::runtime_error (error_msg);
61 set_device_name (index);
64 WinMMEMidiOutputDevice::~WinMMEMidiOutputDevice ()
66 std::string error_msg;
67 if (!close (error_msg)) {
68 DEBUG_MIDI (error_msg);
73 WinMMEMidiOutputDevice::enqueue_midi_event (uint64_t timestamp,
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");
83 MidiEventHeader h (timestamp, size);
84 m_midi_buffer->write ((uint8_t*)&h, sizeof(MidiEventHeader));
85 m_midi_buffer->write (data, size);
87 signal (m_queue_semaphore);
92 WinMMEMidiOutputDevice::open (UINT index, std::string& error_msg)
94 MMRESULT result = midiOutOpen (&m_handle,
96 (DWORD_PTR)winmm_output_callback,
99 if (result != MMSYSERR_NOERROR) {
100 error_msg = get_error_string (result);
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");
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");
118 WinMMEMidiOutputDevice::close (std::string& error_msg)
120 // return error message for first error encountered?
122 MMRESULT result = midiOutClose (m_handle);
123 if (result != MMSYSERR_NOERROR) {
124 error_msg = get_error_string (result);
125 DEBUG_MIDI (error_msg);
129 if (m_sysex_semaphore) {
130 if (!CloseHandle (m_sysex_semaphore)) {
131 DEBUG_MIDI ("WinMMEMidiOut Unable to close sysex semaphore\n");
134 m_sysex_semaphore = 0;
137 if (m_queue_semaphore) {
138 if (!CloseHandle (m_queue_semaphore)) {
139 DEBUG_MIDI ("WinMMEMidiOut Unable to close queue semaphore\n");
142 m_queue_semaphore = 0;
151 WinMMEMidiOutputDevice::set_device_name (UINT index)
153 MIDIOUTCAPS capabilities;
155 midiOutGetDevCaps (index, &capabilities, sizeof(capabilities));
157 if (result != MMSYSERR_NOERROR) {
158 DEBUG_MIDI (get_error_string (result));
159 m_name = "Unknown Midi Output Device";
162 m_name = capabilities.szPname;
168 WinMMEMidiOutputDevice::get_error_string (MMRESULT error_code)
170 char error_msg[MAXERRORLENGTH];
171 MMRESULT result = midiOutGetErrorText (error_code, error_msg, MAXERRORLENGTH);
172 if (result != MMSYSERR_NOERROR) {
175 return "WinMMEMidiOutput: Unknown Error code";
179 WinMMEMidiOutputDevice::start ()
181 if (m_thread_running) {
183 string_compose ("WinMMEMidiOutput: device %1 already started\n", m_name));
187 m_timer = CreateWaitableTimer (NULL, FALSE, NULL);
190 DEBUG_MIDI ("WinMMEMidiOutput: unable to create waitable timer\n");
194 if (!start_midi_output_thread ()) {
195 DEBUG_MIDI ("WinMMEMidiOutput: Failed to start MIDI output thread\n");
197 if (!CloseHandle (m_timer)) {
198 DEBUG_MIDI ("WinMMEMidiOutput: unable to close waitable timer\n");
206 WinMMEMidiOutputDevice::stop ()
208 if (!m_thread_running) {
209 DEBUG_MIDI ("WinMMEMidiOutputDevice: device already stopped\n");
213 if (!stop_midi_output_thread ()) {
214 DEBUG_MIDI ("WinMMEMidiOutput: Failed to stop MIDI output thread\n");
218 if (!CloseHandle (m_timer)) {
219 DEBUG_MIDI ("WinMMEMidiOutput: unable to close waitable timer\n");
227 WinMMEMidiOutputDevice::start_midi_output_thread ()
229 m_thread_quit = false;
231 //pthread_attr_t attr;
232 size_t stacksize = 100000;
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)) {
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",
251 WinMMEMidiOutputDevice::stop_midi_output_thread ()
254 m_thread_quit = true;
255 signal (m_queue_semaphore);
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",
265 if (pthread_join (m_output_thread_handle, &status)) {
266 DEBUG_MIDI (string_compose ("Unable to join midi output device thread: %1\n",
274 WinMMEMidiOutputDevice::signal (HANDLE semaphore)
276 bool result = (bool)ReleaseSemaphore (semaphore, 1, NULL);
278 DEBUG_MIDI ("WinMMEMidiOutDevice: Cannot release semaphore\n");
284 WinMMEMidiOutputDevice::wait (HANDLE semaphore)
286 DWORD result = WaitForSingleObject (semaphore, INFINITE);
289 DEBUG_MIDI ("WinMMEMidiOutDevice: WaitForSingleObject Failed\n");
294 DEBUG_MIDI ("WinMMEMidiOutDevice: Unexpected result from WaitForSingleObject\n");
300 WinMMEMidiOutputDevice::winmm_output_callback (HMIDIOUT handle,
306 ((WinMMEMidiOutputDevice*)instance)
307 ->midi_output_callback (msg, midi_data, timestamp);
311 WinMMEMidiOutputDevice::midi_output_callback (UINT message,
317 DEBUG_MIDI ("WinMMEMidiOutput - MIDI device closed\n");
320 signal (m_sysex_semaphore);
323 DEBUG_MIDI ("WinMMEMidiOutput - MIDI device opened\n");
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",
330 header->dwBytesRecorded));
335 WinMMEMidiOutputDevice::midi_output_thread (void *arg)
337 WinMMEMidiOutputDevice* output_device = reinterpret_cast<WinMMEMidiOutputDevice*> (arg);
338 output_device->midi_output_thread ();
343 WinMMEMidiOutputDevice::midi_output_thread ()
345 m_thread_running = true;
347 DEBUG_MIDI ("WinMMEMidiOut: MIDI output thread started\n");
349 #ifdef USE_MMCSS_THREAD_PRIORITIES
352 PBD::MMCSS::set_thread_characteristics ("Pro Audio", &task_handle);
353 PBD::MMCSS::set_thread_priority (task_handle, PBD::MMCSS::AVRT_PRIORITY_HIGH);
356 while (!m_thread_quit) {
357 if (!wait (m_queue_semaphore)) {
358 DEBUG_MIDI ("WinMMEMidiOut: output thread waiting for semaphore failed\n");
362 DEBUG_MIDI ("WinMMEMidiOut: output thread woken by semaphore\n");
364 MidiEventHeader h (0, 0);
365 uint8_t data[MaxWinMidiEventSize];
367 const uint32_t read_space = m_midi_buffer->read_space ();
369 DEBUG_MIDI (string_compose ("WinMMEMidiOut: total readable MIDI data %1\n", read_space));
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");
377 assert (read_space >= h.size);
379 if (h.size > MaxWinMidiEventSize) {
380 m_midi_buffer->increment_read_idx (h.size);
381 DEBUG_MIDI ("WinMMEMidiOut: MIDI event too large!\n");
384 if (m_midi_buffer->read (&data[0], h.size) != h.size) {
385 DEBUG_MIDI ("WinMMEMidiOut: Garbled MIDI EVENT DATA!!\n");
390 DEBUG_MIDI ("WinMMEMidiOut: MIDI buffer underrun, shouldn't occur\n");
393 uint64_t current_time = PBD::get_microseconds ();
395 DEBUG_TIMING (string_compose (
396 "WinMMEMidiOut: h.time = %1, current_time = %2\n", h.time, current_time));
398 if (h.time > current_time) {
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));
405 if (!wait_for_microseconds (h.time - current_time))
407 DEBUG_MIDI ("WinMMEMidiOut: Error waiting for timer\n");
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));
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));
428 DEBUG_MIDI (string_compose ("WinMMEMidiOut: MIDI event size: %1 time %2 now %3\n", h.size, h.time, current_time));
434 message |= (((DWORD)data[2]) << 16);
437 message |= (((DWORD)data[1]) << 8);
440 message |= (DWORD)data[0];
441 result = midiOutShortMsg (m_handle, message);
442 if (result != MMSYSERR_NOERROR) {
444 string_compose ("WinMMEMidiOutput: %1\n", get_error_string (result)));
450 header.dwBufferLength = h.size;
452 header.lpData = (LPSTR)data;
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)));
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)));
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.
472 DEBUG_MIDI ("WinMMEMidiOut: wait for sysex semaphore\n");
474 if (!wait (m_sysex_semaphore)) {
475 DEBUG_MIDI ("WinMMEMidiOut: wait for sysex semaphore - failed!\n");
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)));
487 #ifdef USE_MMCSS_THREAD_PRIORITIES
488 PBD::MMCSS::revert_thread_characteristics (task_handle);
491 m_thread_running = false;
495 WinMMEMidiOutputDevice::wait_for_microseconds (int64_t wait_us)
497 LARGE_INTEGER due_time;
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");
506 if (!wait (m_timer)) {
513 } // namespace ARDOUR