-
-
Save sloria/5693955 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*- | |
'''recorder.py | |
Provides WAV recording functionality via two approaches: | |
Blocking mode (record for a set duration): | |
>>> rec = Recorder(channels=2) | |
>>> with rec.open('blocking.wav', 'wb') as recfile: | |
... recfile.record(duration=5.0) | |
Non-blocking mode (start and stop recording): | |
>>> rec = Recorder(channels=2) | |
>>> with rec.open('nonblocking.wav', 'wb') as recfile2: | |
... recfile2.start_recording() | |
... time.sleep(5.0) | |
... recfile2.stop_recording() | |
''' | |
import pyaudio | |
import wave | |
class Recorder(object): | |
'''A recorder class for recording audio to a WAV file. | |
Records in mono by default. | |
''' | |
def __init__(self, channels=1, rate=44100, frames_per_buffer=1024): | |
self.channels = channels | |
self.rate = rate | |
self.frames_per_buffer = frames_per_buffer | |
def open(self, fname, mode='wb'): | |
return RecordingFile(fname, mode, self.channels, self.rate, | |
self.frames_per_buffer) | |
class RecordingFile(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 |
Hi, nice example. Is there a reason for the get_callback wrapper?
The actual callback method is defined outside of the start_recording method but within the RecordingFile class. A wrapper is required so that start_recording can reference the callback through "self" object.
Thank you! does exactly what i need without me having to write a single line of code :D
Hi, nice example. Is there a reason for the get_callback wrapper?
PyAudio callbacks cannot be class methods, and do not take a 'self' argument. The wrapper creates a Python closure. This binds the 'self' passed into the 'get_callback' method for use by the 'callback' function, which needs it to call 'self.wavefile.writeframes'.
I am looking for record multiple channels in to multiple .WAV, I can do that with numpy, specific select the rows.
However, I am not sure if I can apply non-blocking with that. as I am not able to using stream.read for non-blocking, right?
Thank you! This made it so much easier for me :) The only thing that I added is an additional argument called "device" to select the audio source.
thanks to you i made my work too easily.
thank you very much!!
Great job!