T1 = clock.time() import pyaudio import struct import math import wave import pandas as pd import numpy as np Trial_nbr = count_trial_sequence +1 # number of standard deviations above the mean (e.g. 1.5 x SD) taken as the threshold for voice onset detection - CHANGE IF MICROPHONE WAS NOT CLOSE ENOUGH OR DETECTION SETTING WAS NOT HIGH ENOUGH nSD=1 # percentage of the peak signal in a file (e.g. 0.01 = 1 % of peak signal) taken as the minimal threshold value allowed minTH=0.005 # Maximum response time - recording duration timeout = 2000 # Parameters on the PyAudio object - do not change FORMAT = pyaudio.paInt16 SHORT_NORMALIZE = (1.0/32768.0) CHANNELS = 2 RATE = 44100 INPUT_BLOCK_TIME = 0.01 INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME) # Path where sound files will be saved FILENAME = experiment_path + "\\" + str(subject_nr) + "\\"+ str(Trial_nbr) + ".wav" CHUNCK=1024 p = pyaudio.PyAudio() # Definition of the function to process sound loundness def get_rms(block): """Get root mean square as a measure of loudness""" count = len(block)/2 format = "%dh" % (count) shorts = struct.unpack( format, block ) sum_squares = 0.0 for sample in shorts: n = sample * SHORT_NORMALIZE sum_squares += n*n return math.sqrt( sum_squares / count ) # Opens the microphone stream = p.open( format=FORMAT, channels=CHANNELS, rate=RATE, input=True, input_device_index=0, frames_per_buffer=INPUT_FRAMES_PER_BLOCK ) frames = [] list_ldn = [] list_ct = [] # Listen for sounds until a timeout occurs. start_time = clock.time() print(f"Trial n° {Trial_nbr} starts recording") while clock.time() - start_time <= timeout: try: block = stream.read(CHUNCK) frames.append(block) except IOError as e: print(e) loudness = get_rms(block) list_ldn.append(loudness) list_ct.append(clock.time()) end_time = clock.time() # Close the audio stream stream.stop_stream() stream.close() p.terminate() print("%f ms total duration" % (end_time - start_time)) # Saves the sound files wf = wave.open(FILENAME, 'wb') wf.setnchannels(CHANNELS) wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setframerate(RATE) wf.writeframes(b''.join(frames)) wf.close() # Creates a dataframe with clock.time, response and trial time df = pd.DataFrame({'clock_time':list_ct,'loudness':list_ldn}) df['trial_time'] = df['clock_time'] - start_time loudness_mean = np.mean(df['loudness']) # Defines the sound threshold as the mean for the trial + the number of std deviation defined in nSD or the % of peak loudness defined in minTH - whichever is the highest. sound_threshold = loudness_mean+np.std(df['loudness'])*nSD if np.std(df['loudness'])*nSD>minTH else minTH # Computes response time as the first moment per trial when the sound threshold as been reached try: response_time = (df.loc[df['loudness'] > sound_threshold, 'trial_time'].iloc[0]) response = 1 var.trial_start = start_time var.trial_end = end_time var.loudness = loudness # Uses except to deal with errors when no response is detected except IndexError: response_time = None response = 0 var.trial_start = start_time var.trial_end = end_time var.loudness = None if response == 1: print("Vocal key detected") else : print("Vocal key not detected") print(f"Trial n° {Trial_nbr} stops recording", end="\n\n") # If a keyboard response is also used, this will append the suffix 'voice_key' to OS variables response and response_time responses.add(response=response, response_time=response_time, item='voice_key') # Lists the variables to include in the data file - DO NOT use a logger if you use this. INCLUDE = ['subject_nr','Trial_nr','response_time', 'correct', 'response','trial_start','trial_end', 'loudness','target', 'sound_threshold'] log.write_vars([log_var for log_var in var.inspect() if log_var in INCLUDE]) T2 = clock.time() print(T2-T1)