Example: Blocking Mode Audio I/O

"""PyAudio Example: Play a wave file."""

import wave
import sys

import pyaudio


CHUNK = 1024

if len(sys.argv) < 2:
    print(f'Plays a wave file. Usage: {sys.argv[0]} filename.wav')
    sys.exit(-1)

with wave.open(sys.argv[1], 'rb') as wf:
    # Instantiate PyAudio and initialize PortAudio system resources (1)
    p = pyaudio.PyAudio()

    # Open stream (2)
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                    channels=wf.getnchannels(),
                    rate=wf.getframerate(),
                    output=True)

    # Play samples from the wave file (3)
    while len(data := wf.readframes(CHUNK)):  # Requires Python 3.8+ for :=
        stream.write(data)

    # Close stream (4)
    stream.close()

    # Release PortAudio system resources (5)
    p.terminate()

To use PyAudio, first instantiate PyAudio using pyaudio.PyAudio() (1), which acquires system resources for PortAudio.

To record or play audio, open a stream on the desired device with the desired audio parameters using pyaudio.PyAudio.open() (2). This sets up a pyaudio.PyAudio.Stream to play or record audio.

Play audio by writing audio data to the stream using pyaudio.PyAudio.Stream.write(), or read audio data from the stream using pyaudio.PyAudio.Stream.read(). (3)

Note that in “blocking mode”, each pyaudio.PyAudio.Stream.write() or pyaudio.PyAudio.Stream.read() blocks until all frames have been played/recorded. An alternative approach is “callback mode”, described below, in which PyAudio invokes a user-defined function to process recorded audio or generate output audio.

Use pyaudio.PyAudio.Stream.close() to close the stream. (4)

Finally, terminate the PortAudio session and release system resources using pyaudio.PyAudio.terminate(). (5)

Example: Callback Mode Audio I/O

"""PyAudio Example: Play a wave file (callback version)."""

import wave
import time
import sys

import pyaudio


if len(sys.argv) < 2:
    print(f'Plays a wave file. Usage: {sys.argv[0]} filename.wav')
    sys.exit(-1)

with wave.open(sys.argv[1], 'rb') as wf:
    # Define callback for playback (1)
    def callback(in_data, frame_count, time_info, status):
        data = wf.readframes(frame_count)
        # If len(data) is less than requested frame_count, PyAudio automatically
        # assumes the stream is finished, and the stream stops.
        return (data, pyaudio.paContinue)

    # Instantiate PyAudio and initialize PortAudio system resources (2)
    p = pyaudio.PyAudio()

    # Open stream using callback (3)
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                    channels=wf.getnchannels(),
                    rate=wf.getframerate(),
                    output=True,
                    stream_callback=callback)

    # Wait for stream to finish (4)
    while stream.is_active():
        time.sleep(0.1)

    # Close the stream (5)
    stream.close()

    # Release PortAudio system resources (6)
    p.terminate()

In callback mode, PyAudio will call a user-defined callback function (1) whenever it needs new audio data to play and/or when new recorded audio data becomes available. PyAudio calls the callback function in a separate thread. The callback function must have the following signature callback(<input_data>, <frame_count>, <time_info>, <status_flag>). It must return a tuple containing frame_count frames of audio data to output (for output streams) and a flag signifying whether there are more expected frames to play or record. (For input-only streams, the audio data portion of the return value is ignored.)

The audio stream starts processing once the stream is opened (3), which will call the callback function repeatedly until that function returns pyaudio.paComplete or pyaudio.paAbort, or until either pyaudio.PyAudio.Stream.stop or pyaudio.PyAudio.Stream.close is called. Note that if the callback returns fewer frames than the frame_count argument (2), the stream automatically closes after those frames are played.

To keep the stream active, the main thread must remain alive, e.g., by sleeping (4). In the example above, once the entire wavefile is read, wf.readframes(frame_count) will eventually return fewer than the requested frames. The stream will stop, and the while loop (4) will end.