Reputation: 65
So, I wrote a little script to try out what's called a "microbreak" during my work. It's pretty simple, all it does is play a sound every random time between 20-30 minutes to notify me to take a break for a random range between 30 to 60 seconds. However, the winsound.Beep function is so infuriatingly inconsistent, or am I doing something wrong.
If I run python in the windows command prompt using "py", import winsound, and try a winsound.Beep it will not play, unless I run the command twice, only then the second time does it play.
If I turn what I described above into a python file and run it twice, the first time it runs, the only the second beep plays. The second time the file runs, both beeps play correctly??
In my script, its even worse. Sometimes it will work, sometimes it will skip, sometimes the duration is cut short. I literally have no idea why or how to recreate this.
I have python 3.10.7, Here is the code:
# Imports modules that are not accessible in the default scope
import time
import random
import winsound
import sys
import os
# Signals to the command prompt to use color mode
os.system('color')
# Function to achieve type-writer effect on print text
def typingPrint(text):
for character in text:
sys.stdout.write(character)
sys.stdout.flush()
time.sleep(0.01)
# Function to achieve type-writer effect on input text
def typingInput(text):
for character in text:
sys.stdout.write(character)
sys.stdout.flush()
time.sleep(0.01)
value = input()
return value
# Function that returns a colored ANSI code for coloring text in the command prompts
def rgb(r, g, b): return f"\u001b[38;2;{r};{g};{b}m"
# ANSI color codes
yellow = rgb(255, 255, 0)
pink = rgb(255, 50, 255)
cyan = rgb(0, 255, 255)
green = rgb(50, 255, 50)
red = rgb(255, 50, 50)
reset = "\u001b[0m"
# Intro prints for information on the script
typingPrint(f"Every {yellow}random time{reset} between {yellow}20-30 minutes{reset}, you will be given a {green}random microbreak{reset} between {green}30-60 seconds{reset}.\n\n")
time.sleep(2)
typingPrint(f"{yellow}Work{reset} time will {red}NOT{reset} be {pink}announced{reset}, but {green}microbreak{reset} time {cyan}will be{reset} {pink}announced{reset} each cycle.\n\n")
time.sleep(1)
typingPrint(f"This script will keep running until {yellow}Ctrl+C{reset} is clicked\n\n")
time.sleep(1)
typingPrint(f"IMPORTANT: {yellow}One beep{reset} to get back to {yellow}work{reset}, {green}two for break{reset}. There will also be {pink}prints{reset} along with them like this one.\n\n")
time.sleep(2)
# Prompts the user to start the script
typingInput(f"{yellow}Press any key once ready.{reset}\n\n")
# Times stats
total_work = 0
total_break = 0
total = 0
try:
while True:
# Work time code block
work_time = random.randint(20, 31) * 60 # The random range for work time
print(f"Time to {yellow}work{reset}!\n") # Print to notify work time began
winsound.Beep(500, 1000) # Beep to notify work time began
time.sleep(work_time) # Sleep (pause operations) for the entirety of work time
total_work += work_time # Add work time to total work time
# Microbreak time code block
microbreak_time = random.randint(30, 61) # The random range for microbreak time
print(f"Take a {green}microbreak{reset} for: {green}{microbreak_time}{reset}\n") # Print to notify microbreak time began
winsound.Beep(1000, 500) # Beep to notify microbreak time began
winsound.Beep(1000, 500) # Beep to notify microbreak time began
time.sleep(microbreak_time) # Sleep (pause operations) for the entirety of microbreak time
total_break += microbreak_time # Add microbreak time to total break time
total = total_work + microbreak_time # Sum the total time
# Stop the script if Ctrl+C is pressed.
except KeyboardInterrupt:
# Prints the total work, total break, and overall total times.
typingPrint(f"\n{red}Script was stopped{reset} using {yellow}Ctrl+C{reset}. Here is your stats:\t(Note: stats will only be counted once a full cycle was complete)\n")
typingPrint(f"Total {yellow}work{reset} time: {yellow}{round(total_work/60, 2)}{reset} minutes ({yellow}{round(total_work/3600, 2)}{reset} hours)\n")
typingPrint(f"Total {green}break{reset} time: {green}{round(total_break/60, 2)}{reset} minutes ({green}{round(total_break/3600, 2)}{reset} hours)\n")
typingPrint(f"Total {cyan}time{reset}: {cyan}{round(total/60, 2)}{reset} minutes ({cyan}{round(total/3600, 2)}{reset} hours)\n\n")
# Prompts the user to close the console by pressing any key
typingInput(f"{red}Press any key to close this console.{reset}")
Edit: If I use Powershell to play a beep instead of Python, the same thing seems to happen. The first time it's played, there is nothing that plays, but the second time it does. Maybe it's not Python's fault but I literally have no idea why this happens.
Upvotes: 1
Views: 742
Reputation: 31406
You indicated that the problem is that your audio playback device goes to sleep after a certain amount of time, to comply with energy preservation laws. There's probably not much you can do about that, but here's something to try to get around it.
Create a file that is just a few seconds of silence, called silence.wav
(I created 5 seconds, but any reasonable range should work).
Add the following to your code:
winsound.PlaySound('silence.wav', winsound.SND_FILENAME | winsound.SND_ASYNC | winsound.SND_LOOP)
Your Beep
calls will play over the top of this playback, but since the script is now technically constantly playing a sound, hopefully this prompts your device to remain active.
You may want to make sure the sound keeps playing in the background by first generating a tone instead of silence, and playing over the top of that. I'm not sure winsound
behaves the same everywhere, or for any device.
I used the free Audacity to generate a .wav with silence and a tone, in case you don't have the appropriate software.
Upvotes: 0