Simple playback for intermittent audio byte streams in Python.
Project description
easyaudiostream
Simple playback for intermittent audio byte streams in Python.
I built this library because I was tired of existing audio playback solutions in Python not supporting audio streams particularly well (compared to audio files). easyaudiostream is intended for simple, high-enough-fidelity playback of audio byte streams:
- No choppiness between bytestream segments
- Handles non-real-time streams -- faster and slower than real-time
- Handles intermittent streams (i.e., streams that may not yield bytes for a while)
- asyncio-safe -- no blocking computation on main thread
easyaudiostream also includes basic utilities for recording audio from a microphone.
Installation
Requires Python 3.8 or higher.
By default, easyaudiostream will use the ffmpeg (if it is installed) or the bundled PyDub library for playback.
$ pip install easyaudiostream
[!NOTE] You can also install PyAudio to slightly improve playback startup time and enable the microphone input utilities:
$ pip install easyaudiostream[pyaudio]This is not installed by default due to compatibility issues across operating systems.
Usage
There are two main ways of playing an audio stream: using an iterator (if your audio generator yields bytes) or callbacks (if your audio generator calls a function with generated bytes).
Iterator Mode
For iterator mode, use the following two functions:
def play_stream(audio_stream: Iterable[bytes]):
"""
Consume bytes from the audio stream and play them.
.. note::
This function will return as soon as the stream is exhausted!
If your program ends after calling this function, the audio will not play - you might need to ``sleep(...)``.
"""
def play_raw_stream(
audio_stream: Iterable[bytes], *, sample_width: int = 2, channels: int = 1, frame_rate: int = 24000
):
"""
Consume PCM audio bytes from the audio stream and play them.
.. warning::
If you aren't sure which function to use, use :func:`.stream_audio` instead! Playing raw audio bytes is
a minor optimization that avoids converting the audio format, but requires a specific input format.
.. note::
This function will return as soon as the stream is exhausted!
If your program ends after calling this function, the audio will not play - you might need to ``sleep(...)``.
By default, this method accepts raw 16 bit PCM audio at 24kHz, 1 channel, little-endian. You can control the
expected format of the raw audio using the keyword arguments.
"""
Callback Mode
For callback mode, use the following two functions:
def play_audio(audio_bytes: bytes):
"""
Play the given audio at the next available opportunity, using a global audio queue.
.. note::
This function will return immediately - it just puts the audio on a queue!
If your program ends after calling this function, the audio will not play - you might need to ``sleep(...)``.
"""
def play_raw_audio(audio_bytes: bytes, *, sample_width: int = 2, channels: int = 1, frame_rate: int = 24000):
"""
Play the given raw audio at the next available opportunity, using a global audio queue.
.. warning::
If you aren't sure which function to use, use :func:`.play_audio` instead! Playing raw audio bytes is
a minor optimization that avoids converting the audio format, but requires a specific input format.
.. note::
This function will return immediately - it just puts the audio on a queue!
If your program ends after calling this function, the audio will not play - you might need to ``sleep(...)``.
By default, this method accepts raw 16 bit PCM audio at 24kHz, 1 channel, little-endian. You can control the
expected format of the raw audio using the keyword arguments.
"""
Record Mic
The following functions are available to record audio from a mic:
def get_mic_stream(mic_id: int | None) -> Iterable[bytes]:
"""Return an audio stream manager that yields audio frames from the given mic."""
def get_mic_stream_async(mic_id: int | None) -> AsyncIterable[bytes]:
"""Return an audio stream manager that yields audio frames from the given mic."""
def list_mics():
"""Print a list of all microphones connected to this device."""
You can use python -m easyaudiostream to view a list of connected microphones.
Examples
import time
from easyaudiostream import play_stream
# load dummy audio from file -- normally you would get an audio stream from somewhere else
# in this example, test_audio.wav contains 16bLE mono PCM audio at 24kHz
with open("test_audio.wav", "rb") as f:
audio = f.read()
# yield it in 1s packets every 0.95s
# but every 5s, add a 3s delay
def choppy_stream():
for sec in range(0, len(audio), 48000 * 5):
for i in range(0, 48000 * 5, 48000):
yield audio[sec + i : sec + i + 48000]
time.sleep(0.95)
# oh no, my bytes!
time.sleep(3)
play_stream(choppy_stream())
time.sleep(10) # to allow all the audio to play
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file easyaudiostream-0.0.1.tar.gz.
File metadata
- Download URL: easyaudiostream-0.0.1.tar.gz
- Upload date:
- Size: 9.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88eb9b102fa1496c9cf2a82c313c05f738ad15cbca49a07c8a8256fefc62797e
|
|
| MD5 |
d8ff51ce2c0cffd4975210785730c20f
|
|
| BLAKE2b-256 |
6ee428e50d76f8bb4dd0a8ba2c9a4768f4028a3a8775afb5c80fe61b71298042
|
Provenance
The following attestation bundles were made for easyaudiostream-0.0.1.tar.gz:
Publisher:
pythonpublish.yml on zhudotexe/easyaudiostream
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easyaudiostream-0.0.1.tar.gz -
Subject digest:
88eb9b102fa1496c9cf2a82c313c05f738ad15cbca49a07c8a8256fefc62797e - Sigstore transparency entry: 166807995
- Sigstore integration time:
-
Permalink:
zhudotexe/easyaudiostream@56bbac3cb7e67d854120c833ad6763d333dfb5f1 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/zhudotexe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pythonpublish.yml@56bbac3cb7e67d854120c833ad6763d333dfb5f1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file easyaudiostream-0.0.1-py3-none-any.whl.
File metadata
- Download URL: easyaudiostream-0.0.1-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fdd84b5b8c46bf3e461e406b00ed92e2200a43ad471b46b6062dc7c53c869c3
|
|
| MD5 |
7dd8529fd5a3559895ea4bfcd3fb37e4
|
|
| BLAKE2b-256 |
191ad788e6897f6574c494676472f78afc8c0801f406109a2a3b19c0f7d26621
|
Provenance
The following attestation bundles were made for easyaudiostream-0.0.1-py3-none-any.whl:
Publisher:
pythonpublish.yml on zhudotexe/easyaudiostream
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easyaudiostream-0.0.1-py3-none-any.whl -
Subject digest:
5fdd84b5b8c46bf3e461e406b00ed92e2200a43ad471b46b6062dc7c53c869c3 - Sigstore transparency entry: 166807998
- Sigstore integration time:
-
Permalink:
zhudotexe/easyaudiostream@56bbac3cb7e67d854120c833ad6763d333dfb5f1 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/zhudotexe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pythonpublish.yml@56bbac3cb7e67d854120c833ad6763d333dfb5f1 -
Trigger Event:
release
-
Statement type: