Microphone recording not ending properly

Hello,

My experiment hasn’t been ending properly. Once finished, the green Run button is greyed out until I hit Stop. After configuring all of my libraries properly and installing the 1.84.01 (see here for my previous issues), I am still having problems. I have, however, narrowed it down.

I mostly built my experiment from the builder, but added a few things in the coder. Here is the block that is giving me trouble:

 # ------Prepare to start Routine "Q"-------
t = 0
QClock.reset()  # clock
frameN = -1
continueRoutine = True
# update component parameters for each repeat
current_question.setText(question)
go_on_Q = event.BuilderKeyResponse()
wavName = wavDirName+'/'+expInfo['participant']+'.'+expInfo['episode']+expInfo['condition']+'_'+str(Qnum)+'.wav'
record = microphone.AdvAudioCapture(name='record', filename=wavName, stereo=False)
# keep track of which components have finished
QComponents = [current_question, go_on_Q, t_text, record]
for thisComponent in QComponents:
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

# -------Start Routine "Q"-------
while continueRoutine:
    # get current time
    t = QClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *current_question* updates
    if t >= 0.0 and current_question.status == NOT_STARTED:
        # keep track of start time/frame for later
        current_question.tStart = t
        current_question.frameNStart = frameN  # exact frame index
        current_question.setAutoDraw(True)
    
    # *go_on_Q* updates
    if t >= 0.0 and go_on_Q.status == NOT_STARTED:
        # keep track of start time/frame for later
        go_on_Q.tStart = t
        go_on_Q.frameNStart = frameN  # exact frame index
        go_on_Q.status = STARTED
        # keyboard checking is just starting
        win.callOnFlip(go_on_Q.clock.reset)  # t=0 on next screen flip
        event.clearEvents(eventType='keyboard')
    if go_on_Q.status == STARTED:
        theseKeys = event.getKeys(keyList=['space'])
        
        # check for quit:
        if "escape" in theseKeys:
            endExpNow = True
        if len(theseKeys) > 0:  # at least one key was pressed
            go_on_Q.keys = theseKeys[-1]  # just the last key pressed
            go_on_Q.rt = go_on_Q.clock.getTime()
            # a response ends the routine
            continueRoutine = False

    # *t_text* updates
    if t >= 0.0 and t_text.status == NOT_STARTED:
        # keep track of start time/frame for later
        t_text.tStart = t
        t_text.frameNStart = frameN  # exact frame index
        t_text.setAutoDraw(True)
    
    # *record* updates
    if t >= 0.0 and record.status == NOT_STARTED:
        # keep track of start time/frame for later
        record.tStart = t
        record.frameNStart = frameN  # exact frame index
        record.status = STARTED
        record.record(sec=100, block=False)  # start the recording thread
    
    if record.status == STARTED and not record.recorder.running:
        record.status = FINISHED
    
    # check if all components have finished
    if not continueRoutine:  # a component has requested a forced-end of Routine
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in QComponents:
        if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # at least one component has not yet finished
    
    # check for quit (the Esc key)
    if endExpNow or event.getKeys(keyList=["escape"]):
        core.quit()
    
    # refresh the screen
    if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
        win.flip()

# -------Ending Routine "Q"-------
for thisComponent in QComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
# check responses
if go_on_Q.keys in ['', [], None]:  # No response was made
    go_on_Q.keys=None
questions.addData('go_on_Q.keys',go_on_Q.keys)
if go_on_Q.keys != None:  # we had a response
    questions.addData('go_on_Q.rt', go_on_Q.rt)
# record stop & responses
record.stop()  # sometimes helpful
Qnum = int(Qnum) + 1
if not record.savedFile:
    record.savedFile = None
# store data for questions (TrialHandler)
questions.addData('record.filename', record.savedFile)
# the Routine "Q" was not non-slip safe, so reset the non-slip timer
routineTimer.reset()
# completed 1 repeats of 'questions'

Once the experiment is over, it does not properly close out. I have narrow it down to this line:

record.record(sec=100, block=False)  # start the recording thread

The experiment doesn’t end because it appears to be waiting for the duration to finish. For example, if I change sec=1, the experiment ends very quickly. However, I thought that if you call record.stop() then it would end the recording early. I am recording verbal response to questions here and the answers vary in durations, thus I choose sec=100 as a long duration knowing that I could end it early.
Any idea why this isn’t ending properly? I have also attached my full presentation script as well.

Thanks you,

Dustin

TRW_comp.py (33.9 KB)

1 Like

Are you on a Mac? I’m guessing your sound lib is set to be ‘pyo’ (psychopy prefs)? In recent versions pyo on the mac was failing to shut down. Try switching the order of the audio driver (coreaudio and portaudio) also in the prefs

Hi Jon -

Yes, I am on a Macbook, OS X 10.11.6.

I have tried all variations of audio settings:

audio library = [‘pysoundcard’,‘pygame’] OR [‘pygame’,‘pysoundcard’]
and
audio driver = [‘coreaudio’,‘portaudio’] OR [‘portaudio’,‘coreaudio’]

And the problem still persists. Regardless of the above audio settings, the recording appears to want to finish the duration I specify in sec, even if I end the recording early.

EDIT: Also, I get a dialog box after the audio ends saying ‘PsychoPy2 quit unexpectedly’

Also, every now and then, I can’t predict it, but I get a message that says:

Fatal Python error: (pygame parachute) Segmentation Fault

Sorry, then I’m stumped. @jeremygray right know more, since he added the mic recording code

