Precisely synchronise playing wav file with system clock

Alex Simmons

New Member
Joined
Dec 3, 2012
Messages
17
I am attempting to precisely synchronise the playing of an audio wav file with the system clock. I need it to reliably start playing in sync with the clock every time, or at least with a predictable and consistent delay. At present the start delay is not consistent, sometimes it's right on cue, while sometimes it has a delay of ~1 second.

It's for a sporting count down timer, hence why having it play on cue is important to me.

The wav file I created (TTstart2.wav) is 10.800 seconds long and provides a 800ms beep at 10 seconds to go, then 250ms beeps at 5, 4, 3, 2, and 1 seconds to go, and then a long 800ms higher frequency beep at zero seconds.

I'm using Excel 2010 VBA.

I've no problem in getting the wav file to play, I just used the PlayWAV code I found online and included below.

The problem is getting it to play right on cue every time.

It does so most of the time, but no matter what way I make the call to play the wav file, occasionally it has a start delay. I've tried all sort of timing loops (my actual code has countdown timers displayed).

It happens once in about every 4 or 5 times it's called to play and I don't understand why or how to fix it, or what an alternative approach might be to sync an audio file with the system clock. Perhaps Excel VBA can't actually manage such things with such timing precision.

This is some code I have to demonstrate the problem (my code where it occurs is embedded in other more complex stopwatch timer arrangement so I replicated the problem in a more simple example).

Code:
Option Explicit


Private Declare Function PlaySound Lib "winmm.dll" _
  Alias "PlaySoundA" (ByVal lpszName As String, _
  ByVal hModule As Long, ByVal dwFlags As Long) As Long


Const SND_SYNC = &H0
Const SND_ASYNC = &H1
Const SND_FILENAME = &H20000


'----------------------------------------


Sub PlayWAV()
    Dim WAVFile As String
    WAVFile = "TTstart2.wav"
    ' TTstart2.wav is my own audio wav file
    
    WAVFile = ThisWorkbook.Path & "\" & WAVFile
    ' I've put the wav file in same folder as the workbook
    
    Call PlaySound(WAVFile, 0&, SND_ASYNC Or SND_FILENAME)
    ' this plays the wav file in asynchronous mode meaning it does not stop other code
    
End Sub


'----------------------------------------


Sub repeatplayaudio()
    Dim audiostart As Range
    Dim i As Integer
    Dim H, M, S, playtime As String
    
  ' loop through the start times listed in chronological order in column A1:A8
  
  ' before running this sub:
  
  ' in my sheet I put a start time in cell A1 which is just into the future
  ' and then have 15 second gaps between each subsequent cell
  ' e.g. if it's currently 9:09:00AM in cell A1 I put 9:10:00AM
  ' then cell A2  = A1 + 15/24/3600
  ' and copy down
  
    For i = 1 To 8
    
      ' choose next cell in column A
        Set audiostart = Range("A" & i)
      
      ' wait until start time arrives
        Do While audiostart.Value > Now
        Loop
        
      ' once start time has arrived then play the audio file
      ' set the hour, minute and second into a string for the OnTime function
        H = Hour(audiostart.Value)
        M = Minute(audiostart.Value)
        S = Second(audiostart.Value)
        playtime = H & ":" & M & ":" & S
        
        Application.OnTime TimeValue(playtime), "PlayWAV"
        
    Next i
    
End Sub

The problem is that no matter what method I use to call a sub to play the wav file (be it Application.OnTime, or various timing loops, timer delays subs etc), about every 4th or 5th time the wav file is delayed by about a second. All other times it plays right on cue, but for some reason there is an occasional delay and I can't work out why.

Any suggestions?

Thanks!
 
So I tried it with a wav file that is a 250ms audio beep, and ran it every 1000ms. Doesn't miss a beat. Weird.

edit: actually that's not quite right , there are small variations.

when I run it with a 500ms loop, I can hear the slight variations in the timing from beep to beep. Not bad variations, good enough for what I need, but can't work out why the longer wav file suffers from the much longer occasional delay.
 
