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