Jeff Mikels
Jeff Mikels

Reputation: 733

Timing is not reliable playing audio using sdl2/mixer in nim

I'm trying to build a simple metronome to learn the nim programming language, and though I can get audio to play, the timing doesn't work. I'm running this on Mac OSX and there is always a lag every third or fourth 'click'

Here's my code:

# nim code to create a metronome
import times, os
import sdl2, sdl2/mixer

sdl2.init(INIT_AUDIO)

var click : ChunkPtr
var channel : cint
var audio_rate : cint
var audio_format : uint16
var audio_buffers : cint    = 4096
var audio_channels : cint   = 2

if mixer.openAudio(audio_rate, audio_format, audio_channels, audio_buffers) != 0:
    quit("There was a problem")

click = mixer.loadWAV("click.wav")    

var bpm = 120
var next_click = getTime()
let dur = initDuration(milliseconds = toInt(60000 / bpm))
var last_click = getTime()
while true:
    var now = getTime()
    if now >= next_click:
        next_click = next_click + dur
        # discard mixer.playChannelTimed(0, click, 0, cint(500)
        discard mixer.playChannel(0, click, 0)
    os.sleep(1)

Any idea why the lag?

(by the way, the click.wav file is only one channel and 0.2 seconds long)

Upvotes: 1

Views: 212

Answers (1)

Grzegorz Adam Hankiewicz
Grzegorz Adam Hankiewicz

Reputation: 7681

The call to os.sleep(1) is unreliable as a high precision timing control. On MacOSX it calls to nanosleep, which states:

   If the interval specified in req is not an exact multiple of the
   granularity underlying clock (see time(7)), then the interval will be
   rounded up to the next multiple.  Furthermore, after the sleep
   completes, there may still be a delay before the CPU becomes free to
   once again execute the calling thread.

As such, you need to find a different more reliable waiting method or simply remove that delay and burn CPU cicles in the hope of being more precise (your program could still get preempted by the OS anyway).

Upvotes: 1

Related Questions