How to Record, Save and Play audio in Python?

Libraries

The following are the required libraries.

  • PortAudio is a free, cross-platform, open-source, audio I/O library. It lets you write simple audio programs in ‘C’ or C++ that will compile and run on many platforms including Windows, Macintosh OS X, and Unix (OSS/ALSA).
  • PyAudio provides Python bindings for PortAudio. Following is the pip command.
    pip install pyaudio
  • wave module of Python3.
  • simpleaudio to play the saved wave audio file. Following is the pip command:
    pip install simpleaudio

Check mic check

Check if you have a working microphone on your system. Following is the code snippet you can use.

import pyaudio
import pprint 

p = pyaudio.PyAudio()
pp = pprint.PrettyPrinter(indent=4)

try:
    pp.pprint(p.get_default_input_device_info())
except:
    print("No mics availiable")

Example output:

Record the audio

Following is the code snippet to record the audio.

import pyaudio
import wave

p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK) #buffer

print("* recording")
frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data) # 2 bytes(16 bits) per channel

stream.stop_stream()
stream.close()
p.terminate()

pyaudio.PyAudio() method acquires system resources for PortAudio. pyaudio.PyAudio.open() sets up a pyaudio.PyAudio.Stream to play or record audio. pyaudio.PyAudio.Stream.read() is used to read audio data from the stream. In the above code, all the audio frames have been collected in the frames list frames []. These frames will be used for saving the audio file in the later part of the code.

Meaning of parameters to the function open:

  1. FORMAT: PortAudio provides samples in raw PCM (Pulse-Code Modulation) format. That means each sample is an amplitude to be given to the DAC (digital-to-analog converter) in your sound card. For paInt16, this is a value from -32768 to 32767. For paFloat32, this is a floating-point value from -1.0 to 1.0. The sound card converts this values to a proportional voltage that then drives your audio equipment. paFloat32, paInt32, paInt24, paInt16, paInt8, paUInt8, paCustomFormat
  2. CHANNELS means how many samples will be there for each frame.
  3. RATE is the sampling rate (the number of frames per second)
  4. CHUNK is the number of frames the signals are split into. This is arbitrarily chosen.

In the last, the stream should be closed and all the resources acquired must be released.

Saving the audio

Following is the code snippet to save the audio.

# Save the recorded audio file
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

wave module of Python3 is used for this purpose. Parameters are set as the same values that were used while recording the audio.

Play the audio

Following is the code snippet to play the audio. simpleaudio library I have used for this purpose. There are other libraries available that can be tried. simpleaudio I found to be simple enough.

# Play the recorded audio file
wave_obj = sa.WaveObject.from_wave_file("pyaudio-output.wav")
play_obj = wave_obj.play()
play_obj.wait_done()

Complete Code

import pyaudio
import wave
import simpleaudio as sa

CHUNK = 512 
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "myaudio.wav"

p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK) #buffer

print("* recording")
frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data) # 2 bytes(16 bits) per channel

stream.stop_stream()
stream.close()
p.terminate()

# Save the recorded audio file
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

# Play the recorded audio file
wave_obj = sa.WaveObject.from_wave_file("myaudio.wav")
play_obj = wave_obj.play()
play_obj.wait_done()

References