Sound recording with sound.Microphone()
Hi all,
I was wondering if anyone has found a good solution for sound recording in the latest versions of OpenSesame. The sound_recorder plugin does not really seem to be an option anymore.
I did try the sound.Microphone() function from psychopy. Using the Opensesame 3.3.12 megapack for windows build, I can record sounds using this function, but the terminal keeps throwing errors (notably, from the moment the recording starts):
PsychPortAudio('Close' [, pahandle]); Error in function Close: Usage error
Even after the experiment has finished and closed. The terminal then also does not get released anymore.
I tried all the available backends with the various sound driver options, but got the same results each time. This issue occurs on multiple win10 computers. Interestingly, if I just run the code from the terminal directly (or in Rapunzel) everything goes fine, and no errors are thrown. Only if the experiment is actually run does the problem occur.
I tried updating the psychopy library to the latest stable version, this did remove the warnings in the terminal, but still the terminal does not get released after the experiment finishes and the whole GUI get extremely slow and buggy.
I'm also very open for alternative methods. I did see some ideas for different implementations floating around on the forum, but no worked-out recent examples.
from psychopy import sound
def my_cleanup_function():
print(u'Closing mic')
mic.close()
register_cleanup_function(my_cleanup_function)
# parameters
micPref = "Microphone (Logitech Webcam C930e)" # preffered mic name
recTime = 20 # expected recording time in seconds
#check which devices are available
myDev = sound.Microphone.getDevices()
#check if any are found
if not myDev: raise Exception("No microphones present, please check hardware")
# find the preferred mic or if not present, any available mic
anySet = False
for idx, x in enumerate(myDev):
if x.deviceName == micPref or anySet == False:
micIdx = idx
anySet = True
# raise warning if the preferred mic is not found
if micPref != myDev[micIdx].deviceName:
warnings.warn(f"Warning preferred mic not found, instead using: {myDev[micIdx].deviceName}")
#set the recording buffer to be large enough
recBuffer = .010 * recTime * myDev[micIdx].defaultSampleRate * myDev[micIdx].inputChannels
# open the microphone (do this only once per study)
mic = sound.Microphone(device=myDev[micIdx].deviceIndex,channels =myDev[micIdx].inputChannels,streamBufferSecs=10, maxRecordingSize=recBuffer,sampleRateHz=myDev[micIdx].defaultSampleRate,audioRunMode = 1, audioLatencyMode = 3)
mic.start() # start recording
clock.sleep(2000)
mic.stop() # stop recording
audioClip = mic.getRecording()
print(audioClip.duration) # should be ~10 seconds
audioClip.save("test.wav') # save the recorded audio as a 'wav' file
mic.close()
Comments
UPDATE:
So I tried implementing the sounddevice method instead, and was getting very similar results: it records the file but the experiment take a long time to close up to 2 minutes (i.e. the editor stays greyed out) even with just a 5 second sound file. Notably, the sound file is already saved and available before the experiment has closed properly. Once it has closed the terminal will then also still stay unresponsive for up to a minute or 2. I also tried removing the clean up function, but this didn't seem to make a difference.
import sounddevice as sd import warnings import datetime from scipy.io.wavfile import write # make sure any open mic stream is closed on exit # def my_cleanup_function(): # print(u'Closing mic') # #sd.stop() # register_cleanup_function(my_cleanup_function) # parameters micPref = "Microphone Array (Intel® Smart , MME (4 in, 0 out)" # preffered mic name duration = 5 # expected recording time in seconds #check which devices are available myDev = sd.query_devices() #check if any are found if not myDev: raise Exception("No microphones present, please check hardware") # find the preferred mic or if not present, any available mic anySet = False for idx, x in enumerate(myDev): if x['name'] == micPref or (anySet == False and x['name'].find('Microphone') > -1): micIdx = idx anySet = True # raise warning if the preferred mic is not found if micPref != myDev[micIdx]['name']: warnings.warn(f"Warning preferred mic not found, instead using: {myDev[micIdx]['name']}") # set default parameters based on device sd.default.samplerate = myDev[micIdx]['default_samplerate'] sd.default.channels = min(myDev[micIdx]['max_input_channels'],4) sd.default.device = myDev[micIdx]['name'] # start recording myrecording = sd.rec(int(duration * sd.default.samplerate)) # wait for recording to finish sd.wait() # write recording to file write(f'C:\\Users\\olferskjf\\{datetime.datetime.now():%Y-%m-%d %H:%M}.wav', int(sd.default.samplerate), myrecording) # Save as WAV file # close to be sure sd.stop() print(sd.get_status())Hi kolfers,
I used this code:
import sounddevice as sd from scipy.io.wavfile import write fs = 44100 # Sample rate seconds = 3 # Duration of recording myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2) sd.wait() # Wait until recording is finished write('output.wav', fs, myrecording) # Save as WAV filefrom this website, and it seems to work quite well. No unresponsiveness or anything alike. Perhaps you could use that?
edit: I just see that you basically use the same approach in the second posting. Is the delay also present, if you don't do the extra bits related to selecting the correct device, that is, with this basic example here?
If not, it might be related to your system? I used sounddevice version 0.4.1 on Ubuntu 18.
Eduard
Hi Eduard,
Thanks for checking in! The delay is also present with the default mic, and I can replicate it on my laptop (win10) with the internal or an external mic, and with desktop tower in the lab (also win10) which only has an external mic. So perhaps it's a Win10 / WASAPI issue.. I was on sounddevice '0.3.14' and tried again after updating to 0.4.5. If anything, it made the problem worse unfortunately, because now the rest of the machines starts locking up as well. Looking at task manager, I can see RAM used by Opensesame rapidly fluctuating between 800mb and 2-3 GB (this is with 1 editor open, after the experiment has finished, and I'm not interacting with it at all). It feels like some stream or buffer is not closed properly, but I don't know how to investigate this further. For instance sd.get_status() does seem to indicate that everything stopped properly.
I ended up also trying the pyaudio approach (code below for anyone interesting). Notable, while it still took some time for the editor to become available after the experiment finished, the terminal was the also released at the same time, and there was no unresponsiveness or lag remaining in the interface .
A drawback of the pyaudio approach does seem to be that the default way of recording blocks your script until it's finished (as opposed to the sounddevice and psychopy methods) .
op
import pyaudio import wave import warnings import datetime # make sure any open mic stream is closed on exit # def my_cleanup_function(): # print(u'Closing mic') # #sd.stop() # register_cleanup_function(my_cleanup_function) # pyaudio parameters chunk = 1024 # Record in chunks of 1024 samples sample_format = pyaudio.paInt16 # 16 bits per sample p = pyaudio.PyAudio() # Create an interface to PortAudio # parameters micPref = "Microphone Array (Intel® Smart " # preffered mic name duration = 5 # expected recording time in seconds #check which devices are available info = p.get_host_api_info_by_index(0) if not info: raise Exception("No devices present, please check hardware") else: numdevices = info.get('deviceCount') anySet = False devTmp =[] for i in range(0, numdevices): if (p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0: devTmp = p.get_device_info_by_host_api_device_index(0, i) print("Input Device id ", i, " - ", devTmp['name']) if devTmp['name'] == micPref or anySet == False: anySet = True myDev = devTmp if not myDev: raise Exception("No microphone present, please check hardware") if myDev['name']!= micPref: warnings.warn(f'preferred mic not found, instead using {myDev["name"]}') print('Stream open') stream = p.open(format=sample_format, channels=myDev['maxInputChannels'], rate=int(myDev['defaultSampleRate']), frames_per_buffer=chunk, input=True) # Store data in chunks for 3 seconds frames = [] for i in range(0, int(myDev['defaultSampleRate'] / chunk * duration)): data = stream.read(chunk) frames.append(data) # Stop and close the stream stream.stop_stream() stream.close() # Terminate the PortAudio interface p.terminate() print('Finished recording') # Save the recorded data as a WAV file wf = wave.open(f'C:\\Users\\olferskjf\\test - {datetime.datetime.now():%Y-%m-%d %H-%M}.wav', 'wb') wf.setnchannels(myDev['maxInputChannels']) wf.setsampwidth(p.get_sample_size(sample_format)) wf.setframerate(myDev['defaultSampleRate']) wf.writeframes(b''.join(frames)) wf.close()