Simple, asynchronous audio playback for Python 3.
npx @tessl/cli install tessl/pypi-simpleaudio@1.0.0Simple, asynchronous audio playback for Python 3. Provides cross-platform, dependency-free audio playback capability for Python 3 on macOS, Windows, and Linux through a simple object-oriented API.
pip install simpleaudioimport simpleaudio as saFor function checks and testing:
import simpleaudio.functionchecks as fcimport simpleaudio as sa
# Load and play a wave file
wave_obj = sa.WaveObject.from_wave_file("path/to/file.wav")
play_obj = wave_obj.play()
play_obj.wait_done() # Wait until playback finishes
# Play raw audio buffer
audio_data = b'\x00\x01' * 44100 # Sample audio data
play_obj = sa.play_buffer(audio_data, 2, 2, 44100)
play_obj.wait_done()
# Stop all audio playback
sa.stop_all()Load audio from files or create from raw data, then control playback.
class WaveObject:
def __init__(self, audio_data, num_channels=2, bytes_per_sample=2, sample_rate=44100):
"""
Create a WaveObject from raw audio data.
Args:
audio_data (bytes): Raw audio data buffer
num_channels (int): Number of audio channels (1=mono, 2=stereo)
bytes_per_sample (int): Bytes per audio sample (1, 2, 3, or 4)
sample_rate (int): Sample rate in Hz (must be multiple of 11025)
"""
def play(self):
"""
Start playback of this wave object.
Returns:
PlayObject: Object for controlling this playback instance
"""
@classmethod
def from_wave_file(cls, wave_file):
"""
Load a wave object from a WAV file.
Args:
wave_file (str): Path to WAV file
Returns:
WaveObject: New wave object loaded from file
"""
@classmethod
def from_wave_read(cls, wave_read):
"""
Create a wave object from an open wave.Wave_read object.
Args:
wave_read: Open wave.Wave_read object from wave.open()
Returns:
WaveObject: New wave object from wave reader
"""
def __str__(self):
"""
String representation showing audio format details.
Returns:
str: Format description (channels, bit depth, sample rate)
"""Control individual audio playback instances.
class PlayObject:
def __init__(self, play_id):
"""
Internal constructor for playback control object.
Args:
play_id (int): Internal ID for tracking this playback
"""
def stop(self):
"""
Stop this specific audio playback.
"""
def wait_done(self):
"""
Block until this audio playback completes.
Polls playback status every 50ms.
"""
def is_playing(self):
"""
Check if this audio is currently playing.
Returns:
bool: True if still playing, False if stopped or finished
"""Play audio directly from raw buffer data without creating a WaveObject.
def play_buffer(audio_data, num_channels, bytes_per_sample, sample_rate):
"""
Play audio from raw buffer data.
Args:
audio_data (bytes): Raw audio data buffer
num_channels (int): Number of channels (1 or 2)
bytes_per_sample (int): Bytes per sample (1, 2, 3, or 4)
sample_rate (int): Sample rate in Hz (must be multiple of 11025)
Returns:
PlayObject: Object for controlling this playback
Raises:
ValueError: Invalid parameters (channels not 1-2, invalid bytes_per_sample,
or sample_rate not multiple of 11025)
"""Stop all audio playback at once.
def stop_all():
"""
Stop all currently playing audio streams.
"""Built-in audio functionality tests for verifying installation and hardware.
class FunctionCheckBase:
@classmethod
def run(cls, countdown=3):
"""
Run this function check with optional countdown.
Args:
countdown (int): Seconds to wait before starting (default: 3)
"""
class LeftRightCheck(FunctionCheckBase):
"""Tests stereo playback by playing left channel then right channel."""
class OverlappingCheck(FunctionCheckBase):
"""Tests overlapping playback with three notes spaced half-second apart."""
class StopCheck(FunctionCheckBase):
"""Tests stopping individual playback streams."""
class StopAllCheck(FunctionCheckBase):
"""Tests stopping all playback streams at once."""
class IsPlayingCheck(FunctionCheckBase):
"""Tests the is_playing() method functionality."""
class WaitDoneCheck(FunctionCheckBase):
"""Tests the wait_done() method functionality."""
def run_all(countdown=0):
"""
Run all function checks in sequence.
Args:
countdown (int): Seconds to wait before each check (default: 0)
"""import simpleaudio as sa
# Load and play a wave file
wave_obj = sa.WaveObject.from_wave_file("audio.wav")
play_obj = wave_obj.play()
# Wait for playback to finish
play_obj.wait_done()import simpleaudio as sa
from time import sleep
# Load multiple audio files
wave1 = sa.WaveObject.from_wave_file("note1.wav")
wave2 = sa.WaveObject.from_wave_file("note2.wav")
wave3 = sa.WaveObject.from_wave_file("note3.wav")
# Play them with overlapping timing
play1 = wave1.play()
sleep(0.5)
play2 = wave2.play()
sleep(0.5)
play3 = wave3.play()
# Wait for all to finish
play1.wait_done()
play2.wait_done()
play3.wait_done()import simpleaudio as sa
import numpy as np
# Generate a sine wave
sample_rate = 44100
duration = 2.0
frequency = 440
t = np.linspace(0, duration, int(sample_rate * duration), False)
sine_wave = np.sin(2 * np.pi * frequency * t)
# Convert to 16-bit integers
audio_data = (sine_wave * 32767).astype(np.int16).tobytes()
# Play the generated audio
play_obj = sa.play_buffer(audio_data, 1, 2, sample_rate)
play_obj.wait_done()import simpleaudio.functionchecks as fc
# Run a specific test
fc.LeftRightCheck.run()
# Run all tests
fc.run_all(countdown=3)The package raises ValueError exceptions for invalid parameters:
import simpleaudio as sa
try:
# This will raise ValueError - invalid sample rate
sa.play_buffer(b'\x00' * 1000, 2, 2, 44101)
except ValueError as e:
print(f"Invalid parameters: {e}")The package automatically detects the platform and uses the appropriate audio backend. No platform-specific code is required from the user.