Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Timer Precision

This thread is locked; no one can reply to it. rss feed Print
Timer Precision
Paladin
Member #6,645
December 2005
avatar

Ok I already asked how to use timers for my needs and such and it does work, but it's not precise. The reason it needs to be exactly precise is I'm creating a metronome where you can change the beats per minute to anything. This is the main code I am using:

1void calculateMetronome() //This is called in the main loop
2{
3 bps = ((tempo * 1000) / 60000);
4 pause = (1000 / bps);
5}
6 
7void handleMetronome() //This is called after calculate metronome
8{
9 mettimer++;
10 if(mettimer >= pause)
11 {
12 play_sample(click, 255, 128, 1000, FALSE);
13 mettimer = 0;
14 }
15}
16 
17//This is the main loop
18void mainLoop()
19{
20 while(!key[KEY_ESC])
21 {
22 int mx = mouse_x;
23 int my = mouse_y;
24
25 calculateMetronome();
26
27 while(timer > 0)
28 {
29 --timer;
30 handleMetronome();
31 }
32 if(mouse_b & 1)
33 handleMouse(mx, my);
34 }
35}

That is my code right now. It works awesome, it's just the fact that the beats aren't accurate and there isn't really a difference unless you change the beats by a large margin(maybe around 30). I was wondering if there was a much more accurate way to do this? Or is my method just not accurate itself and is there a better way? Thank you.

Richard Phipps
Member #1,632
November 2001
avatar

Allegro's timers aren't too precise..

Here's the timing code I use:

.hpp file

#include <allegro.h>

void start_timer(void);
void reset_timer(void);
int check_timer(int frac_sec);

.cpp file

1#include <allegro.h>
2#include "timer.hpp"
3#ifdef ALLEGRO_WINDOWS
4#include <winalleg.h>
5 
6// High resolution timer code for Windows .Call start_timer first and then call
7// check_timer with the required accuracy range.
8// The timer is more accurate than the default Allegro timers..
9 
10struct timer
11{
12 LARGE_INTEGER tstart, tticks, tnow;
13 int started;
14 int high_freq;
15} timer;
16 
17 
18 
19void start_timer(void)
20{
21 timer.high_freq = QueryPerformanceFrequency(&timer.tticks);
22
23 if (timer.high_freq)
24 {
25 QueryPerformanceCounter(&timer.tstart);
26 }
27}
28 
29void reset_timer(void)
30{
31 if (timer.high_freq)
32 {
33 QueryPerformanceCounter(&timer.tnow);
34 timer.tstart = timer.tnow;
35 }
36}
37 
38int check_timer(int frac_sec)
39{
40 if (timer.high_freq)
41 {
42 QueryPerformanceCounter(&timer.tnow);
43 return (int) (((timer.tnow.QuadPart - timer.tstart.QuadPart) * frac_sec) / timer.tticks.QuadPart);
44 }
45 return 0;
46}
47 
48#endif /*
49 */
50 
51#if defined(ALLEGRO_MACOSX) || defined(ALLEGRO_UNIX)
52#include <sys/time.h>
53 
54// Timer code for OSX.Call start_timer first and then call
55// check_timer with the required accuracy range.
56
57static struct timeval tstart;
58 
59 
60void start_timer(void)
61{
62 gettimeofday(&tstart, NULL);
63}
64 
65void reset_timer(void)
66{
67 gettimeofday(&tstart, NULL);
68}
69 
70int check_timer(int frac_sec)
71{
72 struct timeval now;
73 double hi, lo;
74
75 gettimeofday(&now, NULL);
76
77 hi = (double) (now.tv_sec - tstart.tv_sec);
78 lo = ((double) (now.tv_usec - tstart.tv_usec)) / 1.0e6;
79
80 return (int) ((hi + lo) * ((double) frac_sec));
81}
82 
83#endif /* */

Once you've called start_timer, you can do say time = check_timer(100); Which will check with a precision of 100 ticks per second. You should be able to go up to at least (250) ticks per second precision. Just remember to call reset_timer before you do another check if you've found a new tick.. I.e.:

if (check_timer(100) > 0)
{
 // do stuff..
 reset_timer();
}

Hope that helps. :)

Paladin
Member #6,645
December 2005
avatar

How am I supposed to implement this with my code? Do I call check_timer(); in the main while loop? Or do I replace the if(timer < 0) line in my main loop with a different variable?

Richard Phipps
Member #1,632
November 2001
avatar

Change:

  while(timer > 0)
  {
   --timer;
   handleMetronome();
  }

to:

  timer = check_timer(100);
  while (timer > 0)
  {
   reset_timer();
   handleMetronome();
  }

