Abstract definition of rt-scheduler policy
[ardour.git] / libs / backends / portaudio / winmmemidi_output_device.cc
index 6b31488b00a94f791286adb5c3d785111b762415..72b1494ac5468068072fd3d511299a3583689836 100644 (file)
 
 #include "pbd/debug.h"
 #include "pbd/compose.h"
+#include "pbd/pthread_utils.h"
+#include "pbd/windows_timer_utils.h"
+#include "pbd/windows_mmcss.h"
 
-#include "rt_thread.h"
-#include "win_utils.h"
 #include "midi_util.h"
 
 #include "debug.h"
 
 // remove dup with input_device
 static const uint32_t MIDI_BUFFER_SIZE = 32768;
-static const uint32_t MAX_MIDI_MSG_SIZE = 256; // fix this for sysex
 static const uint32_t MAX_QUEUE_SIZE = 4096;
 
 namespace ARDOUR {
@@ -69,8 +69,8 @@ WinMMEMidiOutputDevice::~WinMMEMidiOutputDevice ()
 
 bool
 WinMMEMidiOutputDevice::enqueue_midi_event (uint64_t timestamp,
-                                            const uint8_t* data,
-                                            size_t size)
+                                           const uint8_t* data,
+                                           size_t size)
 {
        const uint32_t total_bytes = sizeof(MidiEventHeader) + size;
        if (m_midi_buffer->write_space () < total_bytes) {
@@ -90,10 +90,10 @@ bool
 WinMMEMidiOutputDevice::open (UINT index, std::string& error_msg)
 {
        MMRESULT result = midiOutOpen (&m_handle,
-                                      index,
-                                      (DWORD_PTR)winmm_output_callback,
-                                      (DWORD_PTR) this,
-                                      CALLBACK_FUNCTION);
+                                      index,
+                                      (DWORD_PTR)winmm_output_callback,
+                                      (DWORD_PTR) this,
+                                      CALLBACK_FUNCTION);
        if (result != MMSYSERR_NOERROR) {
                error_msg = get_error_string (result);
                return false;
@@ -117,13 +117,7 @@ WinMMEMidiOutputDevice::close (std::string& error_msg)
 {
        // return error message for first error encountered?
        bool success = true;
-       MMRESULT result = midiOutReset (m_handle);
-       if (result != MMSYSERR_NOERROR) {
-               error_msg = get_error_string (result);
-               DEBUG_MIDI (error_msg);
-               success = false;
-       }
-       result = midiOutClose (m_handle);
+       MMRESULT result = midiOutClose (m_handle);
        if (result != MMSYSERR_NOERROR) {
                error_msg = get_error_string (result);
                DEBUG_MIDI (error_msg);
@@ -215,7 +209,7 @@ WinMMEMidiOutputDevice::stop ()
        }
 
        if (!stop_midi_output_thread ()) {
-               DEBUG_MIDI ("WinMMEMidiOutput: Failed to start MIDI output thread\n");
+               DEBUG_MIDI ("WinMMEMidiOutput: Failed to stop MIDI output thread\n");
                return false;
        }
 
@@ -236,7 +230,7 @@ WinMMEMidiOutputDevice::start_midi_output_thread ()
        size_t stacksize = 100000;
 
        // TODO Use native threads
-       if (_realtime_pthread_create (SCHED_FIFO, -21, stacksize,
+       if (pbd_realtime_pthread_create (PBD_SCHED_FIFO, -21, stacksize,
                                &m_output_thread_handle, midi_output_thread, this)) {
                return false;
        }
@@ -245,7 +239,7 @@ WinMMEMidiOutputDevice::start_midi_output_thread ()
        while (!m_thread_running && --timeout > 0) { Glib::usleep (1000); }
        if (timeout == 0 || !m_thread_running) {
                DEBUG_MIDI (string_compose ("Unable to start midi output device thread: %1\n",
-                                           m_name));
+                                           m_name));
                return false;
        }
        return true;
@@ -256,18 +250,19 @@ WinMMEMidiOutputDevice::stop_midi_output_thread ()
 {
        int timeout = 5000;
        m_thread_quit = true;
+       signal (m_queue_semaphore);
 
        while (m_thread_running && --timeout > 0) { Glib::usleep (1000); }
        if (timeout == 0 || m_thread_running) {
                DEBUG_MIDI (string_compose ("Unable to stop midi output device thread: %1\n",
-                                            m_name));
+                                            m_name));
                return false;
        }
 
        void *status;
        if (pthread_join (m_output_thread_handle, &status)) {
                DEBUG_MIDI (string_compose ("Unable to join midi output device thread: %1\n",
-                                           m_name));
+                                           m_name));
                return false;
        }
        return true;
@@ -301,10 +296,10 @@ WinMMEMidiOutputDevice::wait (HANDLE semaphore)
 
 void CALLBACK
 WinMMEMidiOutputDevice::winmm_output_callback (HMIDIOUT handle,
-                                               UINT msg,
-                                               DWORD_PTR instance,
-                                               DWORD_PTR midi_data,
-                                               DWORD_PTR timestamp)
+                                              UINT msg,
+                                              DWORD_PTR instance,
+                                              DWORD_PTR midi_data,
+                                              DWORD_PTR timestamp)
 {
        ((WinMMEMidiOutputDevice*)instance)
            ->midi_output_callback (msg, midi_data, timestamp);
@@ -312,8 +307,8 @@ WinMMEMidiOutputDevice::winmm_output_callback (HMIDIOUT handle,
 
 void
 WinMMEMidiOutputDevice::midi_output_callback (UINT message,
-                                              DWORD_PTR midi_data,
-                                              DWORD_PTR timestamp)
+                                             DWORD_PTR midi_data,
+                                             DWORD_PTR timestamp)
 {
        switch (message) {
        case MOM_CLOSE:
@@ -328,9 +323,9 @@ WinMMEMidiOutputDevice::midi_output_callback (UINT message,
        case MOM_POSITIONCB:
                LPMIDIHDR header = (LPMIDIHDR)midi_data;
                DEBUG_MIDI (string_compose ("WinMMEMidiOut - %1 bytes out of %2 bytes of "
-                                           "the current sysex message have been sent.\n",
-                                           header->dwOffset,
-                                           header->dwBytesRecorded));
+                                           "the current sysex message have been sent.\n",
+                                           header->dwOffset,
+                                           header->dwBytesRecorded));
        }
 }
 
@@ -347,16 +342,30 @@ WinMMEMidiOutputDevice::midi_output_thread ()
 {
        m_thread_running = true;
 
+       DEBUG_MIDI ("WinMMEMidiOut: MIDI output thread started\n");
+
+#ifdef USE_MMCSS_THREAD_PRIORITIES
+       HANDLE task_handle;
+
+       PBD::MMCSS::set_thread_characteristics ("Pro Audio", &task_handle);
+       PBD::MMCSS::set_thread_priority (task_handle, PBD::MMCSS::AVRT_PRIORITY_HIGH);
+#endif
+
        while (!m_thread_quit) {
                if (!wait (m_queue_semaphore)) {
+                       DEBUG_MIDI ("WinMMEMidiOut: output thread waiting for semaphore failed\n");
                        break;
                }
 
+               DEBUG_MIDI ("WinMMEMidiOut: output thread woken by semaphore\n");
+
                MidiEventHeader h (0, 0);
-               uint8_t data[MAX_MIDI_MSG_SIZE];
+               uint8_t data[MaxWinMidiEventSize];
 
                const uint32_t read_space = m_midi_buffer->read_space ();
 
+               DEBUG_MIDI (string_compose ("WinMMEMidiOut: total readable MIDI data %1\n", read_space));
+
                if (read_space > sizeof(MidiEventHeader)) {
                        if (m_midi_buffer->read ((uint8_t*)&h, sizeof(MidiEventHeader)) !=
                            sizeof(MidiEventHeader)) {
@@ -365,7 +374,7 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                        }
                        assert (read_space >= h.size);
 
-                       if (h.size > MAX_MIDI_MSG_SIZE) {
+                       if (h.size > MaxWinMidiEventSize) {
                                m_midi_buffer->increment_read_idx (h.size);
                                DEBUG_MIDI ("WinMMEMidiOut: MIDI event too large!\n");
                                continue;
@@ -379,7 +388,7 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                        DEBUG_MIDI ("WinMMEMidiOut: MIDI buffer underrun, shouldn't occur\n");
                        continue;
                }
-               uint64_t current_time = utils::get_microseconds ();
+               uint64_t current_time = PBD::get_microseconds ();
 
                DEBUG_TIMING (string_compose (
                    "WinMMEMidiOut: h.time = %1, current_time = %2\n", h.time, current_time));
@@ -387,9 +396,9 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                if (h.time > current_time) {
 
                        DEBUG_TIMING (string_compose ("WinMMEMidiOut: waiting at %1 for %2 "
-                                                     "milliseconds before sending message\n",
-                                                     ((double)current_time) / 1000.0,
-                                                     ((double)(h.time - current_time)) / 1000.0));
+                                                     "milliseconds before sending message\n",
+                                                     ((double)current_time) / 1000.0,
+                                                     ((double)(h.time - current_time)) / 1000.0));
 
                        if (!wait_for_microseconds (h.time - current_time))
                        {
@@ -397,15 +406,15 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                                break;
                        }
 
-                       uint64_t wakeup_time = utils::get_microseconds ();
+                       uint64_t wakeup_time = PBD::get_microseconds ();
                        DEBUG_TIMING (string_compose ("WinMMEMidiOut: woke up at %1(ms)\n",
-                                                     ((double)wakeup_time) / 1000.0));
+                                                     ((double)wakeup_time) / 1000.0));
                        if (wakeup_time > h.time) {
                                DEBUG_TIMING (string_compose ("WinMMEMidiOut: overslept by %1(ms)\n",
-                                                             ((double)(wakeup_time - h.time)) / 1000.0));
+                                                             ((double)(wakeup_time - h.time)) / 1000.0));
                        } else if (wakeup_time < h.time) {
                                DEBUG_TIMING (string_compose ("WinMMEMidiOut: woke up %1(ms) too early\n",
-                                                             ((double)(h.time - wakeup_time)) / 1000.0));
+                                                             ((double)(h.time - wakeup_time)) / 1000.0));
                        }
 
                } else if (h.time < current_time) {
@@ -414,6 +423,8 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                            ((double)(current_time - h.time)) / 1000.0));
                }
 
