I'm trying to write a python based metronome with librosa and sounddevice but I've came across some problems with it's accuracy. Here's the code:
from time import sleep, perf_counter
import librosa
import sounddevice as sd
bpm = 200
delay = 60/bpm
tone = librosa.tone(440, sr=22050, length=1000)
try:
while True:
sd.play(tone, 22050)
sleep(delay)
except KeyboardInterrupt:
pass
First of all, the upper limit for properly functioning metronome seems to be around 180bpm - if you set the bpm to be any higher then 200bpm then there is no sound produced. In slower tempos I can hear that metronme is not so consistent as it should be with spacing in between the clicks. I've runned the script from this topic and my results were pretty poor compared to the author of this answer(which was using "old single core 32 bit 2GHz machine" against my six-core 3.9GHz 64bit windows running):
150.0 bpm
+0.007575200
+0.006221200
-0.012907700
+0.001935400
+0.002982700
+0.006840000
-0.009625400
+0.003260200
+0.005553100
+0.000668100
-0.010895100
+0.017142500
-0.012933300
+0.001465200
+0.004203100
+0.004769100
-0.012183100
+0.002174500
+0.002301000
-0.001611100
So i wonder if my metronome problems are somehow correlated to these poor results and what I can do to fix it.
The second problem that I encounter is the way in which the metronome is switched off - I want it to be running up until the point where the user inputs a specific button, or in my case(no GUI) a specific value from the keyboard - let's say the space key. So as you can see now it works only with ctrl + c, but I have no idea how to implement interrupt with a specified key.
Running your code on a mac, the timing inconsistencies are noticeable but also the tempo was of quite a bit off from the set bpm.
This is mostly because sleep() isn't that accurate, but also because you have to account for the time that has elapsed since the last event. e.g. how much time did it take to call sd.play()
I don't know on what operating system you did run this, but most operating systems have a special timer for precise callbacks (e.g. Multimedia Timers on Windows). if you don't want a platform specific solution to improve the timing you could do a "busy wait" instead on sleep(). To do this you could sleep for have the delay, and then go into a loop where you constantly check the time elapsed.
lastTime = perf_counter()
while True:
currentTime = perf_counter()
delta = abs(lastTime - currentTime)
sleep(delay / 2.0)
while True:
currentTime = perf_counter()
if (currentTime - lastTime >= delay):
sd.play(tone, 22050)
lastTime = currentTime
break
Not a perfect solution but it'll get you closer.
You can further optimise the fraction of the delay that is spent sleeping to take load of the CPU.
Related
I'm an amateur coder. I'm working on a small little game for a project in biology, but I have come across an issue in my code. I have a loop that adds +1 to the variable sunlight every two seconds. However, all code below the loop is non-functional now that I have made the loop. I'm guessing it's because it's waiting for the loop to finish. Any way to have the loop always run but allow the code to run through it's sequence at the same time?
print("Game started!")
sunlight = 0
while True:
time.sleep(2)
sunlight += 1
commands = input("Type stats to see which molecules you have, type carbon to get carbon\ndioxide, and type water to get water: ")
if commands == ("stats"):
print("Sunlight: ",sunlight,"")
As you are beginner, i would not recommend to use multithreading or asyncio. Instead just start the time and when user enter "stats", elapsed time//2 will be equal to sunlight.
import time
start_time = time.time()
while True:
commands = input("Type stats to see which molecules you have, type carbon to get carbon\ndioxide, and type water to get water: ")
if commands == ("stats"):
sunlight = (time.time()-start_time)//2 # elapsed time // 2
print("Sunlight: ", sunlight, "")
Your sunlight variable basically functions as a clock; it counts half of the number of seconds since the program begins. Rather than implement your own clock using time.sleep(), it's better to just use an existing clock from the time library.
The function time.monotonic returns a number of seconds, so you can use this to get the current sunlight by saving the start time, then each time you want to know the value of sunlight, take the difference between the current time and the start time, divided by 2.
start_time = time.monotonic()
def get_sunlight():
current_time = time.monotonic()
return int(current_time - start_time) // 2
It is better to use the monotonic() function than the clock() function for this purpose, since the clock() function is deprecated as of Python 3.3:
The time.clock() function is deprecated because it is not portable: it behaves differently depending on the operating system.
It's also better than the time() function for this purpose, because changes to the system clock (such as going forwards or back due to daylight savings time) will affect the result of time():
While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls.
You should look into the multithreading library. That's probably a good resource. You can fire off a thread running your sunlight incrementer that updates a global variable (not a good idea but you seem to have just 1 writer, so you can get by till you have time to pick up more advanced parallel processing concepts).
Reference: https://www.geeksforgeeks.org/multithreading-python-set-1/
In a python script, the response time for pyautogui.moveRel(b, a) is currently .1 sec. Is that the intended time for this function? If so, is there any way I can shorten this response time using another technique in python, or other languages? My goal response time is about 0.01 sec, which is the 60HZ rate.
The results #mertyildiran obtained are to be expected, since PyAutoGUI has a default 0.1 second pause between actions as a failsafe.
To speed things up, you can set the pause to a lower value, or even zero, like so:
pyautogui.PAUSE = 0
The code for checking the response time in microseconds(0.01 seconds = 10000 microseconds):
import pyautogui
import datetime
for x in range(0, 9):
start = datetime.datetime.now()
pyautogui.moveRel(None, 50)
end = datetime.datetime.now()
elapsed_time = end - start
print elapsed_time.microseconds
Output:
269017
112927
113071
113061
112761
112561
113115
112107
112448
That means yeah it's approximately 0.1 seconds with this library and Python. So my recommendation is go with a deeper level programming language like C++.
Stimulate mouse event with C++ in Windows: https://stackoverflow.com/a/7492837/2104879
Stimulate mouse event with C++ in Linux: https://stackoverflow.com/a/8791599/2104879
I'm working on a laser tag game project that uses pygame and Raspberry Pi. In the game, I need a background timer in order to keep track of game time. Currently I'm using the following to do this but doesnt seem to work correctly:
pygame.timer.get_ticks()
My second problem is resetting this timer when the game is restarted. The game should restart without having to restart the program and that is only likely to be done with resetting the timer, I guess.
What I need, in brief, is to have a background timer variable and be able to reset it any time in a while loop.
I'm a real beginner to python and pygame, but the solution of this problem will give a great boost to my knowledge and the progress of the project. Any help will be greately appreciated.
You don't necessarily need Pygame for this -- time.time() should work just as well as pygame.time.get_ticks(), though it reports seconds since the Unix Epoch instead of milliseconds since the Pygame was initialized.
You want to measure the time has elapsed since the last reset (or since the game started, which you can think of as the first reset). The two functions you've got both return the time elapsed since some arbitrary reference point. The simplest way to achieve this to keep a variable t0. This variable will hold the time value at the last reset (or game start).
Here's an example that loops infinitely and constantly prints the value of such a timer, resetting it whenever it reaches 3 seconds:
# import the builtin time module, this is always available
import time
# initialize the t0 variable, "starting the stopwatch"
t0 = time.time()
while True:
# calculate the time since some reference point (here the Unix Epoch)
t1 = time.time()
# calculate the difference, i.e. the time elapsed
dt = t1 - t0
if dt >= 3:
print "Three seconds reached, resetting timer"
t0 = t1
else:
print "Time elapsed is", dt, "seconds"
If you want to get to know the object-oriented features of Python, this is a good opportunity for an exercise. The above behaviour can be neatly encapsulated in a Stopwatch class, e.g. with get_seconds() and reset() methods.
If you write such a class, your high-level timing logic code can be very simple and readable:
my_timer = Stopwatch()
print my_timer.get_seconds(), "seconds have elapsed since start/reset"
my_timer.reset()
The details of the underlying implementation are hidden away in the code for the Stopwatch class.
After pygame.init() is called the pygame timer starts.
So suppose you run your program once and then start making different game sessions in the same run then you can use a reference variable to keep track of timer and resetting it.
Example:
#your game
import pygame
from pygame import time as T
#do your stuffs
pygame.init()
while True: #this loop never ends and you want to reset timer in this loop if I am not wrong.
if(new_session):
tim_var=T.get_ticks()
new_session=0
#do your stuffs
print 'time elaspsed in this session upto now '+str(T.get_ticks()-tim_var)+' millisecs'
if(game_ended):
new_session=1
#restart_your_new_game by resetting your variables or whatever
I need to wait for about 25ms in one of my functions. Sometimes this function is called when the processor is occupied with other things and other times it has the processor all to itself.
I've tried time.sleep(.25) but sometimes its actually 25ms and other times it takes much longer. Is there a way to sleep for an exact amount of time regardless of processor availability?
Because you're working with a preemptive operating system, there's no way you can guarantee that your process will be able to have control of the CPU in 25ms.
If you'd still like to try, it would be better to have a busy loop that polls until 25ms has passed. Something like this might work:
import time
target_time = time.clock() + 0.025
while time.clock() < target_time:
pass
0.25 seconds are 250 ms, not 25. Apart from this, there is no way to wait for exactly 25 ms on common operating systems – you would need some real-time operating system.
What system are you on? If you're on Windows you may want to do something like this for exact timing:
import ctypes
kernel32 = ctypes.windll.kernel32
# This sets the priority of the process to realtime--the same priority as the mouse pointer.
kernel32.SetThreadPriority(kernel32.GetCurrentThread(), 31)
# This creates a timer. This only needs to be done once.
timer = kernel32.CreateWaitableTimerA(ctypes.c_void_p(), True, ctypes.c_void_p())
# The kernel measures in 100 nanosecond intervals, so we must multiply .25 by 10000
delay = ctypes.c_longlong(.25 * 10000)
kernel32.SetWaitableTimer(timer, ctypes.byref(delay), 0, ctypes.c_void_p(), ctypes.c_void_p(), False)
kernel32.WaitForSingleObject(timer, 0xffffffff)
This code will pretty much guarentee your process will sleep .25 seconds. Watch out though- you may want to lower the priority to 2 or 3 unless it's absolutely critical that this sleeps for .25 seconds. Certainly don't change the priority too high for a user-end product.
Edit: in Windows 10 this nonsense seems unnecessary. Try it like so:
>>> from time import sleep
>>> import timeit
>>> '%.2f%% overhead' % (timeit.timeit('sleep(0.025)', number=100, globals=globals()) / 0.025 - 100)
'0.29% overhead'
.29%, or thereabout, is fairly low overhead, and usually more than accurate enough.
Previous Windows versions will by default have a sleep resolution of 55 msecs, which means your sleep call will take somewhere between 25 and 55 msecs. To get the sleep resolution down to 1 millisecond you need to set the resolution used by Windows by calling timeBeginPeriod:
import ctypes
winmm = ctypes.WinDLL('winmm')
winmm.timeBeginPeriod(1)
Another solution for accurate timings and delay is to use the perf_counter() function from module time. Especially useful in windows as time.sleep is not accurate in milliseconds. See below example where function accurate_delay creates a delay in millisecond.
import time
def accurate_delay(delay):
''' Function to provide accurate time delay in millisecond
'''
_ = time.perf_counter() + delay/1000
while time.perf_counter() < _:
pass
delay = 10
t_start = time.perf_counter()
print('Wait for {:.0f} ms. Start: {:.5f}'.format(delay, t_start))
accurate_delay(delay)
t_end = time.perf_counter()
print('End time: {:.5f}. Delay is {:.5f} ms'.
format(t_end, 1000*(t_end - t_start)))
sum = 0
ntests = 1000
for _ in range(ntests):
t_start = time.perf_counter()
accurate_delay(delay)
t_end = time.perf_counter()
print('Test completed: {:.2f}%'.format(_/ntests * 100), end='\r', flush=True)
sum = sum + 1000*(t_end - t_start) - delay
print('Average difference in time delay is {:.5f} ms.'.format(sum/ntests))
What you intend to do is a real time application. Python (and probably the OS you are using) is not intended to program this kind of applications, where time restriction is so strict.
In order for you to achieve what you are looking for you need a RTOS (Real Time Operating System) and develop your application using a suitable programming language (usually C) following RT best practises.
From the docs of the sleep method:
Suspend execution for the given number of seconds. The argument may be
a floating point number to indicate a more precise sleep time. The
actual suspension time may be less than that requested because any
caught signal will terminate the sleep() following execution of that
signal’s catching routine. Also, the suspension time may be longer
than requested by an arbitrary amount because of the scheduling of
other activity in the system.
The fact is that it depends on your underlying OS.
I ran following script on different machine and got quite different results. The elapsed time.clock() is so large.
Script:
#------------------------------------------------------------------------------------
import time
start_clock = time.clock()
time.sleep(60)
end_clock = time.clock()
print "Sleep Clock = ", str(end_clock - start_clock)
start_time = time.time()
time.sleep(60)
end_time = time.time()
print "Sleep Time = ", str(end_time - start_time)
#-------------------------------------------------------------------------------------
Output:
Instance (Windows Server 2008, X64):
Sleep Clock = 938.306471633
Sleep Time = 60.0119998455
Local Machine (Windows Vista, X86):
Sleep Clock = 59.9997987873
Sleep Time = 59.996999979
Following result really confused me:
Sleep Clock = 938.306471633
P.s:
I have not tested on other X64 OSs. This Windows Server 2008 is a running Amazon Instance.
Per the docs on time.clock
On Windows, this function returns
wall-clock seconds elapsed since the
first call to this function, as a floating point number, based on the Win32 function QueryPerformanceCounter().
so my (blind, i.e., I've never seen Amazon's code for Windows virtualization!-) guess would be that Amazon's virtualization doesn't go quite deep enough to trick QueryPerformanceCounter (which is a very low-level, low-overhead function). Tricking time.time (in a virtualizing hypervisor) is easier (and a more common need).
Do you know what happens e.g. on Microsoft's Azure, and with other non-Microsoft virtualizers such as Parallels or VMWare? I wouldn't be surprised to see different "depth" to the amount of "trickery" (deep virtualization) performed in each case. (I don't doubt that the explanation for this observation must have to do with virtualization, although the specific guess I make above could be flawed).
It would also be interesting to try (again, on various different virtualizers) a tiny C program doing just QueryPerformanceCounter, just to confirm that Python's runtime has nothing to do with the case (I believe so, by inspection of the runtime's source, but a confirmation could not hurt -- unfortunately I don't have access to the resources needed to try it myself).