You would have to add a start_timer() call before the main loop and use a timer global variable in your program that other functions can use (like you have now with an allegro timer. (or you could pass it to your functions as a variable).

Paladin
Member #6,645
December 2005
avatar

Ok, I have this timer code of yours put in at the top of my source and it works great, but the problem is that it plays the sound very fast. What type of time measurement am I dealing with so I can change my algorithm to find the pause inbetween each sound?

Richard Phipps
Member #1,632
November 2001
avatar

I'm not sure what you mean.. I don't know music calculations. But the value you call check_timer() with is the accuracy. I.e. using check_timer(1); would mean that the value returned would be the number of seconds since the last reset_timer call.

10 would be 10'ths of a second, etc.. So if check timer(100) returns 100 then 1 second has elaped.

Is that helpful?

Paladin
Member #6,645
December 2005
avatar

Yeah that makes sense. I'm simply trying to get the pause inbetween each beat by finding the beats per second (from beats per minute). So I can change that value to whatever I want to make it more accurate?

EDIT: I am also noticing that I cannot do anything when the program starts. I can't press any keys or click on anything with my mouse. I think it's because of the sound playing, but I don't think it should be doing that.

Richard Phipps
Member #1,632
November 2001
avatar

Quote:

So I can change that value to whatever I want to make it more accurate?

Yes..

Quote:

I am also noticing that I cannot do anything when the program starts. I can't press any keys or click on anything with my mouse. I think it's because of the sound playing, but I don't think it should be doing that.

No idea with this problem. :)

Paladin
Member #6,645
December 2005
avatar

Ok everything is fine except the metronome code now. I'm not sure if I'm handling it correctly or not.

Main Loop:

1 
2void mainLoop()
3{
4 while(!key[KEY_ESC])
5 {
6 int mx = mouse_x;
7 int my = mouse_y;
8
9 if(mouse_b & 1)
10 handleMouse(mx, my);
11 calculateMetronome();
12
13 while(check_timer(100) > 0)
14 {
15 reset_timer();
16 handleMetronome();
17 }
18 }
19}

Code to make sound occur every so often

void handleMetronome()
{
 if(check_timer(100) >= pause)
 {
  play_sample(click, 255, 128, 1000, FALSE);
  reset_timer();
 }
}

Can I use the check_timer twice like that or am I doing it incorrectly?

Richard Phipps
Member #1,632
November 2001
avatar

Not sure about the sound issue.

Quote:

Can I use the check_timer twice like that or am I doing it incorrectly?

No that's fine. However, wouldn't it be easier to do a fixed rate check, i.e. check_timer(200); And then have your metronome data take this into account with the song's tempo?

Paladin
Member #6,645
December 2005
avatar

Is this what you mean?

void handleMetronome()
{
 mettimer = check_timer(100);
 if(mettimer >= 100)
 {
  play_sample(click, 255, 128, 1000, FALSE);
  reset_timer();
 }
}

This is the main loop at the moment:

1void mainLoop()
2{
3 while(!key[KEY_ESC])
4 {
5 int mx = mouse_x;
6 int my = mouse_y;
7
8 if(mouse_b & 1)
9 handleMouse(mx, my);
10 calculateMetronome();
11
12 while(check_timer(100) > 0)
13 {
14 reset_timer();
15 handleMetronome();
16 }
17 }
18}

It's still not working either way though. Sorry to seem stupid.

Mark Oates
Member #1,146
March 2001
avatar

this is the way I've done it for my sequencer, and the playback is pretty acurate. To change the tempo, just call set_playback_tempo() and get_playback_tempo(). In my sequencer, however, nothing is being drawn to the screen during playback. This is to ensure acurate timing.

1volatile int game_counter;
2void my_timer_handler() { game_counter++; }
3END_OF_FUNCTION(my_timer_handler)
4 
5 
6void setup_sound()
7{
8 
9 if (install_int_ex(my_timer_handler, BPS_TO_TIMER(1000)) != 0)
10 {
11 allegro_message("cannot setup playback timer");
12 }
13 LOCK_FUNCTION(my_timer_handler); //locks the function so not to cause problems while interrupting
14 LOCK_VARIABLE(game_counter); //locks the variable so not to cause problems while interrupting
15 
16}
17 
18 
19////////////
20 
21 
22float playback_cursor = 0;
23double playback_speed = 1.0;
24double odd_multiplier = 1.0;
25 
26void set_playback_tempo(double tempo, Duration duration=DURATION_quarter, int dots=0)
27{
28 playback_speed = (tempo / 120.0);
29}
30 
31double get_playback_tempo()
32{
33 return (playback_speed * 120.0f);
34}
35 
36 
37////////////
38 
39 
40void main_loop()
41{
42 if (game_counter > 0)
43 {
44 game_counter = 0;
45 playback_cursor += (playback_speed * odd_multiplier);
46 }
47 
48 if (playback_cursor >= **the_note's_attack_time**) play_note();
49}

the only strange thing is when I change MIDI drivers, I sometimes have to set the odd multiplier to 7.0 . ???

Paladin
Member #6,645
December 2005
avatar

I'm trying to find a method more accurate than the allegro default timers actually. None of my code is working. ><

GullRaDriel
Member #3,861
September 2003
avatar

Check this thread for HighRes Timer.

Here is my own wrapper.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Richard Phipps
Member #1,632
November 2001
avatar

GullRaDriel: Did you read this thread? I already posted high res timer code.. ???

GullRaDriel
Member #3,861
September 2003
avatar

Quote:

I already posted high res timer code..

I know, but there are C/C++, RDTSC, and lots of various way of using GTOD & QPC.

I was not trying to say that your code is not right, I was trying to help.

Quote:

GullRaDriel: Did you read this thread?

I am at work, and I did not took the time to read the whole thread.

Last, be sure I would have put your code into the thread if only I was able to edit it.

My excuse, dear RP.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Go to: