jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
8.9KB

  1. /*
  2. Copyright (C) 2001 Paul Davis
  3. Copyright (C) 2004-2008 Grame
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. */
  16. #include "JackFrameTimer.h"
  17. #include "JackError.h"
  18. #include <math.h>
  19. #include <stdio.h>
  20. namespace Jack
  21. {
  22. #if defined(WIN32) && !defined(__MINGW32__)
  23. /* missing on Windows : see http://bugs.mysql.com/bug.php?id=15936 */
  24. inline double rint(double nr)
  25. {
  26. double f = floor(nr);
  27. double c = ceil(nr);
  28. return (((c -nr) >= (nr - f)) ? f : c);
  29. }
  30. #endif
  31. JackTimer::JackTimer()
  32. {
  33. fInitialized = false;
  34. fFrames = 0;
  35. fCurrentWakeup = 0;
  36. fCurrentCallback = 0;
  37. fNextWakeUp = 0;
  38. fPeriodUsecs = 0.0f;
  39. fFilterOmega = 0.0f; /* Initialised later */
  40. }
  41. jack_nframes_t JackTimer::Time2Frames(jack_time_t usecs, jack_nframes_t buffer_size)
  42. {
  43. if (fInitialized) {
  44. /*
  45. Make sure we have signed differences. It would make a lot of sense
  46. to use the standard signed intNN_t types everywhere instead of e.g.
  47. jack_nframes_t and jack_time_t. This would at least ensure that the
  48. types used below are the correct ones. There is no way to get a type
  49. that would be 'a signed version of jack_time_t' for example - the
  50. types below are inherently fragile and there is no automatic way to
  51. check they are the correct ones. The only way is to check manually
  52. against jack/types.h. FA - 16/02/2012
  53. */
  54. int64_t du = usecs - fCurrentWakeup;
  55. int64_t dp = fNextWakeUp - fCurrentWakeup;
  56. return fFrames + (int32_t)rint((double)du / (double)dp * buffer_size);
  57. } else {
  58. return 0;
  59. }
  60. }
  61. jack_time_t JackTimer::Frames2Time(jack_nframes_t frames, jack_nframes_t buffer_size)
  62. {
  63. if (fInitialized) {
  64. /*
  65. Make sure we have signed differences. It would make a lot of sense
  66. to use the standard signed intNN_t types everywhere instead of e.g.
  67. jack_nframes_t and jack_time_t. This would at least ensure that the
  68. types used below are the correct ones. There is no way to get a type
  69. that would be 'a signed version of jack_time_t' for example - the
  70. types below are inherently fragile and there is no automatic way to
  71. check they are the correct ones. The only way is to check manually
  72. against jack/types.h. FA - 16/02/2012
  73. */
  74. int32_t df = frames - fFrames;
  75. int64_t dp = fNextWakeUp - fCurrentWakeup;
  76. return fCurrentWakeup + (int64_t)rint((double) df * (double) dp / buffer_size);
  77. } else {
  78. return 0;
  79. }
  80. }
  81. int JackTimer::GetCycleTimes(jack_nframes_t* current_frames, jack_time_t* current_usecs, jack_time_t* next_usecs, float* period_usecs)
  82. {
  83. if (fInitialized) {
  84. *current_frames = fFrames;
  85. *current_usecs = fCurrentWakeup;
  86. *next_usecs = fNextWakeUp;
  87. *period_usecs = fPeriodUsecs;
  88. return 0;
  89. } else {
  90. return 1;
  91. }
  92. }
  93. jack_nframes_t JackTimer::FramesSinceCycleStart(jack_time_t cur_time, jack_nframes_t frames_rate)
  94. {
  95. return (jack_nframes_t) floor((((float)frames_rate) / 1000000.0f) * (cur_time - fCurrentCallback));
  96. }
  97. void JackFrameTimer::InitFrameTime()
  98. {
  99. fFirstWakeUp = true;
  100. }
  101. void JackFrameTimer::IncFrameTime(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
  102. {
  103. if (fFirstWakeUp) {
  104. InitFrameTimeAux(callback_usecs, period_usecs);
  105. fFirstWakeUp = false;
  106. }
  107. IncFrameTimeAux(buffer_size, callback_usecs, period_usecs);
  108. }
  109. void JackFrameTimer::ResetFrameTime(jack_nframes_t frames_rate, jack_time_t callback_usecs, jack_time_t period_usecs)
  110. {
  111. if (!fFirstWakeUp) { // ResetFrameTime may be called by a xrun/delayed wakeup on the first cycle
  112. JackTimer* timer = WriteNextStateStart();
  113. jack_nframes_t period_size_guess = (jack_nframes_t)(frames_rate * ((timer->fNextWakeUp - timer->fCurrentWakeup) / 1000000.0));
  114. timer->fFrames += ((callback_usecs - timer->fNextWakeUp) / period_size_guess) * period_size_guess;
  115. timer->fCurrentWakeup = callback_usecs;
  116. timer->fCurrentCallback = callback_usecs;
  117. timer->fNextWakeUp = callback_usecs + period_usecs;
  118. WriteNextStateStop();
  119. TrySwitchState(); // always succeed since there is only one writer
  120. }
  121. }
  122. /*
  123. Use the state returned by ReadCurrentState and check that the state was not changed during the read operation.
  124. The operation is lock-free since there is no intermediate state in the write operation that could cause the
  125. read to loop forever.
  126. */
  127. void JackFrameTimer::ReadFrameTime(JackTimer* timer)
  128. {
  129. UInt16 next_index = GetCurrentIndex();
  130. UInt16 cur_index;
  131. do {
  132. cur_index = next_index;
  133. memcpy(timer, ReadCurrentState(), sizeof(JackTimer));
  134. next_index = GetCurrentIndex();
  135. } while (cur_index != next_index); // Until a coherent state has been read
  136. }
  137. // Internal
  138. void JackFrameTimer::InitFrameTimeAux(jack_time_t callback_usecs, jack_time_t period_usecs)
  139. {
  140. /* the first wakeup or post-freewheeling or post-xrun */
  141. /* There seems to be no significant difference between
  142. the two conditions OR-ed above. Incrementing the
  143. frame_time after an xrun shouldn't harm, as there
  144. will be a discontinuity anyway. So the two are
  145. combined in this version.
  146. FA 16/03/2012
  147. */
  148. /* Since the DLL *will* be run, next_wakeup should be the
  149. current wakeup time *without* adding the period time, as
  150. if it were computed in the previous period.
  151. FA 16/03/2012
  152. */
  153. /* Added initialisation of timer->period_usecs, required
  154. due to the modified implementation of the DLL itself.
  155. OTOH, this should maybe not be repeated after e.g.
  156. freewheeling or an xrun, as the current value would be
  157. more accurate than the nominal one. But it doesn't really
  158. harm either. Implementing this would require a new flag
  159. in the engine structure, to be used after freewheeling
  160. or an xrun instead of first_wakeup. I don't know if this
  161. can be done without breaking compatibility, so I did not
  162. add this
  163. FA 13/02/2012
  164. */
  165. /* Added initialisation of timer->filter_omega. This makes
  166. the DLL bandwidth independent of the actual period time.
  167. The bandwidth is now 1/8 Hz in all cases. The value of
  168. timer->filter_omega is 2 * pi * BW * Tperiod.
  169. FA 13/02/2012
  170. */
  171. JackTimer* timer = WriteNextStateStart();
  172. timer->fPeriodUsecs = (float)period_usecs;
  173. timer->fCurrentCallback = callback_usecs;
  174. timer->fNextWakeUp = callback_usecs;
  175. timer->fFilterOmega = period_usecs * 7.854e-7f;
  176. WriteNextStateStop();
  177. TrySwitchState(); // always succeed since there is only one writer
  178. }
  179. void JackFrameTimer::IncFrameTimeAux(jack_nframes_t buffer_size, jack_time_t callback_usecs, jack_time_t period_usecs)
  180. {
  181. JackTimer* timer = WriteNextStateStart();
  182. /* Modified implementation (the actual result is the same).
  183. 'fSecondOrderIntegrator' is renamed to 'fPeriodUsecs'
  184. and now represents the DLL's best estimate of the
  185. period time in microseconds (before it was a scaled
  186. version of the difference w.r.t. the nominal value).
  187. This allows this value to be made available to clients
  188. that are interested in it (see jack_get_cycle_times).
  189. This change also means that 'fPeriodUsecs' must be
  190. initialised to the nominal period time instead of zero.
  191. This is done in the first cycle in jack_run_cycle().
  192. 'fFilterCoefficient' is renamed to 'fFilterOmega'. It
  193. is now equal to the 'omega' value as defined in the
  194. 'Using a DLL to filter time' paper (before it was a
  195. scaled version of this value). It is computed once in
  196. jack_run_cycle() rather than set to a fixed value. This
  197. makes the DLL bandwidth independent of the period time.
  198. FA 13/02/2012
  199. */
  200. float delta = (float)((int64_t)callback_usecs - (int64_t)timer->fNextWakeUp);
  201. delta *= timer->fFilterOmega;
  202. timer->fCurrentWakeup = timer->fNextWakeUp;
  203. timer->fCurrentCallback = callback_usecs;
  204. timer->fFrames += buffer_size;
  205. timer->fPeriodUsecs += timer->fFilterOmega * delta;
  206. timer->fNextWakeUp += (int64_t)floorf(timer->fPeriodUsecs + 1.41f * delta + 0.5f);
  207. timer->fInitialized = true;
  208. WriteNextStateStop();
  209. TrySwitchState(); // always succeed since there is only one writer
  210. }
  211. } // end of namespace