I have some horribly inelegant code I've written (I know I could do a lot of optimisation here, but I started writing this without a clear plan so it is what it is). Basically, I have a Bluetooth button emulating keystrokes that sends the numbers 1-8 (just 1-2 for this example). If my program receives an initial press, it starts a waiting period then turns on an LED (with capslock) in the button. If the button is pressed again while the LED is on (within 2 seconds), it plays a sound and records the number. If no key is pressed within the LED window, I want the code to record a 0 in several places. Every part of my code runs as expected until I let the LED timer run out without a key press, at which point the program breaks.
# Packages
import msvcrt
import time
import sounddevice as sd
import soundfile as sf
import csv
import keyboard
from datetime import datetime
import pyautogui
import re
# Settings
PLAY_TIME = 20 # in seconds
HEADPHONES_FOR_P1 = 3 # Should be J22 with 2 output channels, no input channels (7)
HEADPHONES_FOR_P2 = 3 # Should be 2-J22 with 2 output channels, no input channels (5)
FILE_NAME = datetime.today().strftime('%Y%m%d%H')
FORCED_BREAK_LENGTH = 2 # in seconds
LED_ACTIVE_LENGTH = 2 # in seconds
# Sound
sound_file_1 = 'C:/Users/am650/Desktop/Audio/aaa_1.mp3'
sound_file_2 = 'C:/Users/am650/Desktop/Audio/aaa_2.mp3'
blast_file_1, fs1 = sf.read(sound_file_1, dtype='float32')
blast_file_2, fs2 = sf.read(sound_file_2, dtype='float32')
# Set Up
time_start = time.perf_counter()
time_stop = time.perf_counter()
time_elapsed = time_stop - time_start
counter = 0
save_files = []
# Basic Logic
while time_elapsed < PLAY_TIME:
initial_blast_encode = msvcrt.getch()
time_in = time.perf_counter()
time_initial_hit = time_in - time_start
if initial_blast_encode in [b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8']:
initial_blast_decode = re.sub("[^0-9]", '', str(initial_blast_encode))
for i in range(150):
keyboard.block_key(i)
time.sleep(FORCED_BREAK_LENGTH)
for i in range(150):
keyboard.unblock_key(i)
pyautogui.press('capslock')
while time_elapsed < LED_ACTIVE_LENGTH:
blast_encode = 'none'
blast_encode = msvcrt.getch()
if blast_encode == b'1':
player_id = 1
blast_decode = 1
time_in = time.perf_counter()
time_to_button_press = time_in - time_start
for i in range(150):
keyboard.block_key(i)
sd.play(blast_file_1, device=HEADPHONES_FOR_P2)
status = sd.wait()
for i in range(150):
keyboard.unblock_key(i)
time_in = time.perf_counter()
time_to_blast_end = time_in - time_start
counter += 1
save_files.append([counter, player_id, initial_blast_decode, blast_decode,
time_initial_hit, time_to_button_press, time_to_blast_end])
pyautogui.press('capslock')
break
elif blast_encode == b'2':
player_id = 1
blast_decode = 2
time_in = time.perf_counter()
time_to_button_press = time_in - time_start
for i in range(150):
keyboard.block_key(i)
sd.play(blast_file_2, device=HEADPHONES_FOR_P2)
status = sd.wait()
for i in range(150):
keyboard.unblock_key(i)
time_in = time.perf_counter()
time_to_blast_end = time_in - time_start
counter += 1
save_files.append([counter, player_id, initial_blast_decode, blast_decode,
time_initial_hit, time_to_button_press, time_to_blast_end])
pyautogui.press('capslock')
break
elif time.perf_counter() - time_start > LED_ACTIVE_LENGTH and blast_encode == 'none':
counter += 1
player_id = 1
blast_decode = 0
time_to_blast_end = 0
time_to_button_press = 0
save_files.append([counter, player_id, initial_blast_decode, blast_decode,
time_initial_hit, time_to_button_press, time_to_blast_end])
pyautogui.press('capslock')
break
else:
print("Invalid Entry")
if time.perf_counter() - time_start > PLAY_TIME:
break
if time.perf_counter() - time_start > PLAY_TIME:
break
# Write .csv
with open(f"C:/Users/am650/Desktop/fubp_data/{FILE_NAME}.csv", "w", newline='') as csvfile:
datawriter = csv.writer(csvfile, delimiter=',')
for row in save_files:
datawriter.writerow(row)
# End of Script
Because I set blast_encode to 'none' before checking for an input, and then check for
elif time.perf_counter() - time_start > LED_ACTIVE_LENGTH and blast_encode == 'none'
I expected the code to use this above elif statement should no key be pressed within the LED time window, but instead it just stays stuck in the while time_elapsed < LED_ACTIVE_LENGTH and then start glitching out. I realise now that the code has to wait for an msvcrt.getch() input before continuing, but I don't know how to get around that as putting msvcrt.getch() directly in my if statment crashes the code.
I have also tried solving this problem with msvcrt.kbhit(), but I cannot figure out how to pass on the fact that a key was pressed AND the actual key pressed which means that you'd have to press the button twice within the LED window for a sound to play (which will not work for my purposes). I am open to any suggestions about how to make my program work, whether by adapting what I have above or finding a way to use msvcrt.kbhit() instead.
Windows 11, Python 3.11.0
Thank you all in advance