About Multimedia Timers

Multimedia timer services allow applications to schedule timer events with the greatest resolution (or accuracy) possible for the hardware platform. These multimedia timer services allow you to schedule timer events at a higher resolution than other timer services. These timer services are useful for applications that demand high-resolution timing. For example, a MIDI sequencer requires a high-resolution timer because it must maintain the pace of MIDI events within a resolution of 1 millisecond.

Availability

The number of multimedia timers your application can create can vary depending on the operating system it is running under. In Windows NT timers are allocated on a per process basis, while in Windows 95/98 there is an absolute limit on the total number of timers system-wide.

Operating System
Timers
Windows 95/98
32 total for system
Windows NT
16 per process

User Interface Issues

If you attempt to update the screen at very high frequencies (more often than every 10ms, or so), prepare for fireworks. Windows 95/98 Graphic Device Interface (GDI) is still composed of much 16-bit code, and simply can't keep up. Hard-locks, and even system resets, can occur. If the Interval property is user configurable, test before updating the screen:

 
Private Sub Timer1_Timer()
Static Ticks As Long
Ticks = Ticks + 1
'
' Updating the display more often than
' every 10ms can blow Win95's 16-bit GDI
' to shreds -- hardlock or system reset.
'
If Timer1.Interval >= 10 Then
lblTimer1.Caption = " " & Format(Ticks, "#,##0")
ElseIf (Ticks Mod 10) = 0 Then
lblTimer1.Caption = " " & Format(Ticks, "#,##0")
End If
End Sub

The Zen of Timers

The ccrpTimers library practices "safe timers." That is, internally, a single multimedia timer is used and each timer object is notified on each timer callback. The timer objects then, in turn, check to see if their specified Interval has elapsed, and if so raise a Timer event (or fire a notification method) to their client. This scheme insures the lowest possible impact on overall system performance.

Each timer object exposes a Stats property, which is actually a non-creatable class composed of data regarding the current state of the ccrpTimers library and the system multimedia timer. You may use the Stats.Frequency property set the internal interval used for the library timer. This is the rate at which all timer objects are notified of timer callbacks. It is not related to how often your client class or form will be notified, as long as this value is set to one equal to or lower than the Interval property of your timer object(s).

That last point is very important to keep in mind! The idea is to set the Stats.Frequency to a value that's as high as possible, without going higher than the timer Interval(s) you plan to use. In practical usage, there's really no need to go higher than 20 or 25 milliseconds, even if you only want a Timer event every few minutes or so. On most of today's hardware, there will be no measurable impact from such a setting.

However, if you set the Stats.Frequency to 20, then set a ccrpTimer.Interval to 10, you'll find that your events will only be half as regular as you desire. The whole point is to make it the highest common demoninator of all the timer object Intervals you plan to use. For example, setting Stats.Frequency to 30 and Interval to 100 doesn't make sense, as the timer callbacks will occur at 90 and 120 milliseconds. The 90 will not be enough to satisfy your Interval, so every notification will be at 120, or 20 milliseconds late. In this case, a setting of 20 or 25 would be far preferable.

Here's a little test you can run to observe how altering the Stats.Frequency setting will affect the accuracy with which your timer events are fired. Open a new project, set a Reference to the ccrpTimers library, and add the following code to the main form:

Option Explicit
Private WithEvents tmr As ccrpTimer 
Private Sub Form_Load() 
Set tmr = New ccrpTimer
With tmr
' Alter next value widely (1 to 400)
' to observe accuracy variations.
.Stats.Frequency = 1
.Interval = 200
.Enabled = True
Debug.Print "Resolution: "; .Stats.Resolution
End With
End Sub
Private Sub tmr_Timer(ByVal Milliseconds As Long) 
Debug.Print Milliseconds
End Sub

You'll see that you'll come extremely close to hitting the desired Interval the lower the Stats.Frequency value goes, and that the target will get progressively sloppier the higher you set this value. What you need to ask yourself at that point is, just how much accuracy does your application require?

Perhaps one of the most important things to take from this little discussion is that timer events are something that need to be handled expeditiously. Get in, get out. If you try to do too much, you'll clog up the system in a hurry. If possible, it's always wisest to share system timers, thus reducing the overhead of having multiple timers firing.