PlaySound() function plays sound with inconsistent delay

567 views Asked by At

I am trying to create a command-line metronome app in C using PlaySound() from windows API. I am using the following code to generate a ticking metronome with 120 bmp tempo. The sound plays perfectly with my local .wav files but the tempo is not consistent. Sometimes it is rushed sometimes it is delayed. Any solution on how to make it consistent?

#include <stdio.h>
#include <windows.h>

int main() {
    while (1) {
        PlaySound("lib\\tick.wav", NULL, SND_FILENAME | SND_NODEFAULT | SND_ASYNC);
        Sleep(500);
        for (int i = 0;i < 3;i++) {
            PlaySound("lib\\click.wav", NULL, SND_FILENAME | SND_NODEFAULT | SND_ASYNC);
            Sleep(500);
        }
    }
    return 0;
}
1

There are 1 answers

1
Phil Freihofner On

When I managed to get a very good metronome working in Java, I did not rely on the Thread.sleep() method. As others point out in the comments, applications don't give very precise real time guarantees like this.

IDK what the equivalent is, with C, but what eventually worked for me was to have a continually streaming audio signal (of silence) where I counted the individual frames as they were being sent to the output, and mixed in the "click" PCM at the calculated frames.

For example, if you have 120 beats per minute, and a format of 44100 fps, then on every 22050th frame, you would mix in your click sound to the outgoing line.

The class I use in Java is the SourceDataLine if it helps to look at how that is structured. Does C even have an equivalent? Maybe it depends on what libraries are in use for sound output? I was pretty bewildered when I was looking at possibly doing my audio coding in C.

The nice thing about SourceDataLine (if you can find an equivalent) is that it employs a blocking queue, so the write operations are forced to wait until the system is ready for the next buffer load, and is thus closely tied to the rate of data consumption which stays very steady for clean playback.