#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 {
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) {
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;
{
// 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);
}
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;
}
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;
}
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;
{
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;
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);
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:
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));
}
}
{
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)) {
}
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;
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));
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))
{
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) {
((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) {
continue;
}
-#if ENABLE_SYSEX
MIDIHDR header;
header.dwBufferLength = h.size;
header.dwFlags = 0;
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;
}