+               DEBUG_MIDI (string_compose ("WinMMEMidiOut: MIDI event size: %1 time %2 now %3\n", h.size, h.time, current_time));
+
                DWORD message = 0;
                MMRESULT result;
                switch (h.size) {
@@ -433,7 +444,6 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                        continue;
                }
 
-#if ENABLE_SYSEX
                MIDIHDR header;
                header.dwBufferLength = h.size;
                header.dwFlags = 0;
@@ -442,33 +452,40 @@ WinMMEMidiOutputDevice::midi_output_thread ()
                result = midiOutPrepareHeader (m_handle, &header, sizeof(MIDIHDR));
                if (result != MMSYSERR_NOERROR) {
                        DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutPrepareHeader %1\n",
-                                                   get_error_string (result)));
+                                                   get_error_string (result)));
                        continue;
                }
 
                result = midiOutLongMsg (m_handle, &header, sizeof(MIDIHDR));
                if (result != MMSYSERR_NOERROR) {
                        DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutLongMsg %1\n",
-                                                   get_error_string (result)));
+                                                   get_error_string (result)));
                        continue;
                }
 
                // Sysex messages may be sent synchronously or asynchronously.  The
                // choice is up to the WinMME driver.  So, we wait until the message is
                // sent, regardless of the driver's choice.
+
+               DEBUG_MIDI ("WinMMEMidiOut: wait for sysex semaphore\n");
+
                if (!wait (m_sysex_semaphore)) {
+                       DEBUG_MIDI ("WinMMEMidiOut: wait for sysex semaphore - failed!\n");
                        break;
                }
 
                result = midiOutUnprepareHeader (m_handle, &header, sizeof(MIDIHDR));
                if (result != MMSYSERR_NOERROR) {
                        DEBUG_MIDI (string_compose ("WinMMEMidiOutput: midiOutUnprepareHeader %1\n",
-                                                   get_error_string (result)));
+                                                   get_error_string (result)));
                        break;
                }
-#endif
        }
 
+#ifdef USE_MMCSS_THREAD_PRIORITIES
+       PBD::MMCSS::revert_thread_characteristics (task_handle);
+#endif
+
        m_thread_running = false;
 }