Read specific key with msvcrt.getch() OR move on after set time with no input

32 views Asked by At

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

0

There are 0 answers