Search code examples
pythoncore-audiopyaudiomacos-big-surportaudio

Audio recording in Python with Pyaudio, error ||PaMacCore (AUHAL)|| ... msg=Audio Unit: cannot do in current context


I am using pyaudio to record sounds on my Mac BigSur 11.6 (20G165). Specifically, I'm redirecting sound from an application to the input using BlackHole, which works fine.

It usually works fine but, sometimes, I get this error in the Terminal:

||PaMacCore (AUHAL)|| Error on line 2500: err='-10863', msg=Audio Unit: cannot do in current context

Any idea why or how I could prevent it from happening (like, waiting until PaMacCore is ready to record again or something)?

I already tried reinstalling but it doesn't help

brew install portaudio

or

brew install portaudio --HEAD

Here's my code:

import pyaudio
import wave


class Recorder(object):
    def __init__(self, fname, mode, channels,
                 rate, frames_per_buffer):
        self.fname = fname
        self.mode = mode
        self.channels = channels
        self.rate = rate
        self.frames_per_buffer = frames_per_buffer
        self._pa = pyaudio.PyAudio()
        self.wavefile = self._prepare_file(self.fname, self.mode)
        self._stream = None

    def __enter__(self):
        return self

    def __exit__(self, exception, value, traceback):
        self.close()

    def record(self, duration):
        # Use a stream with no callback function in blocking mode
        self._stream = self._pa.open(format=pyaudio.paInt16,
                                     channels=self.channels,
                                     rate=self.rate,
                                     input=True,
                                     frames_per_buffer=self.frames_per_buffer)
        for _ in range(int(self.rate / self.frames_per_buffer * duration)):
            audio = self._stream.read(self.frames_per_buffer)
            self.wavefile.writeframes(audio)
        return None

    def start_recording(self):
        # Use a stream with a callback in non-blocking mode
        self._stream = self._pa.open(format=pyaudio.paInt16,
                                     channels=self.channels,
                                     rate=self.rate,
                                     input=True,
                                     frames_per_buffer=self.frames_per_buffer,
                                     stream_callback=self.get_callback())
        self._stream.start_stream()
        return self

    def stop_recording(self):
        self._stream.stop_stream()
        return self

    def get_callback(self):
        def callback(in_data, frame_count, time_info, status):
            self.wavefile.writeframes(in_data)
            return in_data, pyaudio.paContinue

        return callback

    def close(self):
        self._stream.close()
        self._pa.terminate()
        self.wavefile.close()

    def _prepare_file(self, fname, mode='wb'):
        wavefile = wave.open(fname, mode)
        wavefile.setnchannels(self.channels)
        wavefile.setsampwidth(self._pa.get_sample_size(pyaudio.paInt16))
        wavefile.setframerate(self.rate)
        return wavefile

Usage example:

recfile = Recorder(filename, 'wb', 2, 44100, 1024)
recfile.start_recording()
...
recfile.stop_recording()

Solution

  • Apparently the problem were mismatched bitrates in BlackHole's aggregated output device. I was aggregating Blackhole's output (44,1kHz) and the Mac Speakers (48kHz). This did not cause any consistent bad behaviour but sometimes led to these errors.