czardas Posted July 5, 2010 Share Posted July 5, 2010 (edited) 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 July 5, 2010 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
Mat Posted July 5, 2010 Share Posted July 5, 2010 Using Adlib should help i think... Unless you are using that already. AutoIt Project Listing Link to comment Share on other sites More sharing options...
czardas Posted July 5, 2010 Author Share Posted July 5, 2010 (edited) 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. Edited July 5, 2010 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
Mat Posted July 5, 2010 Share Posted July 5, 2010 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 AutoIt Project Listing Link to comment Share on other sites More sharing options...
AdmiralAlkex Posted July 5, 2010 Share Posted July 5, 2010 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 .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface Link to comment Share on other sites More sharing options...
czardas Posted July 5, 2010 Author Share Posted July 5, 2010 (edited) 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. #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 EndFuncThanks 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 July 5, 2010 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
Mat Posted July 5, 2010 Share Posted July 5, 2010 (edited) 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 July 5, 2010 by Mat AutoIt Project Listing Link to comment Share on other sites More sharing options...
AdmiralAlkex Posted July 5, 2010 Share Posted July 5, 2010 @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) : expandcollapse popup#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?) .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface Link to comment Share on other sites More sharing options...
czardas Posted July 5, 2010 Author Share Posted July 5, 2010 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. operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
czardas Posted July 6, 2010 Author Share Posted July 6, 2010 (edited) 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. expandcollapse popup#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. 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. Edited July 6, 2010 by czardas operator64 ArrayWorkshop Link to comment Share on other sites More sharing options...
Mat Posted July 6, 2010 Share Posted July 6, 2010 @ 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 AutoIt Project Listing Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now