enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / pbd / windows_timer_utils.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 "pbd/windows_timer_utils.h"
20
21 #include <windows.h>
22 #include <mmsystem.h>
23
24 #include "pbd/compose.h"
25 #include "pbd/debug.h"
26 #include "pbd/error.h"
27
28 #include "pbd/i18n.h"
29
30 #define DEBUG_TIMING(msg) DEBUG_TRACE (PBD::DEBUG::Timing, msg);
31
32 namespace {
33
34 static
35 UINT&
36 timer_resolution ()
37 {
38         static UINT timer_res_ms = 0;
39         return timer_res_ms;
40 }
41
42 } // namespace
43
44 namespace PBD {
45
46 namespace MMTIMERS {
47
48 bool
49 get_min_resolution (uint32_t& min_resolution_ms)
50 {
51         TIMECAPS caps;
52
53         if (timeGetDevCaps (&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
54                 DEBUG_TIMING ("Could not get timer device capabilities.\n");
55                 return false;
56         }
57
58         min_resolution_ms = caps.wPeriodMin;
59         return true;
60 }
61
62 bool
63 set_min_resolution ()
64 {
65         uint32_t min_resolution = 0;
66
67         if (!get_min_resolution (min_resolution)) {
68                 return false;
69         }
70
71         if (!set_resolution (min_resolution)) {
72                 return false;
73         }
74         return true;
75 }
76
77 bool
78 set_resolution (uint32_t timer_resolution_ms)
79 {
80         if (timer_resolution() != 0) {
81                 DEBUG_TIMING(
82                     "Timer resolution must be reset before setting new resolution.\n");
83         }
84
85         if (timeBeginPeriod(timer_resolution_ms) != TIMERR_NOERROR) {
86                 DEBUG_TIMING(
87                     string_compose("Could not set timer resolution to %1(ms)\n",
88                                    timer_resolution_ms));
89                 return false;
90         }
91
92         timer_resolution() = timer_resolution_ms;
93
94         DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n",
95                                       timer_resolution_ms));
96         return true;
97 }
98
99 bool
100 reset_resolution ()
101 {
102         // You must match calls to timeBegin/EndPeriod with the same resolution
103         if (timeEndPeriod(timer_resolution()) != TIMERR_NOERROR) {
104                 DEBUG_TIMING("Could not reset the Timer resolution.\n");
105                 return false;
106         }
107         DEBUG_TIMING("Reset the Timer resolution.\n");
108         timer_resolution() = 0;
109         return true;
110 }
111
112 } // namespace MMTIMERS
113
114 namespace {
115
116 static double timer_rate_us = 0.0;
117
118 static
119 bool
120 test_qpc_validity ()
121 {
122         int64_t last_timer_val = PBD::QPC::get_microseconds ();
123         if (last_timer_val < 0) return false;
124
125         for (int i = 0; i < 100000; ++i) {
126                 int64_t timer_val = PBD::QPC::get_microseconds ();
127                 if (timer_val < 0) return false;
128                 // try and test for non-syncronized TSC(AMD K8/etc)
129                 if (timer_val < last_timer_val) return false;
130                 last_timer_val = timer_val;
131         }
132         return true;
133 }
134
135 } // anon namespace
136
137 namespace QPC {
138
139 bool
140 check_timer_valid ()
141 {
142         if (!timer_rate_us) {
143                 return false;
144         }
145         return test_qpc_validity ();
146 }
147
148 bool
149 initialize ()
150 {
151         LARGE_INTEGER freq;
152         if (!QueryPerformanceFrequency(&freq) || freq.QuadPart < 1) {
153                 info << X_("Failed to determine frequency of QPC\n") << endmsg;
154                 timer_rate_us = 0;
155         } else {
156                 timer_rate_us = 1000000.0 / freq.QuadPart;
157         }
158         info << string_compose(X_("QPC timer microseconds per tick: %1\n"),
159                                timer_rate_us) << endmsg;
160         return !timer_rate_us;
161 }
162
163 int64_t
164 get_microseconds ()
165 {
166         LARGE_INTEGER current_val;
167
168         if (timer_rate_us) {
169                 // MS docs say this will always succeed for systems >= XP but it may
170                 // not return a monotonic value with non-invariant TSC's etc
171                 if (QueryPerformanceCounter(&current_val) != 0) {
172                         return (int64_t)(current_val.QuadPart * timer_rate_us);
173                 }
174         }
175         DEBUG_TIMING ("Could not get QPC timer\n");
176         return -1;
177 }
178
179 } // namespace QPC
180
181 int64_t
182 get_microseconds ()
183 {
184         if (timer_rate_us) {
185                 return QPC::get_microseconds ();
186         }
187         // For XP systems that don't support a high-res performance counter
188         return g_get_monotonic_time ();
189 }
190
191 } // namespace PBD