Jump to content

Func call ms delay


Recommended Posts

I was just experimenting, with a metronome design. With a simple repetitive click everything goes fine. However I thought of introducing some preset rhythms. I introduced two clicks for every other beat. so I get two short delays (between clicks) followed by a longer delay; repeating in a pattern. When I listened, I noticed that the timing was out. Each click is produced by a function call: so I figured that the extra function call was causing a time delay.

When I introduced a 5ms compensation factor, it seemed to fix the problem. However I guess that the delay caused by calling the extra function will be different on different machines. I am wondering what amount of variance to expect. If the typical time it takes to call a function is reasonably consistant, then I need not worry too much. I am presuming the average user will have between 500MB and 2GB ram, and will just be running the metronome. Any feedback would be most welcome.

Edit: I guess I'll have to run a speed test, to set the compensation factor, before running the main script; unless anyone has another suggestion.

Edited by czardas
Link to comment
Share on other sites

Thanks for the suggestion Mat, I haven't used Adlib before, but it looks interesting. I'm not sure if it will work though: as I will be calling the functon at different time intervals.

Edit: Perhaps I won't bother with preset rhythms, it's not really so important. I might just include the option to acceletate to a higher tempo within a given period of time. That's more useful for training purposes. :blink:

Edited by czardas
Link to comment
Share on other sites

I think timers would be better than adlib then as you can change it easily.

#include<Timers.au3>
#include<WindowsConstants.au3>

Global $hGUI = GUICreate("Metronome", 144, 46, -1, -1, BitOR($WS_SYSMENU, $WS_CAPTION, $WS_POPUP))
Global $hBPM = GUICtrlCreateInput("110", 2, 2, 140, 20)
GUICtrlCreateUpdown(-1)
GUICtrlSetLimit(-1, 300, 1)

$hBtn = GUICtrlCreateButton("Update", 42, 24, 60, 20)

GUISetState()
Global $hTimer = _Timer_SetTimer($hGUI, Int(60000 / 110), "_click")
ConsoleWrite($hTimer & @CRLF)

While 1
    Switch GUIGetMsg()
        Case -3
            _Timer_KillAllTimers($hGUI)
            Exit
        Case $hBtn
            _Timer_SetTimer($hGUI, Int(60000 / GUICtrlRead($hBPM)), "_click", $hTimer)
    EndSwitch
WEnd

Func _click($hWnd, $Msg, $iIDTimer, $dwTime)
    SoundPlay("C:\Windows\Media\Windows Ding.wav")
EndFunc
Link to comment
Share on other sites

And exactly how is that easier than this?

#include<WindowsConstants.au3>

Global $hGUI = GUICreate("Metronome", 144, 46, -1, -1, BitOR($WS_SYSMENU, $WS_CAPTION, $WS_POPUP))
Global $hBPM = GUICtrlCreateInput("110", 2, 2, 140, 20)
GUICtrlCreateUpdown(-1)
GUICtrlSetLimit(-1, 300, 1)

$hBtn = GUICtrlCreateButton("Update", 42, 24, 60, 20)

GUISetState()
AdlibRegister("_click", Int(60000 / 110))

While 1
    Switch GUIGetMsg()
        Case -3
            AdlibUnRegister()
            Exit
        Case $hBtn
            AdlibRegister("_click", Int(60000 / GUICtrlRead($hBPM)))
    EndSwitch
WEnd

Func _click()
    SoundPlay("C:\Windows\Media\Windows Ding.wav")
EndFunc
Link to comment
Share on other sites

Sorry, I should have explained. I am not using wav files. I don't really trust it. I am using Trancexx's DSBeep.au3 (inspired by AZJIO) => post number 13 in this topic. :blink:

#include "DSBeep.au3"

Opt("MustDeclareVars", 1)

HotKeySet("{ESC}", "_Quit")

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")
Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc

_StartDSBeep()

Local $iBpm = 100
Local $iDelay = 60000/$iBpm

While 1
    _Beep(1, 5, 20, $iDelay - 20, 1)
WEnd

_StopDSBeep()
_Quit()

Func _Quit()
    Exit
EndFunc

Thanks for the suggestions anyway. For some reason neither of your scripts are playing the wav file, and I don't know why. I will have to figure that out.

I also just noticed something wrong with my code. The bpm was wrong. It's close enough now that I have altered it.

Edited by czardas
Link to comment
Share on other sites

And exactly how is that easier than this?

I didn't think AdlibRegister called on an already existing function would have the same effect... I missed this line from the helpfile:

Re-registering an already existing Adlib function will update it with a new time.

Mat

Edit: Does this do it for you?

#include "DSBeep.au3"

Opt("MustDeclareVars", 1)

HotKeySet("{ESC}", "_Quit")

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")
Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc

_StartDSBeep()

Global $iBpm = 100
Global $iDelay = 60000/$iBpm

AdlibRegister("_click", $iDelay)

While 1
    Sleep(10)
WEnd

AdlibUnRegister()

_StopDSBeep()
_Quit()

Func _click()
    _Beep(1, 5, 20, 0, 1)
EndFunc

Func _Quit()
    Exit
EndFunc
Edited by Mat
Link to comment
Share on other sites

@czardas

Is that all? I was somehow expecting more. That could easily be fixed with TimerInit/TimerDiff, and if you still want to "zero" the cpu-usage, I recommend adding _HighPrecisionSleep.

Here's an example (look at the console to see how precise it is) :

#include "DSBeep.au3"

Opt("MustDeclareVars", 1)

HotKeySet("{ESC}", "_Quit")

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")
Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc

_StartDSBeep()

Local $iBpm = 100
Local $iDelay = 60000/$iBpm

