Skip to content

Instantly share code, notes, and snippets.

@sloria
Last active April 12, 2024 11:43
Show Gist options
  • Save sloria/5693955 to your computer and use it in GitHub Desktop.
Save sloria/5693955 to your computer and use it in GitHub Desktop.
WAV recording functionality using pyaudio
# -*- 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
@GeorgeK-zn
Copy link

Hey,
Thank you ))

@agrawalhemant5393
Copy link

Hi, I want to trigger event using GUI on button event. Can you help me how to acess.

@mave240
Copy link

mave240 commented Aug 29, 2018

Hi, nice example. Is there a reason for the get_callback wrapper?

@loveAlakazam
Copy link

loveAlakazam commented Sep 17, 2018

Your code is very helpful to me. Thanks :)

@cormacp
Copy link

cormacp commented Feb 27, 2019

Excellent non-blocking example, really clean

@PelayoChoya
Copy link

Congratulations! Very well written code

@chrisfraschetti
Copy link

Hi @sloria

Nice library - do you mind supplying a license, either here in the comments here or on the file itself?

@sloria
Copy link
Author

sloria commented Aug 27, 2019

@benoitvalery
Copy link

Many thanks. Exactly what I was looking for. Non-blocking recording examples are hard to find.

@ivopisarovic
Copy link

Great! Saved my life :)

@ofelix03
Copy link

Thanks man. This is amazing. It's saved me from having to understanding PyAudio.

@sreya0299
Copy link

Hi @sloria
Even after using this code I am getting errors related to ALSA lib. Please help

@benoitvalery
Copy link

Hi @sreya0299, could you post more information about the errors you get ? A copy-paste to begin with ? At what line ? Do you use this exact code ?

@sreya0299
Copy link

sreya0299 commented Mar 10, 2020 via email

@williamhatcher
Copy link

Great job!

@kepler62f
Copy link

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.

@Alge
Copy link

Alge commented Jul 10, 2020

Thank you! does exactly what i need without me having to write a single line of code :D

@david-f-johnston-nuance

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'.

@WalkerC7
Copy link

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?

@lochbika
Copy link

lochbika commented Sep 1, 2020

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.

@qkrwjdan
Copy link

thanks to you i made my work too easily.
thank you very much!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment