A Timer Application Based on IReferenceClock


Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame

Environment: Win 95 or later, Win 2000


To program a MIDI-file player, I need a Windows timer with the best possible timekeeping capability because I send all MIDI events one after another. I have tried the Windows Multimedia Timer but I was not happy with it. Now I use the Reference Clock Interface of the class CBaseReferenceClock with less time latency.

The included project demonstrates how to program the interface of IReferenceClock. The MFC demo-program is a benchmark to get an idea of the exactness of the timer.

The clock counts with 100-nanosecond-entities but a maximal time-delay is not guaranteed. The sample is restricted with a resolution of 1 ms ( = 10000 ns) only.


The underlined keywords of the following text are explained in the MSDN documentation.

The CBaseReferenceClock class provides an interface to the timer-functions of IReferenceClock. In the sample, I have applied the functions GetTime and the one-shot timer AdviseTime. The application is advised by the signal of an event. I have used the class CAMEvent. Within this class I choose the function Wait to wait for a signaled state of the event. GetTime delivers the time in the type REFERENCE_TIME. To check the exactness of the clock, I convert the difference between the times before and after the waiting period using the function ConvertToMilliseconds.

The timer function runs in its own thread with high priority using the functions CreateThread and SetThreadPriority. The user is able to interrupt the process through button-clicks. This is possible thanks to a loop with WaitForSingleObject-calls with a time-out interval of 1000 ms which uses PeekMessage and PumpMessage to look for and process incoming messages.

Programming Hints

The header file "Streams.h" must be included.

With the release version, the libraries winmm.lib, strmbase.lib and strmiids.lib are required.

With the debug version, the libraries winmm.lib, strmbasd.lib, and strmiids.lib are used. You will find strmbasd.lib only after compiling "DXSDK/Samples/C++/DirectShow/dshow.dsw" in "DXSDK/Samples/C++/DirectShow/BaseClasses/Debug_Unicode".



  • CBaseReferenceClock - thinks about.

    Posted by _DS_ on 05/13/2004 04:58pm

    In CBaseReferenceClock all time arguments in-100 ns trimmed down to 1ms. I refer to source refclock.cpp from DirectShow base classes. It has a time-critical thread-loop, inside theread loop has a WaitForSingleObject with timeot value, whitch you specify in AdviseTime parameters, but trimmed to milliseconds, for wait function. All timings in this method depend from Wait function timeout. I think more easy and resourse-safe make a real-time-priority thread and call Sleep() or Wait function, if you need one-shot interval. This is part of refclock.cpp ---------- while ( !m_bAbort ) { // Wait for an interesting event to happen DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Delay: %lu ms"), dwWait )); WaitForSingleObject(m_pSchedule->GetEvent(), dwWait); if (m_bAbort) break; // There are several reasons why we need to work from the internal // time, mainly to do with what happens when time goes backwards. // Mainly, it stop us looping madly if an event is just about to // expire when the clock goes backward (i.e. GetTime stop for a // while). const REFERENCE_TIME rtNow = GetPrivateTime(); DbgLog((LOG_TIMING, 3, TEXT("CBaseRefClock::AdviseThread() Woke at = %lu ms"), ConvertToMilliseconds(rtNow) )); // We must add in a millisecond, since this is the resolution of our // WaitForSingleObject timer. Failure to do so will cause us to loop // franticly for (approx) 1 a millisecond. m_rtNextAdvise = m_pSchedule->Advise( 10000 + rtNow ); LONGLONG llWait = m_rtNextAdvise - rtNow; ASSERT( llWait > 0 ); llWait = ConvertToMilliseconds(llWait); // DON'T replace this with a max!! (The type's of these things is VERY important) dwWait = (llWait > REFERENCE_TIME(UINT_MAX)) ? UINT_MAX : DWORD(llWait); }; ------------------------ REFERENCE_TIME CBaseReferenceClock::GetPrivateTime() { CAutoLock cObjectLock(this); /* If the clock has wrapped then the current time will be less than * the last time we were notified so add on the extra milliseconds * * The time period is long enough so that the likelihood of * successive calls spanning the clock cycle is not considered. */ DWORD dwTime = timeGetTime(); { m_rtPrivateTime += Int32x32To64(UNITS / MILLISECONDS, (DWORD)(dwTime - m_dwPrevSystemTime)); m_dwPrevSystemTime = dwTime; } return m_rtPrivateTime; }

    • CBaseReferenceClock - thinks about.

      Posted by Hiko on 06/24/2004 04:07pm

      Hello, Late, my answer, but not too late, I hope. Your comments about the resolution of the IReferenceClock-Timer are completely correct. I did not know, that the timer works with a granulation of one ms like all other Windows-timers. On the other side, my application seems to run more precisely than before. Now I cannot explain, why. My application has to send a lot of MIDI-Events, i.e. short data-blocks, to an synthesizer via an asynchron serial interface. The events must be send at predefined time-points. Only very small delays can be accepted, because a human's ear is sensitive. The last days I have tried to find out the reason of my problem: I now suppose, it is the Multitasking-Windows, which interrupts my application, and not the quality of timers. In my new test, I exchanged the IReferenceClock-timer by a simple loop of statements. The loop is inside the program's thread as before. The results are bad as before. I got a perfect timing only, when I changed the priority of the task (not the thread) to realtime. In the Internet I found some other programmers, who fight with the same problem. I'll try now to solve it with another organisation for sending the events. Regards Heiko

    • CBaseReferenceClock - thinks about.

      Posted by Hiko on 05/15/2004 08:42pm

      Thank you for your comments about my article and the hint concerning refclock.cpp, which I did not found before. I look at the proposed method and I will answer later. Regards Heiko

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date