Quest for precise timing, Examples

Everything else that doesn't fall into one of the other PB categories.
vmars316
Enthusiast
Enthusiast
Posts: 474
Joined: Fri Jun 29, 2012 12:24 am
Contact:

Quest for precise timing, Examples

Post by vmars316 »

Hello & Thanks ,

I am trying to get an precise repeat rate of 41.6 milliseconds .
I Come fairly close , but "no cigar" .
I would like to get some help on how to do this .

Example A (SetTimer_) , gives a avg repeat rate of 40 milliseconds .
Example B (Elapsed + Delay) , gives a avg repeat rate of 46 milliseconds .
Example C (Elapsed + Delay) , gives a avg repeat rate of 46 milliseconds .

I didn't try AddWindowTimer , because Help said:
"Timers are therefore not suited for precise timing " .

Example A :

Code: Select all

; output average = 40 

Global toggleColor.i , LastElapsedMilliseconds , 
       totalTimesThru.i , totalElapsedTimes.i = 0 , avgElapsedTime.i ,
       currentElapsedMilliseconds.i , calcElapsedMilliseconds , STOP = #False

#PROGRESS_TIME = 3500
OpenWindow(1, 0, 0, 300, 100, "Timed Progress...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ProgressBarGadget(1, 10, 10, 280, 50, 0, #PROGRESS_TIME / 10, #PB_ProgressBar_Smooth)
Time = ElapsedMilliseconds()
LastElapsedMilliseconds = ElapsedMilliseconds()

Repeat
  If GetGadgetState(1) < #PROGRESS_TIME / 10 ; returns the current value of the ProgressBar.
    totalTimesThru = totalTimesThru + 1  
    currentElapsedMilliseconds = ElapsedMilliseconds()
    calcElapsedMilliseconds = ( currentElapsedMilliseconds - LastElapsedMilliseconds ) 
    totalElapsedTimes = totalElapsedTimes + calcElapsedMilliseconds
    SetGadgetState(1, GetGadgetState(1) + 1) ; increment progressBar  
    Debug "ElapsedMilliseconds() " + Str( calcElapsedMilliseconds )
    LastElapsedMilliseconds = ElapsedMilliseconds()
    Delay(41)
  Else 
    Debug "Time took to fill gadget: " + Str(ElapsedMilliseconds() - Time)
    avgElapsedTime = totalElapsedTimes / totalTimesThru -1 ;  because 1st time thru = 0
    Debug "avgElapsedTime = " + Str(avgElapsedTime)  
    MessageRequester("avgElapsedTime" , "avgElapsedTime = " + avgElapsedTime + " milliSeconds")
    STOP = #True 
  EndIf
  
Until STOP
Example B:

Code: Select all

; output average = 46 

Global Window_0 = 0 , Container_0, Button_0
Global toggleColor.i , LastElapsedMilliseconds , 
       totalTimesThru.i , totalElapsedTimes.i , avgElapsedTime.i ,
       currentElapsedMilliseconds.i , calcElapsedMilliseconds

If  OpenWindow(Window_0, 0, 0, 500, 240, "", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  Container_0 = ContainerGadget(#PB_Any, 10, 10, 310, 125)
  SetGadgetColor(Container_0 , #PB_Gadget_BackColor , $FF0000)
  Button_0 = ButtonGadget(#PB_Any, 60, 70, 100, 25, "")
  CloseGadgetList()
  
  LastElapsedMilliseconds = ElapsedMilliseconds()
  toggleColor = 1
  
  Repeat  
    Event = WaitWindowEvent()
    totalTimesThru = totalTimesThru +1  
    currentElapsedMilliseconds = ElapsedMilliseconds()
    calcElapsedMilliseconds = ( currentElapsedMilliseconds - LastElapsedMilliseconds ) 
    If calcElapsedMilliseconds > 0 ; And LastElapsedMilliseconds <> 0
       Debug Str(calcElapsedMilliseconds)
       totalElapsedTimes = totalElapsedTimes + calcElapsedMilliseconds
       If (Mod(toggleColor, 2) <> 0 )
          SetGadgetColor(Container_0 , #PB_Gadget_BackColor , $00FF00)
          LastElapsedMilliseconds = ElapsedMilliseconds()   
       EndIf 
       If (Mod(toggleColor, 2)  = 0 )
          SetGadgetColor(Container_0 , #PB_Gadget_BackColor , $FF0000)
          LastElapsedMilliseconds = ElapsedMilliseconds()   
       EndIf 
       toggleColor = toggleColor + 1 
    EndIf  ;  
    Delay(41)
     
     Until Event = #PB_Event_CloseWindow
     
     avgElapsedTime = totalElapsedTimes / totalTimesThru 
     MessageRequester("avgElapsedTime" , "avgElapsedTime = " + avgElapsedTime + " milliSeconds")
EndIf

End   
Example C:

Code: Select all

; output average = 40 

Global toggleColor.i , LastElapsedMilliseconds , 
       totalTimesThru.i , totalElapsedTimes.i = 0 , avgElapsedTime.i ,
       currentElapsedMilliseconds.i , calcElapsedMilliseconds , STOP = #False

#PROGRESS_TIME = 3500
OpenWindow(1, 0, 0, 300, 100, "Timed Progress...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ProgressBarGadget(1, 10, 10, 280, 50, 0, #PROGRESS_TIME / 10, #PB_ProgressBar_Smooth)
Time = ElapsedMilliseconds()
LastElapsedMilliseconds = ElapsedMilliseconds()

Repeat
  If GetGadgetState(1) < #PROGRESS_TIME / 10 ; returns the current value of the ProgressBar.
    totalTimesThru = totalTimesThru + 1  
    currentElapsedMilliseconds = ElapsedMilliseconds()
    calcElapsedMilliseconds = ( currentElapsedMilliseconds - LastElapsedMilliseconds ) 
    totalElapsedTimes = totalElapsedTimes + calcElapsedMilliseconds
    SetGadgetState(1, GetGadgetState(1) + 1) ; increment progressBar  
    Debug "ElapsedMilliseconds() " + Str( calcElapsedMilliseconds )
    LastElapsedMilliseconds = ElapsedMilliseconds()
    Delay(41)
  Else 
    Debug "Time took to fill gadget: " + Str(ElapsedMilliseconds() - Time)
    avgElapsedTime = totalElapsedTimes / totalTimesThru -1 ;  because 1st time thru = 0
    Debug "avgElapsedTime = " + Str(avgElapsedTime)  
    MessageRequester("avgElapsedTime" , "avgElapsedTime = " + avgElapsedTime + " milliSeconds")
    STOP = #True 
  EndIf
  
Until STOP
Thanks
vmars.us Win11 x64 , Martin Guitar 000-16 (1995)
"All things in moderation , except for love and forgiveness."
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: Quest for precise timing, Examples

Post by Shield »

ElapsedMilliseconds() is AFAIK only accurate to about 16ms by default.
For accurate timing you probably want to use QueryPerformanceCounter():
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

There are lots of examples on the forum on how to use this function. :)

__________________________________________________
URL tags added
16.03.2017
RSBasic
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Quest for precise timing, Examples

Post by Josh »

Windows is not a real-time operating system and no one can guarantee that a timer works correctly.

But maybe the following code can help you:

Code: Select all

Procedure.q ElapsedMicroseconds()
  Define Frequency.q
  Define Time     .q

  QueryPerformanceFrequency_(@Frequency)
  QueryPerformanceCounter_  (@Time)

  ProcedureReturn Time * 1000000 / Frequency

EndProcedure

time.q = ElapsedMicroseconds()
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Delay (100)
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
Debug Str (ElapsedMicroseconds() - time) + " µs"
5 µs
42 µs
52 µs
62 µs
72 µs
81 µs
100472 µs
100500 µs
100510 µs
100519 µs
100530 µs
100540 µs
sorry for my bad english
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Quest for precise timing, Examples

Post by nco2k »

iirc, ElapsedMilliseconds() doesnt use GetTickCount_() anymore, but QueryPerformanceCounter_() instead. the return value is a quad now. so there is no need to write your own function, unless you need the output to be in microseconds.

but yea, a native cross platform ElapsedMicroseconds() function would be nice.

c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
Fred
Administrator
Administrator
Posts: 16686
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Quest for precise timing, Examples

Post by Fred »

ElapsedMilliseconds() should be precise on all OS now as we use PerformanceCounter on Windows and gettimeofday() on Linux/OSX which both have microsecond support. But yes, non of these OS are realtime, so you could approach precise timing but not reach it
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Quest for precise timing, Examples

Post by IdeasVacuum »

If you are writing an app for Windows only, you can elevate it's priority:

Code: Select all

;Elevate the system priority of this app
SetPriorityClass_(GetCurrentProcess_(), #ABOVE_NORMAL_PRIORITY_CLASS)
SetThreadPriority_(GetCurrentThread_(), #THREAD_PRIORITY_HIGHEST)
The options are the same as those presented to the User via the Task Manager:
Priority Class:
#IDLE_PRIORITY_CLASS
#BELOW_NORMAL_PRIORITY_CLASS
#NORMAL_PRIORITY_CLASS
#ABOVE_NORMAL_PRIORITY_CLASS
#HIGH_PRIORITY_CLASS
#REALTIME_PRIORITY_CLASS

Priority Level, within each priority class:
#THREAD_PRIORITY_IDLE
#THREAD_PRIORITY_LOWEST
#THREAD_PRIORITY_BELOW_NORMAL
#THREAD_PRIORITY_NORMAL
#THREAD_PRIORITY_ABOVE_NORMAL
#THREAD_PRIORITY_HIGHEST
#THREAD_PRIORITY_TIME_CRITICAL

See advice on usage: https://msdn.microsoft.com/en-us/librar ... s.85).aspx
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Re: Quest for precise timing, Examples

Post by djes »

If you're using REALTIME_PRIORITY_CLASS, don't forget that the OS will not be able to handle keyboard events, as in this old example : http://www.purebasic.fr/english/viewtop ... 12&t=36852
vmars316
Enthusiast
Enthusiast
Posts: 474
Joined: Fri Jun 29, 2012 12:24 am
Contact:

Re: Quest for precise timing, Examples

Post by vmars316 »

Ok Thanks All ,
I've got lots of research/tinkering to do :)
I'll report back when I make some headway .
vmars.us Win11 x64 , Martin Guitar 000-16 (1995)
"All things in moderation , except for love and forgiveness."
SeregaZ
Enthusiast
Enthusiast
Posts: 619
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: Quest for precise timing, Examples

Post by SeregaZ »

is any new ideas at 2019? i need 125 or 126 microsecunds. and it is passible to make some wav playing by sending per 1 byte with this timer? it is 8k frequency of wav (raw - becouse without head) file. 1000 millisec / 8000 = 0.125 millisec = 125 microsecundes.

yes. i know - it have a lot of ways to play wav... but i want to test one of theory for one my friend. he try to make romhack for Rock n Roll Racing for Sega Mega Drive. so problem is - when Larry says his phrases - song is paused. he finish saying - song continue. one team of romhackers - just replace audio driver... but they replace original music too. we want to keep original music. so it is two ways. one of them - convert original music to GEMS audio driver and replace driver in a game to GEMS, but that converting will be not very accurate - becouse GEMS have 1/24 note as smallest, but songs of that game have 1/48. it can be increased temp, but original temp is too high = 150. for GEMS it will need 300, but it have 255 as max. second way is - make some changes to original driver of RRR. that changes have two path - my friend's - he try to use part of GEMS driver into this one. part, where samples plays. i want make another idea - just a little part. use another timer, with that 125 microseconds and counter - where will count 128 tiks with 125 microseconds. usualy with 125 ms pauses it will check need to send some samples to register $2A or not. and when it will count 128 - run that code, where plays FM part. in that FM code at a few places - i will add call timer checker and run samples playing, i mean 1 byte sending to play. so first i want to create it with PB. then probably it will be transfered to z80. and that code need to be done with 1 thread. or 2 - 1 window for PC, 2 play procedure. or without window...
SeregaZ
Enthusiast
Enthusiast
Posts: 619
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: Quest for precise timing, Examples

Post by SeregaZ »

i am trying to write bytes one by one into sndPlaySound, that have loop... but it something wrong :) maybe i am need some another flags for sndPlaySound?

Code: Select all

Enumeration
  #File
EndEnumeration

Procedure.q ElapsedMicroseconds()
  Define Frequency.q
  Define Time     .q

  QueryPerformanceFrequency_(@Frequency)
  QueryPerformanceCounter_  (@Time)

  ProcedureReturn Time * 1000000 / Frequency

EndProcedure

Procedure WavHeaderCreation(*memst)
  
 ;RIFF
 PokeB(*memst, $52):PokeB(*memst+1, $49):PokeB(*memst+2,$46):PokeB(*memst+3, $46)
    
 ;WAVE
 PokeB(*memst+8, $57):PokeB(*memst+9, $41):PokeB(*memst+10,$56):PokeB(*memst+11, $45)
    
 ;fmt
 PokeB(*memst+12, $66):PokeB(*memst+13, $6d):PokeB(*memst+14,$74):PokeB(*memst+15, $20) 
    
 ;header size
 PokeB(*memst+16, $10)
    
 ;PCM 01
 PokeB(*memst+20, $01)
    
 ;mono stereo
 PokeB(*memst+22, $01)
    
 ;1
 PokeB(*memst+32, $01)
    
 ;bit
 PokeB(*memst+34, $08)
        
 ;data    
 PokeB(*memst+36, $64)
 PokeB(*memst+37, $61)
 PokeB(*memst+38, $74)
 PokeB(*memst+39, $61)
  
EndProcedure

time.q
length.l
value.a

*snddest = AllocateMemory(1+44)

WavHeaderCreation(*snddest)

;size
PokeL(*snddest +  4, 1+40)
PokeL(*snddest + 40, 1+40)
;kbs
PokeL(*snddest + 24, 8000)
PokeL(*snddest + 28, 8000)

sndPlaySound_(*snddest,#SND_MEMORY | #SND_ASYNC | #SND_LOOP | #SND_NODEFAULT)

If ReadFile(#File, "larry\larry2.snd")
  
  length = Lof(#File)                        ; get the length of opened file
  *MemoryID = AllocateMemory(length)         ; allocate the needed memory
  If *MemoryID
    ReadData(#File, *MemoryID, length)   ; read all data into the memory block
  EndIf
  
  CloseFile(#File)
  
  ; aderess where to write after header
  towrite = *snddest + 44
  
  For i = 0 To length
    
    timer = 0 ; reset timer
    
    value = PeekA(*MemoryID + i)
    
    PokeA(towrite, value) ;: Debug PeekA(towrite)
  
    time = ElapsedMicroseconds()

    Repeat
      timer = ElapsedMicroseconds() - time
    Until timer > 125  
  
  Next

Else
  
  Debug "nofile"

EndIf



sample:
https://www.dropbox.com/s/a5n4cnbq0690f ... 2.snd?dl=1
Post Reply