Last edited:
Upvote 0

Excel Facts

Bring active cell back into view
Start at A1 and select to A9999 while writing a formula, you can't see A1 anymore. Press Ctrl+Backspace to bring active cell into view.
Thanks

I modified the code by adding in the PlayWAV routine, and then set the loop to trigger every 15,000 milliseconds.

I still get the same occasional delay of about a second in the audio file playback.

So perhaps it's less of an issue with the triggering of the subroutine on time (although that may still be an issue) and more about the audio file commencing immediately every time.

After trying a multitude of things, including the Set Timer methods and Query PerformanceCounter methods I've been pointed to in this thread, I've come to the conclusion that it's not so much a problem with the timer solution I'm using to trigger the PlayWAV sub, rather the wav file simply does not start playing immediately once called. There is an inconsistent delay.

So I wonder how does one play a wav file precisely on cue? There must be a need to synchronise audio in other applications.

I don't know the correct jargon - e.g. can a wav file be preloaded into RAM and called the instant it's required?
 
Upvote 0
I don't know the correct jargon - e.g. can a wav file be preloaded into RAM and called the instant it's required?
Yes, call PlaySound with the SND_MEMORY flag - https://msdn.microsoft.com/en-us/library/windows/desktop/dd743680(v=vs.85).aspx.

You have to read the .wav file into a byte array and pass the first element of the array as the PlaySound pszSound parameter. Notice that in the declaration of the PlaySound function in the code below, the pszSound parameter is declared "As Any", instead of "As String". This makes it easier to call the function with either a byte array (by passing the first element of the array, which is the address of the array) or a file name.

Code:
Option Explicit

Private Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundA" _
    (ByRef pszSound As Any, ByVal hModule As Long, ByVal dwFlags As Long) As Long

Private Const SND_SYNC = &H0
Private Const SND_ASYNC = &H1
Private Const SND_MEMORY = &H4


'Array to hold bytes read from .wav file must be declared at module level, not procedure level, otherwise
'PlaySound is silent when it plays the sound bytes.  The array could also be declared as Public to make
'it available to all procedures in all modules.

Private WAVbytes() As Byte


Public Sub PlayWAV()

    Dim WAVFile As String, i As Integer
    
    WAVFile = "C:\Windows\Media\Windows XP Startup.wav"
    
    If Dir(WAVFile) <> "" Then

        '.wav file exists, so load into memory

        WAVbytes = ReadFile(WAVFile)
        
        'Play 5 times from memory
        
        For i = 1 To 5
            PlaySound WAVbytes(0), 0&, SND_MEMORY Or SND_ASYNC
            Application.Wait DateAdd("s", 6, Now)
        Next
        
    End If
        
End Sub


Private Function ReadFile(sFile As String) As Byte()
    Dim nFile As Integer
    nFile = FreeFile
    Open sFile For Binary Access Read As #nFile
    If LOF(nFile) > 0 Then
        ReDim ReadFile(0 To LOF(nFile) - 1)
        Get nFile, , ReadFile
    End If
    Close #nFile
End Function
 
Upvote 0
Yes, call PlaySound with the SND_MEMORY flag - https://msdn.microsoft.com/en-us/library/windows/desktop/dd743680(v=vs.85).aspx.

You have to read the .wav file into a byte array and pass the first element of the array as the PlaySound pszSound parameter. Notice that in the declaration of the PlaySound function in the code below, the pszSound parameter is declared "As Any", instead of "As String". This makes it easier to call the function with either a byte array (by passing the first element of the array, which is the address of the array) or a file name.

OK, thanks once again. Learning as I go!

I built in the code to first read the wav file into memory into the timer routine (so it was only done the once), and then making the PlayWAV call as per:

Code:
PlaySound WAVbytes(0), 0&, SND_MEMORY Or SND_ASYNC

Unfortunately I still get a one second delay every fifth time it plays. The other 4 times it's on cue.

I'm running out of ideas!
 
Upvote 0

Forum statistics

Threads
1,215,340
Messages
6,124,382
Members
449,155
Latest member
ravioli44

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top