I see how this is annoying. My understanding is that no data are lost or unable to be acquired, so maybe its just annoying? The issue is that you have to click the red button to make it stop, whereas it should stop by itself. If data are NOT being saved properly, that is a far more serious issue.

A python process will not exit until all the non-daemon-ized threads have ended. Its possible that in pyo, there is a non-daemon thread (not sure). PsychoPy’s microphone class has a threading.Timer() object, so I tried making that a daemon thread, but it did not help.

Possible work-around: What if you only record for up to say 15 seconds, not 100? That is a more tolerable time to wait for it to time-out by itself after an experiment. When running a subject, I think that would be fine in many circumstances. This would still be mildly annoying while developing your experiment, but you could set the duration there to be short, like 5 sec. and change to 15 for running subjects.

The microphone classes use pyo, even if you set the audio lib to something else.

Hello and thank you for your reply.

You are correct, this is just a minor annoyance, the data are saved properly. I cannot record only 15 seconds. I am studying children’s responses to video comprehension questions and sometimes their answers take a long time to solidify. The responses are incredibly variable. Some kids answer right away, some kids think and say ‘um’ for awhile before answering. The time it takes to answer the question is actually one of my research questions. Thus, I need a long enough time to ensure that I capture all variability in answer types.

Ok, I’ll keep what I have. Perhaps in future releases this issue could be reevaluated if enough people are troubled by it. If it is only me, then I can deal with the minor annoyance. Thanks for clarifying!

Dustin

In theory, you could record multiple chunks of say 5s or 10s each, doing so until finished. You could then combine the chunks into a single long recording. In practice, there might be small gaps, and it seems like more hassle than just waiting for 100s at the end of the experiment.

I am hoping that this issue will get sorted out by the pyo team.

Hello,

I just wanted to chime in and say that I am also plagued by this issue. It used to be super intermittent (1 in 10 subjects) so I was just dealing with it. But recently it has started crashing almost every time. This is a major problem for me because I record in the middle of the experiment and it is freezing during that portion, which means I have to throw out every subject because they do not complete all portions of the experiment.

I updated to 1.84.1 in hopes that pyo was fixed in the newest release, but I still get exactly the two issues cited by Dustin : (1) failing to end the experiment (or freezing just after the recording phase) or (2) a segmentation fault in the middle of recording. (Both intermittent)

I tried every permutation of drivers and libraries for audio (though it sounds like the microphone class uses PYO no matter what). Nothing fixes it.

Before psychopy had the microphone class, I used to initiate a subprocess and record with the sox library (http://sox.sourceforge.net/), or just have research assistants record the entire experiment session via a third party software (audacity).

Setting a time limit also fixes the problem for me. But I am not able to do that in practice. I am recording children’s productions and it is not possible to predict how long it will take them to produce the sentences we are requesting from them. (Sometimes they just have to tell you a story about their fish really quick first).

It sounds from this thread like this issue cannot be fixed at the moment? (unless you are able to do the work around for your experiment?) Without this fixed it actually makes the psychopy microphone unusable for me, so I wanted to chime in to say that other people would be into a fix! It sounds like I should go back to my old method until then?

Thanks!
Katie

Sorry to hear about the problem, but glad to hear that I am not alone :slight_smile:

That sucks. Several months ago after coming to the conclusion that pyo sucks, I had done a little digging and came up with a program myself to use for playing and recording audio.

The recording program is modified slightly from a post on github by slorian, so he deserves any credit there, I wrote the player code myself.

If you’re coding your experiment yourself you could consider using this, though it comes ‘without warranty’ :slight_smile: It could also be added in a code component with some careful surgery. It was my first foray into audio programming, so I’m sure there are ways it could be improved. If you want to use it, let me know if anything I’m about to say doesn’t make sense to you.

Some notes and caveats: It only does wav files. My experiment doesn’t care about reaction times, so latency issues haven’t been a big deal for me, I always start recording before the window flip in the hope that I don’t lose the beginning of the enunciation due to slow startup, and haven’t had a problem yet, though my machine is pretty fast.

Keep in mind that since you won’t know how long you want to record for, you want to use the recorder in ‘non-blocking’ mode, meaning the recorder will start a separate thread. A consequence of this is that you will need to do something to pause your main experiment script to wait for recording to finish, otherwise it will keep marching on.

To quickly see if it works, open a Python shell in the same folder as where you have the file, (make sure a file named __init__.py exists that folder as well), and enter:

import simpleAudio 
outFile = 'testFile.wav'
recorder = simpleAudio.Recorder()
rf = recorder.open(outFile, 'wb')
rf.start_recording() # say some stuff
rf.stop_recording()

So to give you an example, you could do something like this. Put this file (simpleAudio.py) in the same folder as your experiment. Now editing your experiment script:

Toward top of experiment file

import simpleAudio

Somewhere toward the beginning of the experiment

recorder = simpleAudio.Recorder()
recordClock = core.Clock()

Now in the recording routine

outFile = 'outFile.wav'

with recorder.open(outFile, 'wb') as rf:
    recordClock.reset()
    rf.start_recording()

    # imaginary icon indicating recording
    recIcon.draw()
    win.flip()

    keepRecording = True
    # if you put too much stuff in this loop 
    # you might get distortion.
    while keepRecording:
        keys = event.getKeys()
        if 'escape' in keys:
            core.quit()
        if 'return' in keys:
            # make sure at least a second
            # has passed
            recT = recordClock.getTime()
            if recT > 1:
                rf.stop_recording()
                keepRecording = False
            

Let me know if you need more info or are having trouble getting it going.

simpleAudio.py (5.2 KB)

3 Likes