Audio end doesn't end routine - Builder v 2026.2.4

OS (Win10):
PsychoPy version (2026.2.4 Py 3.10.11):
Standard Standalone Installation? Yes
Do you want it to also run online? No
What are you trying to achieve?:
Hi! I am trying to create an experiment with auditory stimuli in Builder.
Each experimental trial is made up of 3 routines , as follows:

  • audio_1_routine → fixation cross appears at 0 s and stays visible throughout routine (stop duration is left blank). Audio file starts playing at 1 s (stop duration is left blank). The end of the audio file should end the routine (handled by code component)

  • audio_2_routine → at 0.5 s, both a text resource and a audio file are presented at the same time (stop duration is left blank). The end of the audio file should end the routine (handled by code component)

  • question_routine → a text component with a question appears and a keyboard response is needed to end the routine.

*The issue*: The problem I am having is that the end of the first audio file (i.e. audio file that plays in the routine “audio_1_routine”) will not end the routine. The audio plays correctly but then nothing happens when it is done playing, and the fixation cross stays visible on the screen past that point, without ever progressing to the next routine (“audio_2_routine”)…

What did you try to make it work?:
I am using the code solution I found in other posts to force the end of the audio to end the routine:

if sound.status == FINISHED:# "sound" replaced by name of my relevant component

continueRoutine = False

Previously, I had also tried ticking the “Force end of Routine” box option instead to end the routine (found in the sound resource’s Properties window, in the Playback tab). But that did not work either…

Any suggestions or tips I could try would be very helpful as I am stuck - many thanks in advance!

*Edit*: I also tried fixing the duration of the non-audio components within each routine (e.g. fixation cross as text resource in “audio_1_routine”) to ensure that the issue is not because these non-audio components can run indefinitely. Even after limiting the duration of the non-audio components so that they end before the audio even starts playing, I still have the issue that I can’t progress to the next routine once the audio file stops…

PS: relevant code from exp_lastrun.py
`# — Prepare to start Routine “audio_1_routine” —

create an object to store info about Routine audio_1_routine

audio_1_routine = data.Routine(
name=‘audio_1_routine’,
components=[fixation, sound_resource_1],
)
audio_1_routine.status = NOT_STARTED
continueRoutine = True

update component parameters for each repeat

fixation.setText(‘+’)
sound_resource_1.setSound(audio_1, hamming=True)
sound_resource_1.setVolume(1.0, log=False)
sound_resource_1.seek(0)

store start times for audio_1_routine

audio_1_routine.tStartRefresh = win.getFutureFlipTime(clock=globalClock)
audio_1_routine.tStart = globalClock.getTime(format=‘float’)
audio_1_routine.status = STARTED
thisExp.addData(‘audio_1_routine.started’, audio_1_routine.tStart)
audio_1_routine.maxDuration = None

keep track of which components have finished

audio_1_routineComponents = audio_1_routine.components
for thisComponent in audio_1_routine.components:
thisComponent.tStart = None
thisComponent.tStop = None
thisComponent.tStartRefresh = None
thisComponent.tStopRefresh = None
if hasattr(thisComponent, ‘status’):
thisComponent.status = NOT_STARTED

reset timers

t = 0
_timeToFirstFrame = win.getFutureFlipTime(clock=“now”)
frameN = -1

    # --- Run Routine "audio_1_routine" ---
    thisExp.currentRoutine = audio_1_routine
    audio_1_routine.forceEnded = routineForceEnded = not continueRoutine
    while continueRoutine:
        # if trial has changed, end Routine now
        if hasattr(thisTrial, 'status') and thisTrial.status == STOPPING:
            continueRoutine = False
        # get current time
        t = routineTimer.getTime()
        tThisFlip = win.getFutureFlipTime(clock=routineTimer)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame
        
        # *fixation* updates
        
        # if fixation is starting this frame...
        if fixation.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            fixation.frameNStart = frameN  # exact frame index
            fixation.tStart = t  # local t and not account for scr refresh
            fixation.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(fixation, 'tStartRefresh')  # time at next scr refresh
            # add timestamp to datafile
            thisExp.timestampOnFlip(win, 'fixation.started')
            # update status
            fixation.status = STARTED
            fixation.setAutoDraw(True)
        
        # if fixation is active this frame...
        if fixation.status == STARTED:
            # update params
            pass
        
        # *sound_resource_1* updates
        
        # if sound_resource_1 is starting this frame...
        if sound_resource_1.status == NOT_STARTED and tThisFlip >= 1-frameTolerance:
            # keep track of start time/frame for later
            sound_resource_1.frameNStart = frameN  # exact frame index
            sound_resource_1.tStart = t  # local t and not account for scr refresh
            sound_resource_1.tStartRefresh = tThisFlipGlobal  # on global time
            # add timestamp to datafile
            thisExp.addData('sound_resource_1.started', tThisFlipGlobal)
            # update status
            sound_resource_1.status = STARTED
            sound_resource_1.play(when=win)  # sync with win flip
        
        # if sound_resource_1 is stopping this frame...
        if sound_resource_1.status == STARTED:
            if bool(False) or sound_resource_1.isFinished:
                # keep track of stop time/frame for later
                sound_resource_1.tStop = t  # not accounting for scr refresh
                sound_resource_1.tStopRefresh = tThisFlipGlobal  # on global time
                sound_resource_1.frameNStop = frameN  # exact frame index
                # add timestamp to datafile
                thisExp.timestampOnFlip(win, 'sound_resource_1.stopped')
                # update status
                sound_resource_1.status = FINISHED
                sound_resource_1.stop()
        # Run 'Each Frame' code from stop_sound_resource_1
        if sound_resource_1.status == FINISHED:
            continueRoutine = False
        
        # check for quit (typically the Esc key)
        if defaultKeyboard.getKeys(keyList=["escape"]):
            thisExp.status = FINISHED
        if thisExp.status == FINISHED or endExpNow:
            endExperiment(thisExp, win=win)
            return
        # pause experiment here if requested
        if thisExp.status == PAUSED:
            pauseExperiment(
                thisExp=thisExp, 
                win=win, 
                timers=[routineTimer, globalClock], 
                currentRoutine=audio_1_routine,
            )
            # skip the frame we paused on
            continue
        
        # has a Component requested the Routine to end?
        if not continueRoutine:
            audio_1_routine.forceEnded = routineForceEnded = True
        # has the Routine been forcibly ended?
        if audio_1_routine.forceEnded or routineForceEnded:
            break
        # has every Component finished?
        continueRoutine = False
        for thisComponent in audio_1_routine.components:
            if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
                continueRoutine = True
                break  # at least one component has not yet finished
        
        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()
    
    # --- Ending Routine "audio_1_routine" ---
    for thisComponent in audio_1_routine.components:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # store stop times for audio_1_routine
    audio_1_routine.tStop = globalClock.getTime(format='float')
    audio_1_routine.tStopRefresh = tThisFlipGlobal
    thisExp.addData('audio_1_routine.stopped', audio_1_routine.tStop)
    sound_resource_1.pause()  # ensure sound has stopped at end of Routine
    # the Routine "audio_1_routine" was not non-slip safe, so reset the non-slip timer
    routineTimer.reset()`