diff --git a/PyHabDemo/PyHab/PyHabBuilder.py b/PyHabDemo/PyHab/PyHabBuilder.py index 47c19f8..2ef73c3 100644 --- a/PyHabDemo/PyHab/PyHabBuilder.py +++ b/PyHabDemo/PyHab/PyHabBuilder.py @@ -743,7 +743,7 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo={}): typeInfo = typeDlg.show() - + # Process response if typeDlg.OK: # Check if all the things that need to be numbers are actually numbers. for i in ['maxDur', 'maxOff', 'minOn', 'onTimeDeadline']: @@ -763,15 +763,12 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo={}): if typeInfo['trialType'] is not trialType: # First, do we need to change the trial type label for an existing type? if not makeNew and typeInfo['trialType'] not in self.trialTypesArray['labels']: # change all the dicts and everything. - self.settings['stimNames'][typeInfo['trialType']] = self.settings['stimNames'].pop(trialType) - self.settings['maxDur'][typeInfo['trialType']]=self.settings['maxDur'].pop(trialType) - self.settings['playThrough'][typeInfo['trialType']] = self.settings['playThrough'].pop(trialType) - self.settings['maxOff'][typeInfo['trialType']] = self.settings['maxOff'].pop(trialType) - self.settings['minOn'][typeInfo['trialType']] = self.settings['minOn'].pop(trialType) - if trialType in self.settings['playAttnGetter'].keys(): - self.settings['playAttnGetter'][typeInfo['trialType']] = self.settings['playAttnGetter'].pop(trialType) - if trialType in self.settings['onTimeDeadline'].keys(): - self.settings['onTimeDeadline'][typeInfo['trialType']] = self.settings['onTimeDeadline'].pop(trialType) + # Using the trialTypeKeyList we can automate this to some degree. + trialTypeKeylist = ['stimNames', 'maxDur', 'minOn', 'maxOff', 'ISI', 'maxOn', + 'minDur','playAttnGetter', 'midAG', 'playThrough', 'onTimeDeadline'] + for i in trialTypeKeylist: + if trialType in self.settings[i].keys(): + self.settings[i][typeInfo['trialType']] = self.settings[i].pop(trialType) # update trial type and study flow too numChar = len(typeInfo['trialType']) if numChar <= 3: @@ -783,8 +780,8 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo={}): self.settings['trialTypes'] = [typeInfo['trialType'] if x == trialType else x for x in self.settings['trialTypes']] self.settings['trialOrder'] = [typeInfo['trialType'] if x == trialType else x for x in self.settings['trialOrder']] self.trialTypesArray = self.loadTypes(self.typeLocs, self.settings['trialTypes'], page=self.trialPalettePage) - # Update in all the things that are lists of filenames with a given property - listsList = ['autoRedo','autoAdvance','movieEnd','durationCriterion'] + # Update in all the things that are lists of trial types with a given property + listsList = ['autoRedo','autoAdvance','movieEnd','durationCriterion', 'dynamicPause'] for k in range(0, len(listsList)): if trialType in self.settings[listsList[k]]: self.settings[listsList[k]].remove(trialType) @@ -1189,7 +1186,7 @@ def makeBlockDlg(self, name='', new=True): errDlg.addText("Name cannot be blank!") irrel = errDlg.show() self.makeBlockDlg(name, new) - elif '.' in newBlock['name'] or '^' in newBlock[0] or '*' in newBlock[0]: + elif '.' in newBlock['name'] or '^' in newBlock['name'] or '*' in newBlock['name']: errDlg = gui.Dlg(title="Illegal block name!") errDlg.addText("Name contains illegal character, or is reserved. Please rename!") irrel = errDlg.show() @@ -1587,12 +1584,17 @@ def deleteType(self, dType): if dType in self.settings['blockList'].keys(): # Block vs. trial. Includes hab. del self.settings['blockList'][dType] else: - keylist = ['stimNames','maxDur','minOn','maxOff','ISI'] - for j in range(0, len(keylist)): - if dType in self.settings[keylist[j]].keys(): - del self.settings[keylist[j]][dType] - if dType in self.settings['playThrough']: # if it was in playThrough, remove it from there too. - self.settings['playThrough'].pop(dType, None) + # A list of all the settings that use trial types as keys. + trialTypeKeylist = ['stimNames','maxDur','minOn','maxOff','ISI', 'maxOn', 'minDur', + 'playAttnGetter','midAG','playThrough', 'onTimeDeadline'] + for j in range(0, len(trialTypeKeylist)): + if dType in self.settings[trialTypeKeylist[j]].keys(): + del self.settings[trialTypeKeylist[j]][dType] + # List of items that are lists of trial types themselves + listsList = ['autoRedo', 'autoAdvance', 'movieEnd', 'durationCriterion', 'dynamicPause'] + for k in range(0, len(listsList)): + if dType in self.settings[listsList[k]]: + self.settings[listsList[k]].remove(dType) for i, j in self.settings['blockList'].items(): # If it's part of a block if dType in j: while dType in j: @@ -1610,6 +1612,7 @@ def deleteType(self, dType): self.settings['trialOrder'].remove(dType) self.studyFlowArray = self.loadFlow(self.settings['trialOrder'], self.flowArea, self.flowLocs, self.overFlowLocs, types=self.settings['trialTypes']) # To update colors if needed. if self.settings['condFile'] != '': + # TODO: Right now it just warns the user, but updating the conditions will be tricky. warnDlg = gui.Dlg(title="Update conditions") warnDlg.addText( "WARNING! UPDATE CONDITION SETTINGS AFTER REMOVING THIS TRIAL TYPE! \nIf you do not update conditions, the experiment may crash when you try to run it.") @@ -1998,14 +2001,14 @@ def univSettingsDlg(self): #The universal settings button. else: self.settings['nextFlash'] = 0 # The next two fields have validation, but any other changes to settings will be saved even if they fail. - if isinstance(uInfo['ITIbase'], int): + if isinstance(uInfo['ITIbase'], float) or isinstance(uInfo['ITIbase'], int): self.settings['ITIbase'] = uInfo['ITIbase'] else: try: self.settings['ITIbase'] = eval(uInfo['ITIbase']) except: tryAgain = True - if isinstance(uInfo['ITIjitter'], int): + if isinstance(uInfo['ITIjitter'], float) or isinstance(uInfo['ITIjitter'], int): self.settings['ITIjitter'] = uInfo['ITIjitter'] else: try: @@ -2266,9 +2269,9 @@ def stimSettingsDlg(self, lastSet={}, redo=False, screen='all'): 'screenColor' 2 = Background color of stim window - 'movieWidth' 3 = movieWidth: Width of movieStim3 object inside stim window. Future: Allows for movie default resolution? + 'movieWidth' 3 = movieWidth: Width of movieStim object inside stim window. Future: Allows for movie default resolution? - 'movieHeight' 4 = movieHeight: Height of movieStim3 object inside stim window + 'movieHeight' 4 = movieHeight: Height of movieStim object inside stim window 'freezeFrame' 5 = freezeFrame: If the attention-getter is used (for a given trial type), this is the minimum time the first frame of the movie will be displayed after the attention-getter finishes. @@ -2690,7 +2693,8 @@ def attnGetterVideoDlg(self): if type(fileSelectDlg) is not NoneType: path, namething = os.path.split(fileSelectDlg[0]) # Suboptimal solution for getting duration, but possibly only available. - tempMovie = visual.MovieStim3(self.win, fileSelectDlg[0]) + tempMovie = visual.MovieStim(self.win, fileSelectDlg[0]) + # In order to get duration yo uactually need to start the video...ridiculous. tempGetter = {'stimLoc': fileSelectDlg[0], 'stimName': namething, 'stimDur': tempMovie.duration} del tempMovie @@ -2715,7 +2719,7 @@ def attnGetterMovieAudioDlg(self): if type(fileSelectDlg) is not NoneType: path, namething = os.path.split(fileSelectDlg[0]) # Suboptimal solution for getting duration, but possibly only available. - tempMovie = visual.MovieStim3(self.win, fileSelectDlg[0]) + tempMovie = visual.MovieStim(self.win, fileSelectDlg[0]) tempDuration = tempMovie.duration soundSelectDlg = gui.fileOpenDlg(prompt="Select attention-getter AUDIO file") if type(soundSelectDlg) is not NoneType: @@ -2828,7 +2832,7 @@ def attnGetterDlg(self): if ans2b['AGType'] == 'Audio': tempStim = sound.Sound(fileSelectDlg[0]) else: - tempStim = visual.MovieStim3(self.win, fileSelectDlg[0]) + tempStim = visual.MovieStim(self.win, fileSelectDlg[0]) self.settings['attnGetterList'][ans2b['AGName']].update({'stimLoc': fileSelectDlg[0], 'stimName': namething, 'stimDur': tempStim.duration}) @@ -3491,7 +3495,6 @@ def condSetter(self, shuffleList, cond='NEW', ex=False, blockmode=False): overflow=newFlowLocs, types=tempStims, trials=False, conlines=blockmode) invisStims.append(stims['labels'][j]) - while self.mouse.isPressedIn(stims['shapes'][j], buttons=[0]): # waits until the mouse is released before continuing. pass elif stims['labels'][j] in invisStims: diff --git a/PyHabDemo/PyHab/PyHabClass.py b/PyHabDemo/PyHab/PyHabClass.py index 9d085d2..5b4c910 100644 --- a/PyHabDemo/PyHab/PyHabClass.py +++ b/PyHabDemo/PyHab/PyHabClass.py @@ -11,6 +11,7 @@ prefs.general['audioDevice'] = ['Built-in Output'] from psychopy import sound import numpy as np +from PIL import Image import pyglet from pyglet import input as pyglet_input import wx, random, csv @@ -312,8 +313,8 @@ def __init__(self, settingsDict, testMode = False): self.trialTiming = [] # new in 0.10.3: Records all stimulus start and end times relative to start of experiment. self.absoluteStart = 0 # A value for recording the start time of the experiment for trialTiming. if not self.stimPres and not testMode: - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) - self.endHabSound = sound.Sound('G', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) + self.endHabSound = sound.Sound('G', octave=4, sampleRate=48000, secs=0.2, stereo=True) if type(self.maxDur) is int: # Secretly MaxDur will always be a dict, but if it's a constant we just create the dict here tempDur = self.maxDur self.maxDur = {} # create a dict @@ -325,6 +326,7 @@ def __init__(self, settingsDict, testMode = False): self.testOffset = 0 self.frameCount = {'C':0,'L':0,'R':0} # the frame counter for the trial. Redone so it works for each screen. self.pauseCount = {'C':0,'L':0,'R':0} # used for ISI calculations + self.startPause = {'C':0, 'L':0, 'R':0} self.stimName = '' # used for adding the name of the stimulus file to the output. self.key = pyglet.window.key # This initiates the keyhandler. Here so we can then set the relevant keys. self.secondKey = self.key.L @@ -605,7 +607,7 @@ def checkStop(self, blockName): for i in [0, 1, 2]: core.wait(.25) # an inadvertent side effect of playing the sound is a short pause before the test trial can begin self.endHabSound.play() - self.endHabSound = sound.Sound('G', octave=4, sampleRate=44100, secs=0.2) + self.endHabSound = sound.Sound('G', octave=4, sampleRate=48000, secs=0.2, stereo=True) self.habMetWhen[blockName] = self.habCount[blockName] return True elif self.blockList[blockName]['setCritType'] == 'Threshold' and self.blockList[blockName]['maxHabSet'] > 0 and self.habSetWhen[blockName] == -1 and self.habCount[blockName] >= self.blockList[blockName]['maxHabSet']: @@ -852,13 +854,16 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False): attnGetter['file'].stop(reset=True) break else: - dMovie = attnGetter['file'] - dMovie.seek(0.0) + dMovie = attnGetter['file'] # this is always a movie file + firstImage = attnGetter['firstFrameImage'] + #dMovie.seek(0.0) # The reset has been moved elsewhere if attnGetter['stimType'] == 'Movie + Audio': attnGetter['audioFile'].play() + if midTrial: # with mid-trial AGs, want to restart from the right point in the movie. + stimFrameCount = self.frameCount['C'] self.frameCount['C'] = 0 self.ISI['NobodyNameTheirTrialTypeThis'] = 0.0 # A goofy solution but it'll work. dispMovieStim requires a trial type, and the ISI for an attngetter needs to be 0. - while self.dispMovieStim('NobodyNameTheirTrialTypeThis', dMovie) < 2: + while self.dispMovieStim('NobodyNameTheirTrialTypeThis', dMovie, firstImage) < 2: self.statusSquareA.draw() self.statusTextA.draw() self.statusSquareB.draw() @@ -881,7 +886,9 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False): elif core.getTime() - onCheck > onmin: if attnGetter['stimType'] == 'Movie + Audio': attnGetter['audioFile'].stop(reset=True) - dMovie.pause() + dMovie.pause() # because we can't handle this w/dispMovieStim, we need to do the setup for the seek here. + dMovie.seek(0.0) + dMovie._player._tStream._player.set_mute(True) # Force mute break elif cutoff and onCheck > 0: # A clever little way to say "if they aren't looking but were earlier" self.statusSquareA.fillColor='blue' @@ -892,8 +899,12 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False): if attnGetter['stimType'] == 'Movie + Audio': attnGetter['audioFile'].stop(reset=True) dMovie.pause() + dMovie.seek(0.0) + dMovie._player._tStream._player.set_mute(True) # Force mute break + if midTrial: + self.frameCount['C'] = stimFrameCount if 'bgColor' in attnGetter.keys(): if attnGetter['bgColor'] != 'default': self.win.setColor(self.screenColor['C']) @@ -988,14 +999,18 @@ def dispCoderWindow(self, trialType = -1): self.readyText.draw() self.win2.flip() # flips the status screen without delaying the stimulus onset. - def dispMovieStim(self, trialType, dispMovie, screen='C'): + def dispMovieStim(self, trialType, dispMovie, firstFrame, trialNum = -1, screen='C'): """ Draws movie stimuli to the stimulus display, including movie-based attention-getters. :param trialType: 0 for paused, otherwise a string :type trialType: int or str - :param dispMovie: The moviestim3 object for the stimuli - :type dispMovie: moviestim3 object + :param dispMovie: The moviestim object for the stimuli + :type dispMovie: moviestim object + :param firstFrame: An ImageStim with the first frame of the video, used to smooth out restarts. + :type firstFrame: ImageStim or None + :param trialNum: To record timing, we need the trial number. -1 indicates that it's an attention-getter or otherwise irrelevant. + :type trialNum: int :param screen: The screen on which the movie should display. Only relevant for HPP. :type screen: str :return: an int specifying whether the movie is in progress (0), paused on its last frame (1), or ending and looping (2) @@ -1018,45 +1033,79 @@ def dispMovieStim(self, trialType, dispMovie, screen='C'): fps = 1/dispMovie.frameRate - if self.frameCount[screen] == 0: # initial setup + if self.frameCount[screen] == 0: # initial setup and on rewind self.dummyThing.draw() self.frameCount[screen] += 1 - dispMovie.seek(0.0) # Moved up here from below so that it CAN loop at all + # The fundamental problem is that seek takes a few frames, so we need to ensure that it waits + if dispMovie.frameIndex > 0: # It should be the first frame. If not, first frame image. + firstFrame.draw() + else: + dispMovie.draw() + if trialType == 0: self.frameCount[screen] = 0 # for post-attn-getter pause dispMovie.pause() - dispMovie.draw() + w.flip() return 0 elif self.frameCount[screen] == 1: # print('playing') - dispMovie.play() - dispMovie.draw() + if not dispMovie.isPlaying: + dispMovie.play() # It's hard to record the timing for the video start w/out the trial number + if dispMovie.frameIndex > 1: # Shouldn't be needed for the first time something plays, at least. + # Need to call this again because the first "seek" at the end doesn't actually "take" in the way + # you want it to and would mess with the sound playback if there's sound in the first 100ms or so. + dispMovie.seek(0.0) + # Failsafe to prevent stuttering self.frameCount[screen] += 1 + if dispMovie.frameIndex > 1: + dispMovie.updateVideoFrame() # This forces it to advance until the "seek" takes. + firstFrame.draw() + self.frameCount[screen] = 1 # Stick here until we actually get the playback working. + else: + # Record actual movie start time + if trialNum > 0: + tempTiming = {'trialNum': trialNum, 'trialType': trialType, 'event': 'startMoviePlayback', + 'time': (core.getTime() - self.absoluteStart)} + self.trialTiming.append(tempTiming) + if self.eyetracker > 0: + self.tracker.record_event('trial_' + str(trialNum) + '_' + tempTiming['trialType'] + '_startMoviePlayback') + dispMovie.draw() w.flip() return 0 - elif playTime >= dispMovie.duration - fps*2 and self.pauseCount[screen] < self.ISI[trialType] * 60: # pause, check for ISI. + elif dispMovie.isFinished and self.pauseCount[screen] < self.ISI[trialType]: # pause, check for ISI. self.dummyThing.draw() - dispMovie.pause() + #dispMovie.pause() # This resets the isFinished state, so it can't be used here, but is no longer needed because it pauses anyway on the finish. dispMovie.draw() # might want to have it vanish rather than leave it on the screen for the ISI, in which case comment out this line. self.frameCount[screen] += 1 - self.pauseCount[screen] += 1 - w.flip() # TODO: Goes blank if ISI is long enough. Pyglet problem. + self.pauseCount[screen] = core.getTime() - self.startPause[screen] + w.flip() # TODO: Goes blank if ISI is long enough. Pyglet problem? return 1 - elif playTime >= dispMovie.duration - fps*2 and self.pauseCount[screen] >= self.ISI[trialType] * 60: # MovieStim's Loop functionality can't do an ISI + elif dispMovie.isFinished and self.pauseCount[screen] >= self.ISI[trialType]: # If both are 0 then this is fine. + dispMovie.pause() # Necessary for a silent reset, also resets the "isFinished" status. + dispMovie.seek(0.0) # In 2024 PsychoPy this is now when we want to seek to the start. + dispMovie._player._tStream._player.set_mute(True) # Force mute so the first sound does not replay. self.dummyThing.draw() # print('repeating at ' + str(dispMovie.getCurrentFrameTime())) self.frameCount[screen] = 0 # changed to 0 to better enable studies that want to blank between trials self.pauseCount[screen] = 0 + self.startPause[screen] = 0 # Reset everything dispMovie.draw() # Comment this out as well to blank between loops. w.flip() - dispMovie.pause() - #dispMovie.seek(0.0) #This seek seems to cause the replays. + if trialNum > 0: + tempTiming = {'trialNum': trialNum, 'trialType': trialType, 'event': 'endMoviePlayback', + 'time': (core.getTime() - self.absoluteStart)} + self.trialTiming.append(tempTiming) + if self.eyetracker > 0: + self.tracker.record_event('trial_' + str(trialNum) + '_' + tempTiming['trialType'] + '_endMoviePlayback') return 2 else: dispMovie.draw() self.frameCount[screen] += 1 w.flip() + # This looks weird but if you are using ISI it is necessary to get accurate timing. + if self.ISI[trialType] > 0: + self.startPause[screen] = core.getTime() return 0 def dispImageStim(self, dispImage, screen='C'): @@ -1098,10 +1147,10 @@ def dispAudioStim(self, trialType, dispAudio): self.frameCount['C'] = 1 return 0 elif self.frameCount['C'] == 1: - if dispAudio.status not in [STARTED, PLAYING] and self.pauseCount['C'] < self.ISI[trialType] * 60: + if dispAudio.isPlaying == False and self.pauseCount['C'] < self.ISI[trialType] * 60: self.pauseCount['C'] += 1 return 1 - elif dispAudio.status not in [STARTED, PLAYING] and self.pauseCount['C'] >= self.ISI[trialType] * 60: + elif dispAudio.isPlaying == False and self.pauseCount['C'] >= self.ISI[trialType] * 60: self.frameCount['C'] = 0 return 2 else: @@ -1158,7 +1207,7 @@ def dispAnimationStim(self, trialType, dispAnim, screen='C'): return 1 - def dispTrial(self, trialType, dispMovie = False): #If no stim, dispMovie defaults to false. + def dispTrial(self, trialType, dispMovie = False, trialNum=-1): #If no stim, dispMovie defaults to false. """ Draws each frame of the trial. For stimPres, returns a movie-status value for determining when the movie has ended @@ -1167,6 +1216,8 @@ def dispTrial(self, trialType, dispMovie = False): #If no stim, dispMovie defaul :type trialType: string :param dispMovie: A dictionary containing both the stimulus type and the object with the stimulus file(s) (if applicable) :type dispMovie: bool or dict + :param trialNum: Trial number, relevant for recording timing of movie playback. -1 for attngetters and other cases where this is irrelevant. + :type trialNum: int :return: 1 or 0. 1 = end of movie for trials that end on that. :rtype: int """ @@ -1174,7 +1225,7 @@ def dispTrial(self, trialType, dispMovie = False): #If no stim, dispMovie defaul # now for the test trial display if self.stimPres: if dispMovie['stimType'] == 'Movie': - t = self.dispMovieStim(trialType, dispMovie['stim']) + t = self.dispMovieStim(trialType, dispMovie['stim'], dispMovie['firstFrame'], trialNum=trialNum) elif dispMovie['stimType'] == 'Animation': t = self.dispAnimationStim(trialType, dispMovie['stim']) elif dispMovie['stimType'] == 'Image': @@ -1903,9 +1954,9 @@ def doTrial(self, number, ttype, disMovie): self.frameCount['C'] = 0 # reset display self.pauseCount['C'] = 0 # needed for ISI # returns 0 if do next trial, 1 if end hab, 2 if end experiment, 3 if abort/abort - if self.stimPres and disMovie['stimType'] == 'Movie': - disMovie['stim'].seek(0.0) - disMovie['stim'].pause() +# if self.stimPres and disMovie['stimType'] == 'Movie': +# disMovie['stim'].seek(0.0) +# disMovie['stim'].pause() startTrial = core.getTime() startTrial2 = core.getTime() onArray = [] @@ -1980,7 +2031,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) # determine if they were looking or not at end of trial and update appropriate array if gazeOn: onDur = endTrial - startOn @@ -2021,7 +2072,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) # determine if they were looking or not at end of trial and update appropriate array if gazeOn: onDur = endTrial - startOn @@ -2066,7 +2117,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) endOff = nowOff offDur = nowOff - startOff tempGazeArray = {'trial':number, 'trialType':dataType, 'startTime':startOff, 'endTime':endOff, 'duration':offDur} @@ -2081,15 +2132,15 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea tempGazeArray = {'trial':number, 'trialType':dataType, 'startTime':startOff, 'endTime':endOff, 'duration':offDur} offArray.append(tempGazeArray) if localType in self.dynamicPause and self.stimPres: - if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].status != PLAYING: + if disMovie['stimType'] in ['Movie', 'Audio'] and not disMovie['stim'].isPlaying: disMovie['stim'].play() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].status != PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and not disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].play() else: if localType in self.dynamicPause and self.stimPres: - if disMovie['stimType'] in ['Movie','Audio'] and disMovie['stim'].status == PLAYING: + if disMovie['stimType'] in ['Movie','Audio'] and disMovie['stim'].isPlaying: disMovie['stim'].pause() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].status == PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].pause() if localType in self.midAG and self.stimPres: try: @@ -2102,9 +2153,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if nowOff - startAG >= self.midAG[localType]['trigger']: # TODO: Do something here to deal with recording data about mid-trial AG behavior? if localType not in self.dynamicPause: # Need to pause it anyways to play the AG so they don't overlap - if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].status == PLAYING: + if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].isPlaying: disMovie['stim'].pause() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].status == PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].pause() startAG = core.getTime() - startTrial tempTiming = {'trialNum': number, 'trialType': dataType, 'event': 'startAttnGetter', @@ -2121,10 +2172,10 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea self.tracker.record_event('trial_' + str(number) + '_' + tempTiming['trialType'] + '_endAttnGetter') durAG = endAG - startAG maxDurAdd = maxDurAdd + durAG # Increase max length of trial by duration that AG played. - if localType not in self.dynamicPause: - if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].status != PLAYING: + if localType not in self.dynamicPause: # Note that this only trips if it is not in the "dynamic pause" category + if disMovie['stimType'] in ['Movie', 'Audio'] and not disMovie['stim'].isPlaying: disMovie['stim'].play() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].status != PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and not disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].play() elif gazeOn: @@ -2138,7 +2189,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) endOn = core.getTime() - startTrial onDur = endOn - startOn tempGazeArray = {'trial':number, 'trialType':dataType, 'startTime':startOn, 'endTime':endOn, 'duration':onDur} @@ -2152,7 +2203,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) endOn = core.getTime() - startTrial onDur = endOn - startOn tempGazeArray = {'trial':number, 'trialType':dataType, 'startTime':startOn, 'endTime':endOn, 'duration':onDur} @@ -2185,7 +2236,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea tempGazeArray2 = {'trial':number, 'trialType':dataType, 'startTime':startOn2, 'endTime':endOn2, 'duration':onDur2} onArray2.append(tempGazeArray2) sumOn2 = sumOn2 + onDur2 - movieStatus = self.dispTrial(localType, disMovie) + movieStatus = self.dispTrial(localType, disMovie, trialNum=number) if endFlag: if localType in self.movieEnd and movieStatus >= 1: runTrial = False @@ -2239,8 +2290,10 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if self.stimPres: # Reset everything, stop playing sounds and movies. if disMovie['stimType'] == 'Movie': + if disMovie['stim'].isPlaying: + disMovie['stim'].pause() disMovie['stim'].seek(0.0) - disMovie['stim'].pause() + disMovie['stim']._player._tStream._player.set_mute(True) elif disMovie['stimType'] == 'Audio': disMovie['stim'].stop() elif disMovie['stimType'] == 'Image with audio': @@ -2264,8 +2317,10 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea abort = True if abort: # if the abort button was pressed if self.stimPres and disMovie['stimType'] == 'Movie': + if disMovie['stim'].isPlaying: + disMovie['stim'].pause() disMovie['stim'].seek(0.0) - disMovie['stim'].pause() + disMovie['stim']._player._tStream._player.set_mute(True) self.abortTrial(onArray, offArray, number, dataType, onArray2, offArray2, self.stimName, habDataRec, habCrit) return 3 else: @@ -3131,7 +3186,7 @@ def loadStim(self, stim, screen='C'): w = self.winL elif screen == 'R': w = self.winR - + firstFrameImage = None if tempStim['stimType'] == 'Movie': # It's finally time to switch to the new MovieStim, if we're on a sufficient version of PsychoPy. if eval(__version__[0:4]) < 2023: @@ -3143,6 +3198,11 @@ def loadStim(self, stim, screen='C'): tempStimObj = visual.MovieStim(w, tempStim['stimLoc'], size=[self.movieWidth[screen], self.movieHeight[screen]], flipHoriz=False, flipVert=False, loop=False) + # First-frame extraction. This is needed to solve the stuttering problem, + # but introduces a number of downstream consequences. + firstFrameImgTmp = Image.frombytes('RGBA',tempStimObj.frameSize, + tempStimObj.updateVideoFrame().colorData,'raw', 'BGRA') + firstFrameImage = visual.ImageStim(w, image=firstFrameImgTmp, size=[self.movieWidth[screen], self.movieHeight[screen]]) elif tempStim['stimType'] == 'Animation': tempStimObj = tempStim['stimLoc'] # in this case it's just a string referencing a custom function elif tempStim['stimType'] == 'Image': @@ -3155,7 +3215,7 @@ def loadStim(self, stim, screen='C'): imageObj = visual.ImageStim(w, tempStim['imageLoc'], size=[self.movieWidth[screen], self.movieHeight[screen]]) tempStimObj = {'Audio': audioObj, 'Image': imageObj} - tempAdd = {'stimType': tempStim['stimType'], 'stim': tempStimObj} + tempAdd = {'stimType': tempStim['stimType'], 'stim': tempStimObj, 'firstFrame':firstFrameImage} return tempAdd def TrackerCalibrateValidate(self): @@ -3292,9 +3352,21 @@ def SetupWindow(self): if self.attnGetterList[i]['stimType'] == 'Audio': self.attnGetterList[i]['file'] = sound.Sound(self.attnGetterList[i]['stimLoc']) else: - self.attnGetterList[i]['file'] = visual.MovieStim3(self.win, self.attnGetterList[i]['stimLoc'], + if eval(__version__[0:4]) < 2023: + self.attnGetterList[i]['file'] = visual.MovieStim3(self.win, self.attnGetterList[i]['stimLoc'], + size=[self.movieWidth['C'], self.movieHeight['C']], + flipHoriz=False, flipVert=False, loop=False) + else: + self.attnGetterList[i]['file'] = visual.MovieStim(self.win, self.attnGetterList[i]['stimLoc'], size=[self.movieWidth['C'], self.movieHeight['C']], flipHoriz=False, flipVert=False, loop=False) + # For loading the first frame of an attention-getter. Necessary for smooth resets. + tmpFirstFrame = Image.frombytes('RGBA', self.attnGetterList[i]['file'].frameSize, + self.attnGetterList[i]['file'].updateVideoFrame().colorData, + 'raw', 'BGRA') + self.attnGetterList[i]['firstFrameImage'] = visual.ImageStim(self.win, image=tmpFirstFrame, + size=[self.movieWidth['C'], + self.movieHeight['C']]) if self.attnGetterList[i]['stimType'] == 'Movie + Audio': self.attnGetterList[i]['audioFile'] = sound.Sound(self.attnGetterList[i]['audioLoc']) if self.endImage != '': # Load image for end of experiment, if needed. diff --git a/PyHabDemo/PyHab/PyHabClassHPP.py b/PyHabDemo/PyHab/PyHabClassHPP.py index 62e8dfb..8a59eff 100644 --- a/PyHabDemo/PyHab/PyHabClassHPP.py +++ b/PyHabDemo/PyHab/PyHabClassHPP.py @@ -284,7 +284,7 @@ def dispCoderWindow(self, trialType = -1): self.readyText.draw() self.win2.flip() # flips the status screen without delaying the stimulus onset. - def dispTrial(self, trialType, dispMovie = False): + def dispTrial(self, trialType, dispMovie = False, trialNum=-1): """ An HPP-specific version of dispTrial that can display on multiple things, read the new stimDict, etc. @@ -292,6 +292,8 @@ def dispTrial(self, trialType, dispMovie = False): :type trialType: str :param dispMovie: Now a dictionary C/L/R each index of which contains what this function expects in the base class :type dispMovie: bool or dict + :param trialNum: Trial number for movie playback timing. -1 for attention-getter and other cases where it is irrelevant + :type trialNum: int :return: 1 or 0. 1 = end of movie for trials that end on that. TODO: for HPP currently returns 1 if EVERYTHING IN IT is done. :rtype: int """ @@ -306,7 +308,7 @@ def dispTrial(self, trialType, dispMovie = False): t = [] for k in screens: if dispMovie[k]['stimType'] == 'Movie': - t.append(self.dispMovieStim(trialType, dispMovie[k]['stim'], screen=k)) + t.append(self.dispMovieStim(trialType, dispMovie[k]['stim'], dispMovie[k]['firstFrame'], trialNum=trialNum, screen=k)) elif dispMovie[k]['stimType'] == 'Image': t.append(self.dispImageStim(dispMovie[k]['stim'], screen=k)) elif dispMovie[k]['stimType'] == 'Audio' and trialType != 0: # No still-frame equivalent @@ -502,7 +504,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC or gazeOnL or gazeOnR: if gazeOnC: onDurC = endTrial - startOnC @@ -577,7 +579,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC or gazeOnL or gazeOnR: if gazeOnC: onDurC = endTrial - startOnC @@ -642,7 +644,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) endOff = nowOff offDur = nowOff - dataStartOff tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOff, @@ -662,9 +664,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType in self.dynamicPause and self.stimPres: # Let's get fancy: This now only starts playing the thing on the screen they're looking at! if disMovie['C'] != 0: - if disMovie['C']['stimType'] in ['Movie', 'Audio'] and disMovie['C']['stim'].status != PLAYING: + if disMovie['C']['stimType'] in ['Movie', 'Audio'] and not disMovie['C']['stim'].isPlaying: disMovie['C']['stim'].play() - elif disMovie['C']['stimType'] == ['Image with audio'] and disMovie['C']['stim']['Audio'].status != PLAYING: + elif disMovie['C']['stimType'] == ['Image with audio'] and not disMovie['C']['stim']['Audio'].isPlaying: disMovie['C']['stim']['Audio'].play() elif self.keyboard[self.leftKey]: gazeOnL = True @@ -679,9 +681,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType in self.dynamicPause and self.stimPres: # Let's get fancy: This now only starts playing the thing on the screen they're looking at! if disMovie['L'] != 0: - if disMovie['L']['stimType'] in ['Movie', 'Audio'] and disMovie['L']['stim'].status != PLAYING: + if disMovie['L']['stimType'] in ['Movie', 'Audio'] and not disMovie['L']['stim'].isPlaying: disMovie['L']['stim'].play() - elif disMovie['L']['stimType'] == ['Image with audio'] and disMovie['L']['stim']['Audio'].status != PLAYING: + elif disMovie['L']['stimType'] == ['Image with audio'] and not disMovie['L']['stim']['Audio'].isPlaying: disMovie['L']['stim']['Audio'].play() elif self.keyboard[self.rightKey]: gazeOnR = True @@ -696,9 +698,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType in self.dynamicPause and self.stimPres: # Let's get fancy: This now only starts playing the thing on the screen they're looking at! if disMovie['R'] != 0: - if disMovie['R']['stimType'] in ['Movie', 'Audio'] and disMovie['R']['stim'].status != PLAYING: + if disMovie['R']['stimType'] in ['Movie', 'Audio'] and not disMovie['R']['stim'].isPlaying: disMovie['R']['stim'].play() - elif disMovie['R']['stimType'] == ['Image with audio'] and disMovie['R']['stim']['Audio'].status != PLAYING: + elif disMovie['R']['stimType'] == ['Image with audio'] and not disMovie['R']['stim']['Audio'].isPlaying: disMovie['R']['stim']['Audio'].play() else: if localType in self.midAG and self.stimPres: @@ -707,9 +709,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType not in self.dynamicPause: # Need to pause it anyways to play the AG so they don't overlap for i,j in disMovie.items(): if j != 0: - if j['stimType'] in ['Movie', 'Audio'] and j['stim'].status == PLAYING: + if j['stimType'] in ['Movie', 'Audio'] and j['stim'].isPlaying: j['stim'].pause() - elif j['stimType'] == ['Image with audio'] and j['stim']['Audio'].status == PLAYING: + elif j['stimType'] == ['Image with audio'] and j['stim']['Audio'].isPlaying: j['stim']['Audio'].pause() startAG = core.getTime() tempTiming = {'trialNum': number, 'trialType': dataType, 'event': 'startAttnGetter', @@ -724,9 +726,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType not in self.dynamicPause: for i,j in disMovie.items(): if j != 0: - if j['stimType'] in ['Movie', 'Audio'] and j['stim'].status != PLAYING: + if j['stimType'] in ['Movie', 'Audio'] and not j['stim'].isPlaying: j['stim'].play() - elif j['stimType'] == ['Image with audio'] and j['stim']['Audio'].status != PLAYING: + elif j['stimType'] == ['Image with audio'] and not j['stim']['Audio'].isPlaying: j['stim']['Audio'].play() elif gazeOnC or gazeOnL or gazeOnR: nowOn = core.getTime() - startTrial @@ -758,7 +760,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC: onDur = endTrial - startOnC tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOnC, @@ -783,7 +785,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC: onDur = endTrial - startOnC tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOnC, @@ -840,7 +842,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC: onDur = endTrial - startOnC tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOnC, @@ -862,9 +864,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType not in self.dynamicPause: # Need to pause it anyways to play the AG so they don't overlap for i, j in disMovie.items(): if j != 0: - if j['stimType'] in ['Movie', 'Audio'] and j['stim'].status == PLAYING: + if j['stimType'] in ['Movie', 'Audio'] and j['stim'].isPlaying: j['stim'].pause() - elif j['stimType'] == ['Image with audio'] and j['stim']['Audio'].status == PLAYING: + elif j['stimType'] == ['Image with audio'] and j['stim']['Audio'].isPlaying: j['stim']['Audio'].pause() startAG = core.getTime() self.attnGetter(localType, self.midAG[localType]['cutoff'], @@ -874,10 +876,10 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if localType not in self.dynamicPause: for i, j in disMovie.items(): if j != 0: - if j['stimType'] in ['Movie', 'Audio'] and j['stim'].status != PLAYING: + if j['stimType'] in ['Movie', 'Audio'] and not j['stim'].isPlaying: j['stim'].play() - elif j['stimType'] == ['Image with audio'] and j['stim'][ - 'Audio'].status != PLAYING: + elif j['stimType'] == ['Image with audio'] and not j['stim'][ + 'Audio'].isPlaying: j['stim']['Audio'].play() @@ -891,9 +893,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea sumOnC = sumOnC + onDur if localType in self.dynamicPause and self.stimPres: if disMovie['C'] != 0: - if disMovie['C']['stimType'] in ['Movie','Audio'] and disMovie['C']['stim'].status == PLAYING: + if disMovie['C']['stimType'] in ['Movie','Audio'] and disMovie['C']['stim'].isPlaying: disMovie['C']['stim'].pause() - elif disMovie['C']['stimType'] == ['Image with audio'] and disMovie['C']['stim']['Audio'].status == PLAYING: + elif disMovie['C']['stimType'] == ['Image with audio'] and disMovie['C']['stim']['Audio'].isPlaying: disMovie['C']['stim']['Audio'].pause() if self.keyboard[self.leftKey]: gazeOnL = True @@ -925,9 +927,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea sumOnL = sumOnL + onDur if localType in self.dynamicPause and self.stimPres: if disMovie['L'] != 0: - if disMovie['L']['stimType'] in ['Movie','Audio'] and disMovie['L']['stim'].status == PLAYING: + if disMovie['L']['stimType'] in ['Movie','Audio'] and disMovie['L']['stim'].isPlaying: disMovie['L']['stim'].pause() - elif disMovie['L']['stimType'] == ['Image with audio'] and disMovie['L']['stim']['Audio'].status == PLAYING: + elif disMovie['L']['stimType'] == ['Image with audio'] and disMovie['L']['stim']['Audio'].isPlaying: disMovie['L']['stim']['Audio'].pause() if self.keyboard[self.centerKey]: gazeOnC = True @@ -959,9 +961,9 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea sumOnR = sumOnR + onDur if localType in self.dynamicPause and self.stimPres: if disMovie['R'] != 0: - if disMovie['R']['stimType'] in ['Movie','Audio'] and disMovie['R']['stim'].status == PLAYING: + if disMovie['R']['stimType'] in ['Movie','Audio'] and disMovie['R']['stim'].isPlaying: disMovie['R']['stim'].pause() - elif disMovie['R']['stimType'] == ['Image with audio'] and disMovie['R']['stim']['Audio'].status == PLAYING: + elif disMovie['R']['stimType'] == ['Image with audio'] and disMovie['R']['stim']['Audio'].isPlaying: disMovie['R']['stim']['Audio'].pause() if self.keyboard[self.centerKey]: gazeOnC = True @@ -990,7 +992,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC: onDur = endTrial - startOnC tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOnC, @@ -1017,7 +1019,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOnC: onDur = endTrial - startOnC tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOnC, diff --git a/PyHabDemo/PyHab/PyHabClassPL.py b/PyHabDemo/PyHab/PyHabClassPL.py index 2d61bf3..475ce40 100644 --- a/PyHabDemo/PyHab/PyHabClassPL.py +++ b/PyHabDemo/PyHab/PyHabClassPL.py @@ -327,7 +327,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) #determine if they were looking or not at end of trial and update appropriate array if gazeOn or gazeOn2: if gazeOn: @@ -385,7 +385,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) #determine if they were looking or not at end of trial and update appropriate array if gazeOn or gazeOn2: if gazeOn: @@ -440,7 +440,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) endOff = nowOff offDur = nowOff - startOff tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOff, 'endTime': endOff, @@ -451,20 +451,19 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea if np.nan in gpos: #gaze off if localType in self.dynamicPause and self.stimPres: - if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].status == PLAYING: + if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].isPlaying: disMovie['stim'].pause() elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim'][ - 'Audio'].status == PLAYING: + 'Audio'].isPlaying: disMovie['stim']['Audio'].pause() if localType in self.midAG and self.stimPres: if nowOff - startOff >= self.midAG[localType]['trigger']: # TODO: Do something here to deal with recording data about mid-trial AG behavior? if localType not in self.dynamicPause: # Need to pause it anyways to play the AG so they don't overlap if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie[ - 'stim'].status == PLAYING: + 'stim'].isPlaying: disMovie['stim'].pause() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim'][ - 'Audio'].status == PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].pause() tempTiming = {'trialNum': number, 'trialType': dataType, 'event': 'startAttnGetter', 'time': (core.getTime() - self.absoluteStart)} @@ -557,17 +556,17 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea disMovie['stim']['Audio'].play() else: if localType in self.dynamicPause and self.stimPres: - if disMovie['stimType'] in ['Movie','Audio'] and disMovie['stim'].status == PLAYING: + if disMovie['stimType'] in ['Movie','Audio'] and disMovie['stim'].isPlaying: disMovie['stim'].pause() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].status == PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].pause() if localType in self.midAG and self.stimPres: if nowOff - startOff >= self.midAG[localType]['trigger']: # TODO: Do something here to deal with recording data about mid-trial AG behavior? if localType not in self.dynamicPause: # Need to pause it anyways to play the AG so they don't overlap - if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].status == PLAYING: + if disMovie['stimType'] in ['Movie', 'Audio'] and disMovie['stim'].isPlaying: disMovie['stim'].pause() - elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].status == PLAYING: + elif disMovie['stimType'] == ['Image with audio'] and disMovie['stim']['Audio'].isPlaying: disMovie['stim']['Audio'].pause() tempTiming = {'trialNum': number, 'trialType': dataType, 'event': 'startAttnGetter', 'time': (core.getTime() - self.absoluteStart)} @@ -603,7 +602,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOn: onDur = endTrial - startOn tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOn, 'endTime': endTrial, @@ -622,7 +621,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) if gazeOn: onDur = endTrial - startOn tempGazeArray = {'trial': number, 'trialType': dataType, 'startTime': startOn, @@ -715,14 +714,14 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea else: numOff = numOff + 1 startOff = core.getTime() - startTrial - movieStatus = self.dispTrial(localType, disMovie) + movieStatus = self.dispTrial(localType, disMovie, trialNum=number) if endFlag: if localType in self.movieEnd and movieStatus >= 1: runTrial = False endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) # determine if they were looking or not at end of trial and update appropriate array if gazeOn: onDur = endTrial - startOn @@ -745,7 +744,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea endTrial = core.getTime() - startTrial if not self.stimPres: self.endTrialSound.play() - self.endTrialSound = sound.Sound('A', octave=4, sampleRate=44100, secs=0.2) + self.endTrialSound = sound.Sound('A', octave=4, sampleRate=48000, secs=0.2, stereo=True) # determine if they were looking or not at end of trial and update appropriate array if gazeOn: onDur = endTrial - startOn