class Audio(Stimulus): '''A simple audio stimulus.''' def __init__(self, window, sound, text=None, *args, **kwargs): '''Constructor for the Audio stimulus. Arguments: sound - A number (pitch in Hz), string for a note, or string for a filename. For more info, see: http://www.psychopy.org/api/sound.html text - Text to display on screen (Optional). Additional args and kwargs are passed to the sound.Sound constructor. ''' super(Audio, self).__init__(window) self.sound = Sound(sound, *args, **kwargs) self.text = text def show(self): self.display_text(self.text) self.play_sound() return super(Audio, self).show() def play_sound(self): self.sound.play() core.wait(self.sound.getDuration()) return None
class Audio(Stimulus): """A simple audio stimulus.""" def __init__(self, window, sound, text=None, *args, **kwargs): """Constructor for the Audio stimulus. Arguments: sound - A number (pitch in Hz), string for a note, or string for a filename. For more info, see: http://www.psychopy.org/api/sound.html text - Text to display on screen (Optional). Additional args and kwargs are passed to the sound.Sound constructor. """ super(Audio, self).__init__(window) self.sound = Sound(sound, *args, **kwargs) self.text = text def show(self): self.display_text(self.text) self.play_sound() return super(Audio, self).show() def play_sound(self): self.sound.play() core.wait(self.sound.getDuration()) return None
class SyncGenerator(threading.Thread): def __init__(self, TR=1.0, volumes=10, sync="5", skip=0, sound=False): """Class for a character-emitting metronome thread (emulate MR sync pulse). Aim: Allow testing of temporal robustness of fMRI scripts by emulating a hardware sync pulse. Adds an arbitrary 'sync' character to the key buffer, with sub-millisecond precision (less precise if CPU is maxed). Recommend: TR=1.000 or higher and less than 100% CPU. Shorter TR --> higher CPU load. Parameters: TR: seconds per whole-brain volume volumes: number of 3D volumes to obtain in a given scanning run sync: character used as flag for sync timing, default='5' skip: how many frames to silently omit initially during T1 stabilization, no sync pulse. Not needed to test script timing, but will give more accurate feel to start of run. aka "discdacqs". sound: play a tone, slightly shorter duration than TR """ if TR < 0.1: raise ValueError, "SyncGenerator: whole-brain TR < 0.1 not supported" self.TR = TR self.hogCPU = 0.035 self.timesleep = self.TR self.volumes = int(volumes) self.sync = sync self.skip = skip self.playSound = sound if self.playSound: self.sound = Sound(secs=self.TR - 0.08, octave=6, autoLog=False) self.sound.setVolume(0.15) self.clock = core.Clock() self.stopflag = False threading.Thread.__init__(self, None, "SyncGenerator", None) self.running = False def run(self): self.running = True if self.skip: if self.playSound: self.sound.play() core.wait(self.TR * self.skip) # emulate T1 stabilization without data collection self.clock.reset() for vol in range(1, self.volumes + 1): if self.playSound: self.sound.play() if self.stopflag: break # "emit" a sync pulse by placing a key in the buffer: event._onPygletKey(symbol=self.sync, modifiers=None, emulated=True) # wait for start of next volume, doing our own hogCPU for tighter sync: core.wait(self.timesleep - self.hogCPU, hogCPUperiod=0) while self.clock.getTime() < vol * self.TR: pass # hogs the CPU for tighter sync self.running = False def stop(self): self.stopflag = True
def play_file(path, msg, trigger_port, trigger_twice=False): all_events.append({ 'what': "audio played", 'when': core.getTime() - experiment_start, 'content': path, 'message': msg, 'response': None }) msg = visual.TextStim(win, text=msg) msg.draw() win.flip() mySound = Sound(path) if (trigger_port): sendTrigger(trigger_port, duration=0.01) mySound.play() core.wait(mySound.getDuration()) if (trigger_port and trigger_twice): sendTrigger(trigger_port, duration=0.01)
class AudioScene(object): def __init__(self, win, manager, soundFile, fixationCross): self.win = win self.manager = manager self.sound = Sound(soundFile) self.fixationCross = fixationCross self.max_frame = math.ceil( self.sound.getDuration() * constants.FRAME_RATE + constants.AUDIO_DELAY * constants.FRAME_RATE) self.delay_frames = math.ceil(constants.AUDIO_DELAY * constants.FRAME_RATE) self.current_frame = 0 def update(self): self.current_frame += 1 if self.current_frame == self.delay_frames: self.sound.play() self.manager.eyeTracker.sound_start() if self.current_frame >= self.max_frame: self.manager.set_response_scene() self.draw() def draw(self): self.fixationCross.draw()
class SyncGenerator(threading.Thread): def __init__(self, TR=1.0, volumes=10, sync='5', skip=0, sound=False): """Class for a character-emitting metronome thread (emulate MR sync pulse). Aim: Allow testing of temporal robustness of fMRI scripts by emulating a hardware sync pulse. Adds an arbitrary 'sync' character to the key buffer, with sub-millisecond precision (less precise if CPU is maxed). Recommend: TR=1.000 or higher and less than 100% CPU. Shorter TR --> higher CPU load. Parameters: TR: seconds per whole-brain volume volumes: number of 3D volumes to obtain in a given scanning run sync: character used as flag for sync timing, default='5' skip: how many frames to silently omit initially during T1 stabilization, no sync pulse. Not needed to test script timing, but will give more accurate feel to start of run. aka "discdacqs". sound: simulate scanner noise """ if TR < 0.1: raise ValueError, 'SyncGenerator: whole-brain TR < 0.1 not supported' self.TR = TR self.hogCPU = 0.035 self.timesleep = self.TR self.volumes = int(volumes) self.sync = sync self.skip = skip self.playSound = sound if self.playSound: # pragma: no cover self.sound1 = Sound(800, secs=self.TR-.08, volume=0.15, autoLog=False) self.sound2 = Sound(813, secs=self.TR-.08, volume=0.15, autoLog=False) self.clock = core.Clock() self.stopflag = False threading.Thread.__init__(self, None, 'SyncGenerator', None) self.running = False def run(self): self.running = True if self.skip: for i in range(int(self.skip)): if self.playSound: # pragma: no cover self.sound1.play() self.sound2.play() core.wait(self.TR, hogCPUperiod=0) # emulate T1 stabilization without data collection self.clock.reset() for vol in range(1, self.volumes+1): if self.playSound: # pragma: no cover self.sound1.play() self.sound2.play() if self.stopflag: break # "emit" a sync pulse by placing a key in the buffer: event._onPygletKey(symbol=self.sync, modifiers=None, emulated=True) # wait for start of next volume, doing our own hogCPU for tighter sync: core.wait(self.timesleep - self.hogCPU, hogCPUperiod=0) while self.clock.getTime() < vol * self.TR: pass # hogs the CPU for tighter sync self.running = False return self def stop(self): self.stopflag = True
tstart = time.time() while time.time() - tstart < 30: # Check if there are new data to read while oxi.serial.inWaiting() >= 5: # Convert bytes into list of int paquet = list(oxi.serial.read(5)) if oxi.check(paquet): # Data consistency oxi.add_paquet(paquet[2]) # Add new data point # T + 0 if oxi.peaks[-1] == 1: beat = Sound("C", secs=0.1) beat.play() systoleTime1 = time.time() systoleTime2 = time.time() systoleTime3 = time.time() # T + 1/4 if systoleTime1 is not None: if time.time() - systoleTime1 >= ((oxi.instant_rr[-1] / 4) / 1000): diastole1 = Sound("E", secs=0.1) diastole1.play() systoleTime1 = None # T + 2/4 if systoleTime2 is not None: if time.time() - systoleTime2 >= (( (oxi.instant_rr[-1] / 4) * 2) / 1000):
loops=0) trial_times = np.array([]) trial_times = win.flip() for itrial in range(ntrials): for gframe in range(int(gap_frames)): #print(gframe) fixation.draw() win.flip() for gtframe in range(int(grating_frames)): if gtframe == 0: if ppt: win.callOnFlip(p_port.setData, int(itrial + 1)) if sst: win.callOnFlip(p_port.write, b'01') medf_s.play() if gtframe == 1: if ppt: win.callOnFlip(p_port.setData, int(0)) if sst: win.callOnFlip(p_port.write, b'00') #print(gtframe) #grating.draw() circle.draw() fixation.draw() if gtframe == 0: t = win.flip() trial_times = np.append(trial_times, t) else: win.flip #p_port.stop()
prevState = serFmri.getDSR() while tot_vol < MR_settings['volumes']: currentState = serFmri.getDSR() if (currentState != prevState): if tot_vol == 0: globalClock.reset() #reset time at first pulse prevState = currentState tot_vol = tot_vol + 1 win.flip() fixation.draw() #update the total volumes counter print('Current volume: ', str(tot_vol)) #display the images according to the timing onset predefined if tot_vol in baselines_onset[1:]: print(globalClock.getTime()) stop_stim.play() if tot_vol in task_onset: audio_timing.append(globalClock.getTime()) stimulus.play() win.close() with open(os.path.join(outdir, 'audio_stim_timing_NF1.txt'), 'w') as f: for item in audio_timing: f.write("%s\n" % item) #write the complete item list (with brackets) f.close()
class EyeLinkCoreGraphicsPsychoPy(pylink.EyeLinkCustomDisplay): def __init__(self, tracker, win): '''Initialize tracker: an EyeLink instance (connection) win: the PsychoPy window we use for calibration''' pylink.EyeLinkCustomDisplay.__init__(self) # background and target color self._backgroundColor = win.color self._foregroundColor = 'black' # window to use for calibration self._display = win # make the mouse cursor invisible self._display.mouseVisible = False # display width & height self._w, self._h = win.size # resolution fix for Mac retina displays if 'Darwin' in platform.system(): sys_cmd = 'system_profiler SPDisplaysDataType | grep Retina' is_ret = os.system(sys_cmd) if is_ret == 0: self._w = int(self._w / 2.0) self._h = int(self._h / 2.0) # store camera image pixels in an array self._imagebuffer = array.array('I') # store the color palette for camera image drawing self._pal = None # initial size of the camera image self._size = (384, 320) # initial mouse configuration self._mouse = event.Mouse(False) self.last_mouse_state = -1 # camera image title self._msgHeight = self._size[1] / 16.0 self._title = visual.TextStim(self._display, '', wrapWidth=self._w, color=self._foregroundColor) # calibration target self._targetSize = self._w / 64. self._tar = visual.Circle(self._display, size=self._targetSize, lineColor=self._foregroundColor, lineWidth=self._targetSize / 2) # calibration sounds (beeps) self._target_beep = Sound('type.wav', stereo=True) self._error_beep = Sound('error.wav', stereo=True) self._done_beep = Sound('qbeep.wav', stereo=True) # a reference to the tracker connection self._tracker = tracker # for a clearer view we always enlarge the camera image self.imgResize = None def setup_cal_display(self): '''Set up the calibration display ''' self._display.clearBuffer() def clear_cal_display(self): '''Clear the calibration display''' self._display.color = self._backgroundColor self._display.flip() def exit_cal_display(self): '''Exit the calibration/validation routine''' self.clear_cal_display() def record_abort_hide(self): '''This function is called if aborted''' pass def erase_cal_target(self): '''Erase the target''' self.clear_cal_display() def draw_cal_target(self, x, y): '''Draw the target''' self.clear_cal_display() # target position xVis = (x - self._w / 2.0) yVis = (self._h / 2.0 - y) # draw the calibration target self._tar.pos = (xVis, yVis) self._tar.draw() self._display.flip() def play_beep(self, beepid): ''' Play a sound during calibration/drift-correction.''' if beepid in [pylink.CAL_TARG_BEEP, pylink.DC_TARG_BEEP]: self._target_beep.play() elif beepid in [pylink.CAL_ERR_BEEP, pylink.DC_ERR_BEEP]: self._error_beep.play() elif beepid in [pylink.CAL_GOOD_BEEP, pylink.DC_GOOD_BEEP]: self._done_beep.play() core.wait(0.4) def getColorFromIndex(self, colorindex): '''Retrieve the colors for camera image elements, e.g., crosshair''' if colorindex == pylink.CR_HAIR_COLOR: return (255, 255, 255) elif colorindex == pylink.PUPIL_HAIR_COLOR: return (255, 255, 255) elif colorindex == pylink.PUPIL_BOX_COLOR: return (0, 255, 0) elif colorindex == pylink.SEARCH_LIMIT_BOX_COLOR: return (255, 0, 0) elif colorindex == pylink.MOUSE_CURSOR_COLOR: return (255, 0, 0) else: return (128, 128, 128) def draw_line(self, x1, y1, x2, y2, colorindex): '''Draw a line ''' color = self.getColorFromIndex(colorindex) # scale the coordinates w, h = self._img.im.size x1 = int(x1 / 192 * w) x2 = int(x2 / 192 * w) y1 = int(y1 / 160 * h) y2 = int(y2 / 160 * h) # draw the line if not any([x < 0 for x in [x1, x2, y1, y2]]): self._img.line([(x1, y1), (x2, y2)], color) def draw_lozenge(self, x, y, width, height, colorindex): ''' draw a lozenge to show the defined search limits ''' color = self.getColorFromIndex(colorindex) # scale the coordinates w, h = self._img.im.size x = int(x / 192 * w) y = int(y / 160 * h) width = int(width / 192 * w) height = int(height / 160 * h) # draw the lozenge if width > height: rad = int(height / 2.) if rad == 0: return else: self._img.line([(x + rad, y), (x + width - rad, y)], color) self._img.line([(x + rad, y + height), (x + width - rad, y + height)], color) self._img.arc([x, y, x + rad * 2, y + rad * 2], 90, 270, color) self._img.arc([x + width - rad * 2, y, x + width, y + height], 270, 90, color) else: rad = int(width / 2.) if rad == 0: return else: self._img.line([(x, y + rad), (x, y + height - rad)], color) self._img.line([(x + width, y + rad), (x + width, y + height - rad)], color) self._img.arc([x, y, x + rad * 2, y + rad * 2], 180, 360, color) self._img.arc( [x, y + height - rad * 2, x + rad * 2, y + height], 0, 180, color) def get_mouse_state(self): '''Get the current mouse position and status''' w, h = self._display.size X, Y = self._mouse.getPos() # scale the mouse position so the cursor stay on the camera image mX = (X + w / 2.0) / w * self._size[0] / 2.0 mY = (h / 2.0 - Y) / h * self._size[1] / 2.0 state = self._mouse.getPressed()[0] return ((mX, mY), state) def get_input_key(self): '''This function is repeatedly pooled to check keyboard events''' ky = [] for keycode, modifier in event.getKeys(modifiers=True): k = pylink.JUNK_KEY if keycode == 'f1': k = pylink.F1_KEY elif keycode == 'f2': k = pylink.F2_KEY elif keycode == 'f3': k = pylink.F3_KEY elif keycode == 'f4': k = pylink.F4_KEY elif keycode == 'f5': k = pylink.F5_KEY elif keycode == 'f6': k = pylink.F6_KEY elif keycode == 'f7': k = pylink.F7_KEY elif keycode == 'f8': k = pylink.F8_KEY elif keycode == 'f9': k = pylink.F9_KEY elif keycode == 'f10': k = pylink.F10_KEY elif keycode == 'pageup': k = pylink.PAGE_UP elif keycode == 'pagedown': k = pylink.PAGE_DOWN elif keycode == 'up': k = pylink.CURS_UP elif keycode == 'down': k = pylink.CURS_DOWN elif keycode == 'left': k = pylink.CURS_LEFT elif keycode == 'right': k = pylink.CURS_RIGHT elif keycode == 'backspace': k = ord('\b') elif keycode == 'return': k = pylink.ENTER_KEY elif keycode == 'space': k = ord(' ') elif keycode == 'escape': k = 27 elif keycode == 'tab': k = ord('\t') elif keycode in string.ascii_letters: k = ord(keycode) elif k == pylink.JUNK_KEY: k = 0 # plus & minus signs for CR adjustment if keycode in ['num_add', 'equal']: k = ord('+') if keycode in ['num_subtract', 'minus']: k = ord('-') # handles key modifier if modifier['alt'] is True: mod = 256 elif modifier['ctrl'] is True: mod = 64 elif modifier['shift'] is True: mod = 1 else: mod = 0 ky.append(pylink.KeyInput(k, mod)) return ky def exit_image_display(self): '''Clear the camera image''' self.clear_cal_display() self._display.flip() def alert_printf(self, msg): '''Print error messages.''' print("Error: " + msg) def setup_image_display(self, width, height): ''' set up the camera image return 1 to show high-resolution camera images''' self.last_mouse_state = -1 self._size = (width, height) return 1 def image_title(self, text): '''Draw title text below the camera image''' self._title.text = text def draw_image_line(self, width, line, totlines, buff): '''Display image pixel by pixel, line by line''' for i in range(width): try: self._imagebuffer.append(self._pal[buff[i]]) except: pass if line == totlines: bufferv = self._imagebuffer.tostring() img = Image.frombytes("RGBX", (width, totlines), bufferv) self._img = ImageDraw.Draw(img) # draw the cross hairs self.draw_cross_hair() # scale the camera image self.imgResize = img.resize((width * 2, totlines * 2)) cam_img = visual.ImageStim(self._display, image=self.imgResize, units='pix') cam_img.draw() # draw the camera image title self._title.pos = (0, -totlines - self._msgHeight) self._title.draw() self._display.flip() # clear the camera image buffer self._imagebuffer = array.array('I') def set_image_palette(self, r, g, b): '''Given a set of RGB colors, create a list of 24bit numbers representing the color palette. For instance, RGB of (1,64,127) would be saved as 82047, or 00000001 01000000 011111111''' self._imagebuffer = array.array('I') sz = len(r) i = 0 self._pal = [] while i < sz: rf = int(b[i]) gf = int(g[i]) bf = int(r[i]) self._pal.append((rf << 16) | (gf << 8) | (bf)) i = i + 1
#------------------------------- # SETUP SOUND STIMS freq_factor = 2*np.pi*np.linspace(0,0.5,22050) freqL = 440 freqR = 440 soundL = np.sin(freq_factor*freqL) soundR = np.sin(freq_factor*freqR) s_out = Sound(np.array([soundL,soundR]).T,secs=10) # #------------------------------- t=1 while not kb_events: s_out.stop() if t==1: s_out.play() t=0 # soundL = np.sin(freq_factor*(freqL+10)) # soundR = np.sin(freq_factor*freqR) # s_out.setSound = np.array([soundL,soundR]).T # Get the current mouse position # posDelta is the change in position * since the last call * position, posDelta = mouse.getPositionAndDelta() mouse_dX, mouse_dY = posDelta # Get the current state of each of the Mouse Buttons left_button, middle_button, right_button = mouse.getCurrentButtonStates() # If the left button is pressed, change the grating's spatial frequency t_now = time.time()
# trials = TrialHandler(sampleFilenames, 1, method="random") s = Sound() win = visual.Window() msg = visual.TextStim(win, text="Press a key to hear a sound, Q to quit") s.setSound("./samples/13.wav") while True: msg.draw() win.flip() k = event.getKeys() if len(k)>0: if 'q' in k: break ns.sync() s.play() ns.send_event('evt1') time.sleep(0.1) event.clearEvents() ns.send_event('evt5', timestamp=egi.ms_localtime()) # for trial in trials: # s.setSound(trial) # while True: # msg.draw() # win.flip() # if len(event.getKeys())>0: # event.clearEvents() # s.play() # break
class StopSignalSession(MRISession): def __init__(self, subject_initials, index_number, tr, start_block, config): super(StopSignalSession, self).__init__( subject_initials, index_number, tr=tr, simulate_mri_trigger=False, # NB: DO NOT use this MRI simulation option, but rather another! mri_trigger_key=config.get('mri', 'mri_trigger_key')) self.config = config self.start_block = start_block # allows for starting at a later block than 1 self.warmup_trs = config.get('mri', 'warmup_trs') if tr == 2: self.trial_duration = 8 - .5 elif tr == 3: self.trial_duration = 9 - 0.5 if self.subject_initials == 'pilot': self.trial_duration = [8.5, 7.5, 8.5, 7.5] if config.get('audio', 'engine') == 'psychopy': # BEFORE moving on, ensure that the correct audio driver is selected from psychopy import prefs prefs.general['audioLib'] = config.get('audio', 'backend') from psychopy.sound import Sound self.bleeper = Sound(secs=0.1, octave=5, loops=0, sampleRate=44100, name='') # self.bleeper.play() elif config.get('audio', 'engine') == 'TK': self.setup_sound_system() self.read_sound_file('sounds/0.wav', '0') # to test sound: # self.play_sound(sound_index=0) self.response_button_signs = [ config.get('input', 'response_button_left'), config.get('input', 'response_button_right') ] screen = self.create_screen( engine='psychopy', size=config.get('screen', 'size'), full_screen=config.get('screen', 'full_screen'), background_color=config.get('screen', 'background_color'), gamma_scale=config.get('screen', 'gamma_scale'), physical_screen_distance=config.get('screen', 'physical_screen_distance'), physical_screen_size=config.get('screen', 'physical_screen_size'), max_lums=config.get('screen', 'max_lums'), wait_blanking=config.get('screen', 'wait_blanking'), screen_nr=config.get('screen', 'screen_nr'), mouse_visible=config.get('screen', 'mouse_visible')) # Try this # TODO: think about really including this? self.screen.recordFrameIntervals = True self.phase_durations = np.array([ -0.0001, # wait for scan pulse -5, 1, -5 ]) # the strings will be filled before every trial self.load_design() self.prepare_objects() self.prepare_staircase() # creating a mixture class would be a lot nicer but I can't be bothered so I'll cheat and include everything # here def setup_sound_system(self): """initialize pyaudio backend, and create dictionary of sounds.""" self.pyaudio = pyaudio.PyAudio() self.sound_files = \ subprocess.Popen('ls ' + os.path.join('.', 'sounds', '*.wav'), shell=True, stdout=subprocess.PIPE).communicate()[0].split('\n')[0:-1] self.sounds = {} for sf in self.sound_files: self.read_sound_file(file_name=sf) # print self.sounds def read_sound_file(self, file_name, sound_name=None): """Read sound file from file_name, and append to self.sounds with name as key""" if sound_name == None: sound_name = os.path.splitext(os.path.split(file_name)[-1])[0] rate, data = wavfile.read(file_name) # create stream data assuming 2 channels, i.e. stereo data, and use np.float32 data format stream_data = data.astype(np.int16) # check data formats - is this stereo sound? If so, we need to fix it. wf = wave.open(file_name, 'rb') # print sound_name # print wf.getframerate(), wf.getnframes(), wf.getsampwidth(), wf.getnchannels() if wf.getnchannels() == 2: stream_data = stream_data[::2] self.sounds.update({sound_name: stream_data}) def play_bleep(self): if self.config.get('audio', 'engine') == 'TK': self.play_sound('0') else: self.bleeper.play() def load_design(self): fn = 'sub-' + str(self.subject_initials).zfill(3) + '_tr-' + str( self.index_number) + '_design' design = pd.read_csv(os.path.join('designs', fn + '.csv'), sep='\t', index_col=False) self.design = design self.design = self.design.apply(pd.to_numeric) # cast all to numeric # self.design.stop_trial = pd.to_ # print(self.design) def prepare_staircase(self): # TODO: load from previous run? # check for old file now = datetime.datetime.now() opfn = now.strftime("%Y-%m-%d") expected_filename = str(self.subject_initials) + '_' + str( self.index_number) + '_' + opfn fns = glob.glob('./data/' + expected_filename + '_*_staircases.pkl') if self.start_block > 1 and len(fns) == 1: # if previous run was created with open(fns[0], 'r') as f: self.stairs = pkl.load(f) else: # Make dict info = { 'startPoints': [.100, .200] } # start points for the four staircases # create staircases self.stairs = [] for thisStart in info['startPoints']: # we need a COPY of the info for each staircase # (or the changes here will be made to all the other staircases) thisInfo = copy.copy(info) # now add any specific info for this staircase thisInfo[ 'thisStart'] = thisStart # we might want to keep track of this thisStair = data.StairHandler(startVal=thisStart, extraInfo=thisInfo, stepType='lin', minVal=0, nTrials=1000, maxVal=0.900, stepSizes=[0.050]) self.stairs.append(thisStair) # Save staircases with open(self.output_file + '_staircases.pkl', 'w') as f: pkl.dump(self.stairs, f) self.design.staircase_ID = -1 for block in np.unique(self.design.block): if block < self.start_block: continue # how many stop trials this block? n_stop_trials = self.design.loc[self.design.block == block].stop_trial.sum() staircase_idx = np.tile(np.arange(len(self.stairs)), reps=1000)[:n_stop_trials] np.random.shuffle(staircase_idx) # append to design self.design.loc[(self.design.stop_trial == 1) & (self.design.block == block), 'staircase_id'] = \ staircase_idx def prepare_objects(self): config = self.config self.left_stim = StopStimulus(screen=self.screen, direction=0, arrow_size_horizontal_degrees=config.get( 'stimulus', 'arrow_size')) self.right_stim = StopStimulus( screen=self.screen, direction=1, arrow_size_horizontal_degrees=config.get('stimulus', 'arrow_size')) self.fixation_circle = FixationCircle( screen=self.screen, circle_radius_degrees=config.get('stimulus', 'circle_radius_degrees'), line_width=config.get('stimulus', 'line_width'), line_color=config.get('stimulus', 'line_color')) self.scanner_wait_screen = visual.TextStim( win=self.screen, text='Waiting for scanner...', name='scanner_wait_screen', units='pix', font='Helvetica Neue', pos=(0, 0), italic=True, height=30, alignHoriz='center') if self.subject_initials == 'DEBUG': self.stop_timing_circle = visual.Circle(win=self.screen, radius=3, edges=50, lineWidth=1.5, fillColor='red', lineColor='red', units='deg', lineColorSpace='rgb', fillColorSpace='rgb') def save_data(self, trial_handler=None, block_n='all'): output_fn_dat = self.output_file + '_block-' + str(block_n) output_fn_frames = self.output_file + '_block-' + str(block_n) if trial_handler is not None: trial_handler.saveAsPickle(output_fn_dat) trial_handler.saveAsWideText(output_fn_dat + '.csv', ) if self.screen.recordFrameIntervals: # Save frame intervals to file self.screen.saveFrameIntervals(fileName=output_fn_frames + '_frameintervals.log', clear=False) # import matplotlib.pyplot as plt # # Make a nice figure # intervals_ms = np.array(self.screen.frameIntervals) * 1000 # m = np.mean(intervals_ms) # sd = np.std(intervals_ms) # # msg = "Mean=%.1fms, s.d.=%.2f, 99%%CI(frame)=%.2f-%.2f" # dist_string = msg % (m, sd, m - 2.58 * sd, m + 2.58 * sd) # n_total = len(intervals_ms) # n_dropped = sum(intervals_ms > (1.5 * m)) # msg = "Dropped/Frames = %i/%i = %.3f%%" # dropped_string = msg % (n_dropped, n_total, 100 * n_dropped / float(n_total)) # # # plot the frame intervals # plt.figure(figsize=[12, 8]) # plt.subplot(1, 2, 1) # plt.plot(intervals_ms, '-') # plt.ylabel('t (ms)') # plt.xlabel('frame N') # plt.title(dropped_string) # # plt.subplot(1, 2, 2) # plt.hist(intervals_ms, 50, normed=0, histtype='stepfilled') # plt.xlabel('t (ms)') # plt.ylabel('n frames') # plt.title(dist_string) # plt.savefig(output_fn_frames + '_frameintervals.png') def close(self): """ Saves stuff and closes """ self.save_data() super(StopSignalSession, self).close() def run(self): """ Runs this Stop Signal task""" test_sound = TestSoundTrial(ID=-1, parameters={}, phase_durations=[1000], session=self, screen=self.screen, tracker=None) test_sound.run() self.block_start_time = 0 # start emulator TODO REMOVE THIS STUFF!! # n_vols = [343+2, 513+2, 343+2, 513+2] # trs = [3, 2, 3, 2] # n_vols = [31+2, 21+2] # trs = [3, 2] # from psychopy.hardware.emulator import launchScan for block_n in np.unique(self.design.block): if block_n < self.start_block: continue this_block_design = self.design.loc[self.design.block == block_n] # scanner_emulator = launchScan(win=self.screen, settings={'TR': trs[block_n-1], 'volumes': n_vols[block_n-1], # 'sync': 't'}, # mode='Test') if isinstance(self.trial_duration, list): trial_duration = self.trial_duration[block_n - 1] else: trial_duration = self.trial_duration trial_handler = data.TrialHandler( this_block_design.to_dict('records'), nReps=1, method='sequential') for block_trial_ID, this_trial_info in enumerate(trial_handler): is_stop_trial = this_trial_info['stop_trial'] if is_stop_trial: this_trial_staircase_id = int( this_trial_info['staircase_id']) this_trial_ssd = next(self.stairs[this_trial_staircase_id]) this_staircase_start_val = self.stairs[ this_trial_staircase_id].extraInfo['thisStart'] else: this_trial_staircase_id = -1 this_trial_ssd = -1 this_staircase_start_val = -1 this_trial_parameters = { 'direction': int(this_trial_info['direction']), 'stop_trial': int(this_trial_info['stop_trial']), 'current_ssd': this_trial_ssd, 'current_staircase': this_trial_staircase_id, 'staircase_start_val': this_staircase_start_val, 'block': block_n, 'block_trial_ID': block_trial_ID } these_phase_durations = self.phase_durations.copy() these_phase_durations[1] = this_trial_info.jitter # NB we stop the trial 0.5s before the start of the new trial, to allow sufficient computation time # for preparing the next trial. Therefore 8.5s instead of 9s. these_phase_durations[ 3] = trial_duration - these_phase_durations[ 1] - these_phase_durations[2] this_trial = StopSignalTrial( ID=int(this_trial_info.trial_ID), parameters=this_trial_parameters, phase_durations=these_phase_durations, session=self, screen=self.screen) # run the prepared trial this_trial.run() # Record some stuff trial_handler.addData('rt', this_trial.rt) trial_handler.addData('response', this_trial.response) # absolute times since session start trial_handler.addData('start_time', this_trial.start_time) trial_handler.addData('t_time', this_trial.t_time) trial_handler.addData('jitter_time', this_trial.jitter_time) trial_handler.addData('stimulus_time', this_trial.stimulus_time) trial_handler.addData('iti_time', this_trial.iti_time) # durations / time since actual trial start (note that the *actual* trial start is t_time!) if is_stop_trial: trial_handler.addData('ssd', this_trial_ssd) trial_handler.addData( 'stop_signal_time_recorded', this_trial.bleep_time - this_trial.jitter_time) trial_handler.addData('staircase_start_val', this_staircase_start_val) trial_handler.addData( 'phase_0_measured', this_trial.t_time - this_trial.start_time) trial_handler.addData( 'phase_1_measured', this_trial.jitter_time - this_trial.t_time) trial_handler.addData( 'phase_2_measured', this_trial.stimulus_time - this_trial.jitter_time) trial_handler.addData( 'phase_3_measured', this_trial.iti_time - this_trial.stimulus_time) # durations / time since actual start of the block. These are useful to create events-files later for # convolving. Can also grab these from the eventArray though. trial_handler.addData( 'trial_t_time_block_measured', this_trial.t_time - self.block_start_time) trial_handler.addData( 'stimulus_onset_time_block_measured', this_trial.jitter_time - self.block_start_time) # Counter-intuitive, but jitter_time is END of the jitter period = onset of stim # Update staircase if this was a stop trial if is_stop_trial: if this_trial.response_measured: # Failed stop: Decrease SSD self.stairs[this_trial_staircase_id].addData(1) else: # Successful stop: Increase SSD self.stairs[this_trial_staircase_id].addData(0) if self.stopped: # out of trial break # Save self.save_data(trial_handler, block_n) if self.stopped: # out of block break # end of block this_trial = EndOfBlockTrial(ID=int('999' + str(block_n)), parameters={}, phase_durations=[0.5, 1000], session=self, screen=self.screen) this_trial.run() self.close()
class LocalGame: action = None sound = None slidenum = None slideons = None def __init__(self): self.immerseT = [] self.countT = [] self.imgBank_instruct = [] self.imgBank_proc = [] self.slidenum = 0 self.slideons = None self.localGraphics = GameGraphics() self.state = Interact() self.action = None self.sound = Sound(resourceRoot + soundfile) self.saveLoc = DATA_FOLDER + "PREEMPT2_%s/" + SAVE_NAME self.start() def start(self): self.generateTimeBank() self.generateImgBank() def generateTimeBank(self): times = timeorder[cycle - 1] for x in times: self.immerseT.append(TB[x][0]) self.countT.append(TB[x][1]) self.countT = self.countT[:-1] def generateImgBank(self): self.imgBank_proc = ["Slide11.jpg", "Slide12.jpg"] for x in range(1, 11): self.imgBank_instruct.append("Slide" + str(x) + ".jpg") countorder[cycle - 1].extend((23, 24)) for x in countorder[cycle - 1]: self.imgBank_proc.append("Slide" + str(x) + ".jpg") def runInstructions(self): for x in self.imgBank_instruct: self.slidenum += 1 self.localGraphics.baseScreen(x) self.slideons = str(datetime.now()) self.action = self.state.actionCont() self.state.saveDataInst() self.slidenum = 0 return def runFInstructions(self): self.localGraphics.baseScreen("Slide25.jpg") self.action = self.state.actionCont() def runMast(self): retcount = 0 self.localGraphics.baseScreen(self.imgBank_proc[0]) self.action = self.state.sleeper(3) for x in range(0, 5): self.slidenum += 1 self.localGraphics.baseScreen(self.imgBank_proc[1]) self.sound.play() self.state.saveDataMast(self.immerseT[x], 'Immersion') self.action = self.state.sleeper(self.immerseT[x]) if x > 0: retcount = 1 if x < 4: self.slidenum += 1 self.localGraphics.baseScreen(self.imgBank_proc[2 + retcount]) self.sound.play() self.state.saveDataMast(self.countT[x], 'Subtraction') self.action = self.state.sleeper(self.countT[x]) self.sound.play() self.localGraphics.baseScreen(self.imgBank_proc[4]) self.action = self.state.sleeper(90) self.localGraphics.baseScreen(self.imgBank_proc[5]) self.action = self.state.actionCont() return def fixation(self, time): self.localGraphics.fixationScreen() self.action = self.state.sleeper(time)
geninstructions() window.flip() keys = event.waitKeys(keyList=['space']) for trials in exphandler.loops: # traverse through trials for trial in trials: # display baseline genbaseline(subjects) window.flip() # wait for a random time between 2 to 4 seconds core.wait(np.random.uniform(2, 4)) # preparing time for next window flip, to precisely co-ordinate window flip and beep nextflip = window.getFutureFlipTime(clock='ptb') beep.play(when=nextflip) # display stimulus gendecisionint(subjects, trials.thisTrial['condition']) window.flip() # we decided to reset the clock after flipping (redrawing) the window responsetime.reset() # fetch button press response = fetchbuttonpress(subjects, responsetime) print(response) # need to explicity call stop() to go back to the beginning of the track # we reset after collecting a response, otherwise the beep is stopped too early beep.stop() # display inter trial interval