From 4ffe8ffc0faef8ea4bb17e27963bf3998a006995 Mon Sep 17 00:00:00 2001 From: Tim Mayberry Date: Sat, 12 Sep 2015 14:58:35 +1000 Subject: [PATCH] Put Windows timer functions into PBD namespace in preparation for moving them to libpbd Add functions for get/set the Multimedia timer resolution, although we are really only interested in the minimum, this will facilitate testing Put timer utility functions inside nested namespaces as they are platform specific --- libs/backends/portaudio/portaudio_backend.cc | 8 +- libs/backends/portaudio/win_utils.cc | 154 ++++++++++++------ libs/backends/portaudio/win_utils.h | 58 ++++++- .../portaudio/winmmemidi_input_device.cc | 2 +- libs/backends/portaudio/winmmemidi_io.cc | 5 +- .../portaudio/winmmemidi_output_device.cc | 4 +- 6 files changed, 167 insertions(+), 64 deletions(-) diff --git a/libs/backends/portaudio/portaudio_backend.cc b/libs/backends/portaudio/portaudio_backend.cc index dcdb0d168d..f4ab827556 100644 --- a/libs/backends/portaudio/portaudio_backend.cc +++ b/libs/backends/portaudio/portaudio_backend.cc @@ -680,7 +680,7 @@ PortAudioBackend::samples_since_cycle_start () return 0; } - return m_cycle_timer.samples_since_cycle_start (utils::get_microseconds()); + return m_cycle_timer.samples_since_cycle_start (PBD::get_microseconds()); } int @@ -1525,7 +1525,7 @@ PortAudioBackend::blocking_process_main(const float* interleaved_input_data, int64_t min_elapsed_us = 1000000; int64_t max_elapsed_us = 0; - m_dsp_calc.set_start_timestamp_us (utils::get_microseconds()); + m_dsp_calc.set_start_timestamp_us (PBD::get_microseconds()); i = 0; /* Copy input audio data into input port buffers */ @@ -1551,7 +1551,7 @@ PortAudioBackend::blocking_process_main(const float* interleaved_input_data, } m_last_cycle_start = m_cycle_timer.get_start(); - m_cycle_timer.reset_start(utils::get_microseconds()); + m_cycle_timer.reset_start(PBD::get_microseconds()); m_cycle_count++; uint64_t cycle_diff_us = (m_cycle_timer.get_start() - m_last_cycle_start); @@ -1599,7 +1599,7 @@ PortAudioBackend::blocking_process_main(const float* interleaved_input_data, _processed_samples += _samples_per_period; /* calculate DSP load */ - m_dsp_calc.set_stop_timestamp_us (utils::get_microseconds()); + m_dsp_calc.set_stop_timestamp_us (PBD::get_microseconds()); _dsp_load = m_dsp_calc.get_dsp_load(); DEBUG_TIMING(string_compose("DSP Load: %1\n", _dsp_load)); diff --git a/libs/backends/portaudio/win_utils.cc b/libs/backends/portaudio/win_utils.cc index dc0f8d5270..6d0e404749 100644 --- a/libs/backends/portaudio/win_utils.cc +++ b/libs/backends/portaudio/win_utils.cc @@ -27,6 +27,90 @@ namespace { +UINT& +old_timer_resolution () +{ + static UINT timer_res_ms = 0; + return timer_res_ms; +} + +} // anon namespace + +namespace PBD { + +namespace MMTIMERS { + +bool +set_min_resolution () +{ + TIMECAPS caps; + + if (timeGetDevCaps (&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not get timer device capabilities.\n"); + return false; + } + return set_resolution(caps.wPeriodMin); +} + +bool +set_resolution (uint32_t timer_resolution_ms) +{ + TIMECAPS caps; + + if (timeGetDevCaps (&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not get timer device capabilities.\n"); + return false; + } + + UINT old_timer_res = caps.wPeriodMin; + + if (timeBeginPeriod(timer_resolution_ms) != TIMERR_NOERROR) { + DEBUG_TIMING( + string_compose("Could not set minimum timer resolution to %1(ms)\n", + timer_resolution_ms)); + return false; + } + + old_timer_resolution () = old_timer_res; + + DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n", + caps.wPeriodMin)); + return true; +} + +bool +get_resolution (uint32_t& timer_resolution_ms) +{ + TIMECAPS caps; + + if (timeGetDevCaps(&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not get timer device capabilities.\n"); + return false; + } + timer_resolution_ms = caps.wPeriodMin; + return true; +} + +bool +reset_resolution () +{ + if (old_timer_resolution ()) { + if (timeEndPeriod (old_timer_resolution ()) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not reset timer resolution.\n"); + return false; + } + } + + DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n", + old_timer_resolution ())); + + return true; +} + +} // namespace MMTIMERS + +namespace { + bool& qpc_frequency_success () { @@ -48,76 +132,52 @@ qpc_frequency () return freq; } -UINT& -old_timer_resolution () +LARGE_INTEGER +qpc_frequency_cached () { - static UINT timer_res_ms = 0; - return timer_res_ms; + static LARGE_INTEGER frequency = qpc_frequency (); + return frequency; } } // anon namespace -namespace utils { +namespace QPC { bool -set_min_timer_resolution () +get_timer_valid () { - TIMECAPS caps; - - if (timeGetDevCaps (&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { - DEBUG_TIMING ("Could not get timer device capabilities.\n"); - return false; - } else { - old_timer_resolution () = caps.wPeriodMin; - if (timeBeginPeriod (caps.wPeriodMin) != TIMERR_NOERROR) { - DEBUG_TIMING (string_compose ( - "Could not set minimum timer resolution to %1(ms)\n", caps.wPeriodMin)); - return false; - } - } - - DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n", - caps.wPeriodMin)); - - return true; -} - -bool -reset_timer_resolution () -{ - if (old_timer_resolution ()) { - if (timeEndPeriod (old_timer_resolution ()) != TIMERR_NOERROR) { - DEBUG_TIMING ("Could not reset timer resolution.\n"); - return false; - } - } - - DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n", - old_timer_resolution ())); - - return true; + // setup caching the timer frequency + qpc_frequency_cached (); + return qpc_frequency_success (); } int64_t get_microseconds () { - static LARGE_INTEGER frequency = qpc_frequency (); LARGE_INTEGER current_val; if (qpc_frequency_success()) { - // MS docs say this will always succeed for systems >= XP but it may // not return a monotonic value with non-invariant TSC's etc if (QueryPerformanceCounter(¤t_val) != 0) { return (int64_t)(((double)current_val.QuadPart) / - ((double)frequency.QuadPart) * 1000000.0); - } else { - DEBUG_TIMING ("Could not get QPC timer\n"); + ((double)qpc_frequency_cached().QuadPart) * 1000000.0); } - return -1; + } + DEBUG_TIMING ("Could not get QPC timer\n"); + return -1; +} + +} // namespace QPC + +int64_t +get_microseconds () +{ + if (qpc_frequency_success()) { + return QPC::get_microseconds (); } // For XP systems that don't support a high-res performance counter return g_get_monotonic_time (); } -} // namespace utils +} // namespace PBD diff --git a/libs/backends/portaudio/win_utils.h b/libs/backends/portaudio/win_utils.h index 072ce1f0b0..c0c7bbf491 100644 --- a/libs/backends/portaudio/win_utils.h +++ b/libs/backends/portaudio/win_utils.h @@ -21,22 +21,66 @@ #include -namespace utils { +namespace PBD { -bool set_min_timer_resolution (); +namespace MMTIMERS { -bool reset_timer_resolution (); +/** + * Set the minimum Multimedia Timer resolution as supported by the system + * @return true if min timer resolution was successfully set + * + * Call reset_resolution to restore old timer resolution + */ +bool set_min_resolution (); -/** The highest resolution timer source provided by the system. On Vista and +/** + * Get current Multimedia Timer resolution + * @return true if getting the timer value was successful + */ +bool get_resolution(uint32_t& timer_resolution_us); + +/** + * Set current Multimedia Timer resolution + * @return true if setting the timer value was successful + */ +bool set_resolution(uint32_t timer_resolution_us); + +/** + * Reset the Multimedia Timer back to what it was originally before + * setting the timer resolution. + */ +bool reset_resolution (); + +} // namespace MMTIMERS + +namespace QPC { + +/** + * @return true if QueryPerformanceCounter is usable as a timer source + */ +bool get_timer_valid (); + +/** + * @return the value of the performance counter converted to microseconds + * + * If get_counter_valid returns true then get_microseconds will always + * return a positive value. If QPC is not supported(OS < XP) then -1 is + * returned but the MS docs say that this won't occur for systems >= XP. + */ +int64_t get_microseconds (); + +} // namespace QPC + +/** + * The highest resolution timer source provided by the system. On Vista and * above this is the value returned by QueryPerformanceCounter(QPC). On XP, * this will QPC if supported or otherwise g_get_monotonic_time will be used. * * @return A timer value in microseconds or -1 in the event that the reading - * the timer source fails, but the MS docs say that this won't occur for - * systems >= XP + * the timer source fails. */ int64_t get_microseconds (); -} +} // namespace PBD #endif // WIN_UTILS_H diff --git a/libs/backends/portaudio/winmmemidi_input_device.cc b/libs/backends/portaudio/winmmemidi_input_device.cc index d7a0a6dbaa..95e7faa476 100644 --- a/libs/backends/portaudio/winmmemidi_input_device.cc +++ b/libs/backends/portaudio/winmmemidi_input_device.cc @@ -346,7 +346,7 @@ WinMMEMidiInputDevice::enqueue_midi_msg (const uint8_t* midi_data, } // don't use winmme timestamps for now - uint64_t ts = utils::get_microseconds (); + uint64_t ts = PBD::get_microseconds (); DEBUG_TIMING (string_compose ( "Enqueing MIDI data device: %1 with timestamp: %2 and size %3\n", diff --git a/libs/backends/portaudio/winmmemidi_io.cc b/libs/backends/portaudio/winmmemidi_io.cc index 43c5d36469..4c93799637 100644 --- a/libs/backends/portaudio/winmmemidi_io.cc +++ b/libs/backends/portaudio/winmmemidi_io.cc @@ -31,7 +31,6 @@ #include "i18n.h" using namespace ARDOUR; -using namespace utils; WinMMEMidiIO::WinMMEMidiIO() : m_active (false) @@ -136,7 +135,7 @@ WinMMEMidiIO::start () m_run = true; DEBUG_MIDI ("Starting MIDI driver\n"); - set_min_timer_resolution(); + PBD::MMTIMERS::set_min_resolution(); discover(); start_devices (); } @@ -156,7 +155,7 @@ WinMMEMidiIO::stop () cleanup (); pthread_mutex_unlock (&m_device_lock); - reset_timer_resolution(); + PBD::MMTIMERS::reset_resolution(); } void diff --git a/libs/backends/portaudio/winmmemidi_output_device.cc b/libs/backends/portaudio/winmmemidi_output_device.cc index 84a72a2304..a1d76cd236 100644 --- a/libs/backends/portaudio/winmmemidi_output_device.cc +++ b/libs/backends/portaudio/winmmemidi_output_device.cc @@ -390,7 +390,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)); @@ -408,7 +408,7 @@ 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)); if (wakeup_time > h.time) {