Local $iTimer
While 1
    Do
        _HighPrecisionSleep2(500)
    Until TimerDiff($iTimer) > $iDelay -2   ;-2 is enough to compensate for most "hick-ups" in AutoIt (change it to -1 and notice the difference in the console output)
    Do
    Until TimerDiff($iTimer) > $iDelay  ;TimerDiff usually (?) return a float so there's no point in >=
    $iTimer = TimerInit()
    _Beep(1, 5, 10, 0, 0.5)
    ConsoleWrite(@MSEC & @CRLF)
WEnd

_StopDSBeep()
_Quit()

Func _Quit()
    Exit
EndFunc

Func _HighPrecisionSleep2($iMicroSeconds)       ;Based on Monoceres code in the forum.
    Static Local $hDLL = DllOpen("ntdll.dll")

    Local $hStruct
    $hStruct=DllStructCreate("int64 time")
    DllStructSetData($hStruct,"time",-1*($iMicroSeconds*10))
    DllCall($hDll,"dword","ZwDelayExecution","int",0,"ptr",DllStructGetPtr($hStruct))
EndFunc

Thanks for the suggestions anyway. For some reason neither of your scripts are playing the wav file, and I don't know why. I will have to figure that out.

I think that file was renamed in Vista or 7 so make sure the filename is right for your OS.

I also just noticed something wrong with my code. The bpm was wrong. It's close enough now that I have altered it.

You did this in the middle of writing my post so the code is based on what you originally posted. I'm sure you can adopt it to the new one (you only changed the _Beep?)
Link to comment
Share on other sites

Is that all? I was somehow expecting more. That could easily be fixed with TimerInit/TimerDiff, and if you still want to "zero" the cpu-usage, I recommend adding _HighPrecisionSleep.

Before I had several _beep calls in a row, and each had a different delay. The delay being a parameter of the _beep function. I'm sure I can adapt your code. I much appreciate you spending time on this. It looks like it could be very useful. I will have to look at it later though, because right now I have some other stuff to do.

(you only changed the _Beep?)

Yes, it was a trivial thing. Thanks again. :blink:
Link to comment
Share on other sites

I ran the following code against a steady beat, and once I got the two scripts in sync, they stayed that way for long enough to convince me. I have compensated for the 10 ms duration of the beep. I hope I got that right. => I just modified it again. ;)

#include "DSBeep.au3"

Opt("MustDeclareVars", 1)

HotKeySet("{ESC}", "_Quit")

Global $oError = ObjEvent("AutoIt.Error", "_ErrFunc")
Func _ErrFunc()
    ConsoleWrite("! COM Error !  Number: 0x" & Hex($oError.number, 8) & "   ScriptLine: " & $oError.scriptline & " - " & $oError.windescription & @CRLF)
    Return
EndFunc

_StartDSBeep()

Local $iBpm = 100
Local $iDelay = 60000/$iBpm

Local $iTimer
While 1

    Do
        _HighPrecisionSleep2(500)
    Until TimerDiff($iTimer) > $iDelay -12

    $iTimer = TimerInit()
    _Beep(1, 5, 10, 0, 0.5)

    Do
        _HighPrecisionSleep2(500)
    Until TimerDiff($iTimer) > $iDelay -12

    $iTimer = TimerInit()
    _Beep(1, 5, 10, 0, 0.5)

    Do
        _HighPrecisionSleep2(500)
    Until TimerDiff($iTimer) > $iDelay/2 -6

    $iTimer = TimerInit()
    _Beep(1, 5, 10, 0, 0.5)

    Do
        _HighPrecisionSleep2(500)
    Until TimerDiff($iTimer) > $iDelay/2 -6

    $iTimer = TimerInit()
    _Beep(1, 5, 10, 0, 0.5)

    Do
        _HighPrecisionSleep2(500)
    Until TimerDiff($iTimer) > $iDelay -12

    $iTimer = TimerInit()
    _Beep(1, 5, 10, 0, 0.5)

WEnd

_StopDSBeep()
_Quit()

Func _Quit()
    Exit
EndFunc

Func _HighPrecisionSleep2($iMicroSeconds)       ;Based on Monoceres code in the forum.
    Static Local $hDLL = DllOpen("ntdll.dll")

    Local $hStruct
    $hStruct=DllStructCreate("int64 time")
    DllStructSetData($hStruct,"time",-1*($iMicroSeconds*10))
    DllCall($hDll,"dword","ZwDelayExecution","int",0,"ptr",DllStructGetPtr($hStruct))
EndFunc

@ Mat Thanks for introducing me to Adlib. However I guess it would need to be re-registered repeatedly to accomodate the changes in duration, and that could be inconvenient.

The original discrepancies were small, but significant enough to disrupt a metronome. Although with a straight beat there seemed to be no issues. I may use preset rhythms, or I may include them later. These are only preliminary experiments, and preset rhythms are not really required for a basic metronome. I am however happy you showed me this: it increases my options. Excellent solution - thanks. :P

About windows ding. I haven't spent time on that. I'm sure I can fix it. The path seems fine. Unfortunately ding.wav is not so useful for a metronome. The sound lasts too long, and that means it will likely be audible. You know that you are truly in time when you don't hear the metronome. If you play out of time, then the annoying tick becomes audible because you are out of sync. :blink:

Edited by czardas
Link to comment
Share on other sites

@ Mat Thanks for introducing me to Adlib. However I guess it would need to be re-registered repeatedly to accomodate the changes in duration, and that could be inconvenient.

I thought so to, until AdmiralAlkex corrected me. Calling AdlibRegister on an already adlibbing function just changes the timer :blink:

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...