Skip to main content

ap_ds: A Lightweight Python Audio Library

Current Version: v3.1.1 – Performance & Batch Parsing Edition (July 2026)
Stable Release Recommendation: v3.0.0 LTS (Long-Term Support)

⚠️ Version Notice !!!

v v3.1.0 is deprecated and not recommended for use.

  • Reason: Documentation and log output were in Chinese, which is not aligned with the project's globalization strategy.
  • Please upgrade to v3.1.1 or later.
pip install --upgrade ap_ds
## 🚀 v v3.1.0/3.1.1 – Performance & Batch Parsing Edition

This is a major feature release of ap_ds, focusing on **batch parsing performance** and **Python 3.15 free-threading support**.

### ✨ New Features

#### 📦 Batch Parsing API (Brand New)

| API | Description |
|-----|-------------|
| `batch_get_metadata()` | Batch parse audio files, returns full metadata list |
| `batch_get_duration()` | Batch get audio durations, returns `{path: duration}` |
| `batch_get_metadata_by_type()` | Filter batch parsing by format (e.g., parse only MP3) |

#### 🧩 DAP Deduplication Optimization

`_add_to_dap_recordings()` upgraded from O(n) linear scan to **O(1) set-based deduplication**, delivering significant performance gains for large playlists.

#### 🐍 Python 3.15t Free-Threading Support

- **True GIL-less parallelism**: Batch parsing scales linearly on multi-core CPUs
- **Runtime self-check**: Automatically detects GIL status on import and provides clear prompts

**Full-performance users will see:**

🎉 ap_ds: GIL disabled (free-threading mode)


**Non-full-performance users will see:**

⚠️ ap_ds: GIL is enabled (multi-core parallelism limited). For full performance, upgrade to Python 3.15t


#### ⚙️ New Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `AP_DS_SUPPRESS_WARNINGS` | Not set | Set to `1` to suppress downgrade warnings |
| `AP_DS_SHOW_CONGRATS` | Not set | Set to `0` to hide congratulations message |

#### 🚀 Startup Acceleration

Python 3.15+ users automatically benefit from `lazy import` – heavy modules are loaded on demand, significantly speeding up `import ap_ds`.


## ⚡ Performance Comparison

### Test Environment

| Item | v3.0.0 LTS | v v3.1.0/3.1.1/  | Mutagen |
|------|------------|--------|---------|
| **Python Version** | 3.13.4 (with GIL) | 3.15.0b4 (without GIL) | 3.13.4 (with GIL) |
| **Concurrency Method** | ThreadPoolExecutor | ProcessPoolExecutor | Single-threaded |
| **GIL Status** | ✅ Enabled | ❌ Disabled | ✅ Enabled |
| **Batch Parsing** | ❌ No | ✅ Yes | ❌ No |
| **Test Files** | 120 MP3s | 120 MP3s | 120 MP3s |


### 📊 Comparison 1: ap_ds v3.0.0 vs v v3.1.0/3.1.1/  (120 files)

| Test | v3.0.0 (with GIL) | v v3.1.0/3.1.1 (without GIL) | Conclusion |
|------|-------------------|----------------------|------------|
| **Serial parsing** | 0.848s | 1.367s |  v3.1.0/3.1.1 slightly slower (GIL-less overhead) |
| **2-way concurrency** | 0.789s (1.08x) | 0.795s (1.72x) | ✅  v3.1.0/3.1.1 begins to accelerate |
| **4-way concurrency** | 1.863s (0.46x) ❌ | 0.471s (2.90x) | 🚀  v3.1.0/3.1.1 is 3.95x faster |
| **8-way concurrency** | 1.285s (0.66x) ❌ | **0.331s (4.13x)** | 🚀  v3.1.0/3.1.1 is 3.88x faster |
| **Best time** | 0.789s (2 threads) | **0.331s (8 processes)** | 🚀 ** v3.1.0/3.1.1 is 2.38x faster** |

**Key Findings:**

- **v3.0.0 (with GIL)**: Multi-threading is completely ineffective – 4 threads are twice as slow as serial (1.86s), and 8 threads achieve only 0.66x speedup
- **v v3.1.0/3.1.1/  (without GIL)**: True multi-process parallelism – 8 processes achieve 4.13x speedup, reducing 120 files from 1.37s to 0.33s

> 💡 v3.0.0's multi-threading is bottlenecked by the GIL – more threads actually slow things down. v v3.1.0/3.1.1 uses ProcessPoolExecutor for true parallelism, fully unleashing multi-core performance!


### 📊 Comparison 2: ap_ds v v3.1.0/3.1.1/  vs Mutagen (120 files)

**Why compare with Mutagen?**

Mutagen is the most popular audio metadata library in the Python ecosystem, widely used in music players, tag editors, and more. However, it has the following limitations:

- ❌ **Single-threaded design**: All parsing tasks run serially, unable to utilize multi-core CPUs
- ❌ **No built-in batch concurrency**: Users must implement their own multi-threading/multi-processing wrappers
- ❌ **No Python 3.15t support**: Mutagen hasn't adapted to free-threading mode – the GIL limitation remains
- ❌ **No built-in batch APIs**: Processing large numbers of files requires manual loops, making code cumbersome

**These pain points are exactly what inspired ap_ds v v3.1.0/3.1.1's batch parsing functionality.**

| Comparison | Mutagen (3.13.4) | ap_ds v v3.1.0/3.1.1 (3.15t) | Advantage |
|------------|------------------|----------------------|-----------|
| **Parsing time** | 0.973s | **0.331s** | 🚀 ap_ds **2.94x faster** |
| **Concurrency** | Single-threaded serial | 8-process parallel | ✅ True parallelism |
| **Per-file average** | 0.0081s | **0.00275s** | 🚀 ap_ds **2.95x faster** |
| **GIL limitation** | ❌ Yes | ✅ No | ✅ ap_ds unconstrained |
| **Batch API** | ❌ No | ✅ `batch_get_metadata()` | ✅ Ready to use |
| **Multi-core utilization** | ❌ Single core | ✅ All cores | ✅ Full CPU utilization |


### 📈 Comprehensive Comparison Chart

               120 MP3 File Parsing Time

Mutagen (single-thread) ████████████████████████████████████ 0.973s v3.0.0 (8 threads) ████████████████████████████████████ 1.285s ❌ Slower v v3.1.0/3.1.1 (serial) ████████████████████████████████████████ 1.367s v v3.1.0/3.1.1 (8 processes) ████████████ 0.331s 🚀 2.94x faster!



### 🎯 Summary

| Solution | 120 files time | Speedup | Recommended Use Case |
|----------|----------------|---------|---------------------|
| Mutagen (single-thread) | 0.973s | 1.00x | Small number of files |
| v3.0.0 (8 threads) | 1.285s | 0.66x ❌ | Not recommended for concurrency |
| v v3.1.0/3.1.1 (serial) | 1.367s | 1.00x | Small number of files |
| **v v3.1.0/3.1.1 (8 processes)** | **0.331s** | **4.13x** | **Large batches** 🚀 |

> **Conclusion: v v3.1.0/3.1.1 + Python 3.15t + 8-process batch parsing = 120 MP3 files in just 0.33 seconds – 3x faster than Mutagen and 4x faster than v3.0.0 multi-threading!**


## 📦 Version Relationship

| Version | Type | Support Period | Use Case |
|---------|------|----------------|----------|
| **v3.0.0 LTS** | Long-Term Support | Until March 2031 | Production environments |
| **v v3.1.0/3.1.1** | Feature Release | ~6 months | Early adopters / batch parsing needs |


## 🌐 apds.top is Now Live!

**The official ap_ds project homepage is now live with TLS encryption!**

🎉 Visit: **https://apds.top**

### Website Features

- 📄 **Complete Documentation**: API reference, user guides, FAQs
- 📦 **Release Distribution**: All version download links and changelogs
- 🔗 **Repository Navigation**: GitCode (primary), Gitee (China mirror)
- ✉️ **Feedback System**: Users can submit feedback directly via the website
- 🔒 **Full-site TLS Encryption**: All pages served over HTTPS


## 📌 Version Upgrade Recommendations

| User Type | Recommendation |
|-----------|----------------|
| Production environment | Continue using **v3.0.0 LTS**, wait for v4.0.0 LTS |
| Development/Testing | Upgrade to **v v3.1.0/3.1.1** to experience new features |
| Need batch parsing | **Must upgrade** to v v3.1.0/3.1.1 |
| Python 3.15t users | **Must upgrade** to v v3.1.0/3.1.1 to leverage GIL-less advantages |

```bash
pip install --upgrade ap_ds

⚠️ Repository Migration Notice

GitHub Deprecation, GitLab Abandonment, Migration to GitCode – apds.top Becomes the Permanent Home

This section explains in detail the history of ap_ds's official code repository migrations and final destination.

1. Why GitHub Was Deprecated

The developer's GitHub account was locked due to the loss of two-factor authentication (2FA) devices. After multiple attempts to contact GitHub support, only automated bot replies were received. Due to the complete lack of human assistance, the developer decided to permanently abandon that GitHub account and will not create a new one in the foreseeable future. The old dvs-web/ap_ds repository is now officially deprecated and will no longer receive any updates.

2. Why GitLab (JiHu) Was Abandoned

Following the GitHub issue, the project migrated its primary repository to Gitee and GitLab (JiHu). However, GitLab was recently abandoned due to platform policy changes that made basic account login a paid feature. Since the project relies on free open-source collaboration, this change created an unacceptable barrier for contributors and users. The developer attempted to find alternatives but found none within the platform's free tier. Therefore, the GitLab repository is no longer actively maintained.

3. Why Gitee Is Now a Backup (Not Primary)

Gitee is an excellent platform, especially for developers within China, offering fast and stable access. It remains a strongly recommended choice. However, its role has been adjusted to backup or China-facing mirror for two primary reasons:

  • International Accessibility: Gitee's servers are primarily located within China. For developers outside mainland China, access can be slow, unstable, and in some cases, completely blocked due to international network policies. This creates a poor experience for a significant portion of the user base.

  • User Interface and Workflow: While functional, Gitee's UI and workflow are often considered outdated and less aligned with the modern Git workflows many international developers are accustomed to.

For these reasons, while Gitee is by no means "bad" and will continue to be fully supported as a China-facing mirror, it is no longer suitable as the sole primary repository for a project with a global audience.

4. The Solution: GitCode Becomes the New Primary Repository

After evaluating the landscape of free Git hosting platforms, GitCode emerged as the ideal solution. GitCode offers a modern interface, a robust feature set, and most importantly, excellent accessibility for both domestic and international developers. It has become the new primary official repository for the ap_ds project.

The new primary repository is located at: https://gitcode.com/dvsxt/ap_ds

5. The Permanent Home: apds.top

apds.top is now officially live and fully operational!

This is not just another repository mirror – it is the permanent official home of ap_ds. It resolves all platform dependency issues:

  • ✅ Full independent control: No longer affected by third-party platform policy changes
  • ✅ TLS encryption: Full-site HTTPS secure access
  • ✅ Permanent stability: Even if all third-party platforms fail, apds.top remains available
  • ✅ One-stop service: Documentation, downloads, feedback, and repository navigation all integrated

Official Project Homepage: https://apds.top

6. Final Repository Strategy

Platform Status Purpose
apds.top ✅ Permanent Home Official source, documentation, downloads
GitCode ✅ Primary Mirror Code hosting for global users
Gitee ✅ China Mirror Fast access for China-based users
GitHub ❌ Deprecated No longer maintained
GitLab (JiHu) ❌ Abandoned No longer maintained

Note for PyPI users: The project description on PyPI will be updated with the v v3.1.0/3.1.1 release. For the latest information, always refer to https://apds.top.

Overview

ap_ds is a lightweight (2.5MB) Python audio library for playing and high-precision metadata parsing of MP3, FLAC, OGG, and WAV files. It has zero external Python dependencies, using only the Python standard library, and provides non-blocking playback suitable for GUI applications.

Core Features:

  • Extremely lightweight: 2.5MB on Windows / 3.36MB on macOS complete solution
  • Zero Python dependencies: Uses only the standard library
  • High-precision metadata: WAV/FLAC 100%, OGG 99.99%, MP3 >98%
  • Batch parsing: Process hundreds of files in parallel using batch_get_metadata()
  • Non-blocking playback: Perfect for GUI applications
  • Cross-platform: Windows, macOS, Linux, embedded ARM64
  • DAP recording system: Automatic playback history with metadata only
  • Python 3.15t support: GIL-less true parallelism, full multi-core performance
  • LTS support: First long-term support version with 5-year maintenance commitment

Contact & Support

📧 Licensing Inquiries

me@dvsyun.top or dvs6666@163.com · Response within 7 business days

🛠️ Technical Support

apds.top Issues · GitCode Issues · Gitee Issues · Email (free during LTS period)


Official Repositories & Project Sources

✅ Official Primary Repository (First Recommendation): https://apds.top – ap_ds's permanent official home, fully independently controlled, TLS encrypted, unaffected by third-party platform policies.

✅ Primary Mirror (Global Access): GitCode – Globally accessible, modern UI, actively maintained as a public mirror.

✅ China Mirror (Fast & Stable): Gitee – Full mirror for China-based developers, fast access, classic stable UI.

❌ Deprecated & Abandoned:

  • GitHub (dvs-web/ap_ds) – Deprecated due to permanent account lockout, no longer maintained.
  • GitLab (JiHu) – Abandoned due to platform policy changes (basic account login became a paid feature), no longer maintained.

ℹ️ ap_ds v3.0.0 LTS – First Long-Term Support Release
This version consolidates all previous improvements, adds deterministic resource cleanup, hash-verified downloads, and a 5-year support commitment. The old GitHub repository (dvs-web/ap_ds) is deprecated and no longer updated. The GitLab repository has been abandoned. For future updates and contributions, please use the official primary repository apds.top or the public mirrors GitCode and Gitee.

Developer Personal Homepage & Blog: https://dvsx.top – Blog is currently undergoing maintenance and upgrades. Stay tuned.
ap_ds Project Homepage: https://apds.top (Official documentation, releases, and license center).

🔗 Canonical URL: https://apds.top/
📖 Blog & Author: dvsx.top – Blog is currently undergoing maintenance and upgrades. Stay tuned.


About the Author

Developer: Dvs (DvsXT)
Personal Homepage & Blog: https://dvsx.top – Blog is currently undergoing maintenance and upgrades. Stay tuned
Author Bio: https://dvsyun.top/me/dvs
Email: me@dvsyun.top · dvs6666@163.com

ap_ds Official Portal

🎵 ap_ds Official Website (Primary): https://apds.top – Permanent official home, complete documentation, releases, license center
📦 PyPI Project Page: https://pypi.org/project/ap_ds/ – Installable via pip
🌐 Mirror Documentation Site: https://www.dvsyun.top/ap_ds – Backup documentation access

👉 The ap_ds project homepage (apds.top) is the first recommendation for official sources, hosting complete documentation, license details, version changelogs, and official releases. The author's personal blog (dvsx.top) is currently undergoing maintenance and upgrades. Stay tuned.

Let's Get Started!

Installation

pip install ap_ds

Upgrade from an older version:

pip install --upgrade ap_ds

💡 Why Python 3.15t / 3.14t?

Python's Free-Threading version (filename with t) removes the GIL (Global Interpreter Lock) enabling true multi-core parallelism. Combined with ap_ds v v3.1.0/3.1.1's batch parsing, 120 MP3 files can be parsed in just 0.33 seconds.

⚠️ Version Selection Note: Python 3.15t is currently a beta release (b4) and may have unknown issues. For a more stable environment, we recommend Python 3.14t (stable). Both support GIL-less free-threading mode.

Windows Users

Python 3.14t (Stable) Downloads:

Architecture Download Link
Windows 64-bit python-3.14.4t-amd64.zip
Windows 32-bit python-3.14.4t-win32.zip
ARM64 python-3.14.4t-arm64.zip

Python 3.15t (Beta) Downloads:

Architecture Download Link
Windows 64-bit python-3.15.0b4t-amd64.zip
Windows 32-bit python-3.15.0b4t-win32.zip
ARM64 python-3.15.0b4t-arm64.zip

📦 ZIP – Extract and Use: Download and extract to any directory, add the python.exe path to your system PATH, and you're ready to go. No need to run an EXE installer – deployment takes seconds.

Linux Users

Option 1: Using Package Manager

Fedora:

sudo dnf install python3.14-freethreading

After installation, the interpreter is located at /usr/bin/python3.14t.

Ubuntu/Debian (using deadsnakes PPA):

sudo add-apt-repository ppa:deadsnakes
sudo apt-get update
sudo apt-get install python3.14-nogil

This PPA provides the -nogil version, which is also a GIL-disabled build.

Option 2: Using Conda (Cross-platform)

Install from the conda-forge channel:

conda create -n nogil -c conda-forge python-freethreading
mamba create -n nogil -c conda-forge python-freethreading

Option 3: Compiling from Source (General Method)

# Download Python 3.14 source
wget https://www.python.org/ftp/python/3.14.0/Python-3.14.0.tgz
tar -xzf Python-3.14.0.tgz
cd Python-3.14.0

# Configure: --disable-gil is the key parameter
./configure --disable-gil

# Compile and install
make -j$(nproc)
sudo make install

macOS Users

Option 1: Official Installer (Graphical)

  1. Download the macOS installer package from python.org
  2. Run the installer, click the "Customize" button on the "Installation Type" screen
  3. In the component list that appears, check the "Free-threaded Python" option, and continue with the installation

Option 2: Using Homebrew

brew install python-freethreading

After installation, the interpreter is located at $(brew --prefix)/bin/python3.14t.

Verify Installation

Run the following commands to verify that the free-threading version is working correctly:

# Check version information (should include "free-threading build")
python3.14t --version

# Check GIL status (output False means GIL is disabled)
python3.14t -c "import sys; print(sys._is_gil_enabled())"

Create a Virtual Environment

python3.14t -m venv my_env
source my_env/bin/activate  # Linux/macOS
my_env\Scripts\activate     # Windows

💡 Tip: Using python3.14t -m venv creates a GIL-less isolated environment.

Quick Start

from ap_ds import AudioLibrary

# Initialize the library
lib = AudioLibrary()

# Play an audio file
aid = lib.play_from_file("music/song.mp3")

# Control playback
lib.pause_audio(aid)      # Pause
lib.play_audio(aid)       # Resume
lib.seek_audio(aid, 30.5) # Seek to 30.5 seconds

# Stop and get the played duration
duration = lib.stop_audio(aid)
print(f"Played {duration:.2f} seconds")

🚀 Batch Parsing (New in v v3.1.0/3.1.1)

from ap_ds import batch_get_metadata

# Batch parse an entire folder (120 MP3s in just 0.33 seconds!)
results = batch_get_metadata("/music/playlist/", max_workers=8)

for meta in results:
    print(f"{meta['path']}: {meta['duration']}s, {meta['bitrate']}bps")

Batch Parsing APIs at a Glance:

API Description
batch_get_metadata() Batch parse, returns full metadata list
batch_get_duration() Batch get durations, returns {path: duration}
batch_get_metadata_by_type() Filter batch parsing by format

DAP Playlist System

Audio files are automatically recorded to DAP (Dvs Audio Playlist) when played:

# Files are automatically recorded
aid1 = lib.play_from_file("song1.mp3")
aid2 = lib.play_from_file("song2.ogg")

# Get all recordings
recordings = lib.get_dap_recordings()
print(f"Recorded {len(recordings)} files")

# Save as JSON
success = lib.save_dap_to_json("my_playlist.ap_ds-dap")

DAP stores only metadata (path, duration, bitrate, channels), not audio data. Each record uses approximately 150 bytes of memory.

Platform Support

Windows

  • Automatically downloads SDL2.dll and SDL2_mixer.dll with hash verification
  • No manual configuration required
  • Supports Windows 7 and above

macOS

  • Automatically downloads SDL2.framework and SDL2_mixer.framework with hash verification
  • No manual configuration required
  • Supports macOS 10.9 and above

Linux

Intelligent multi-layer import system:

  1. System library check: Uses system-installed SDL2 libraries
  2. User configuration: Checks paths saved from previous runs
  3. Automatic installation: Detects package manager and installs required packages
  4. Interactive guidance: Provides manual options if all above fail

Package manager support:

# Ubuntu/Debian
sudo apt-get install libsdl2-dev libsdl2-mixer-dev

# Fedora
sudo dnf install SDL2-devel SDL2_mixer-devel

# Arch
sudo pacman -S sdl2 sdl2_mixer

Embedded ARM64

Tested on:

  • Orange Pi 4 Pro (Allwinner A733, 2xA76 + 6xA55 @ 2.0GHz)
  • Raspberry Pi 5 (BCM2712, 4xA76 @ 2.4GHz)

Both run Ubuntu 22.04 with full audio functionality via 3.5mm output. Memory growth after extensive testing: ~4MB.

ap_ds Audio Library – Complete API Reference

Version: v3.1.0/3.1.1
Documentation Date: July 2026
Project Homepage: https://apds.top

Table of Contents

  1. AudioLibrary Class – Complete API

    • Initialization
    • Playback Methods
    • Control Methods
    • Volume Methods
    • Fade & Transition Methods
    • Metadata Methods
    • Batch Parsing Methods (New in v v3.1.0/3.1.1)
    • DAP System Methods
    • Resource Management
    • Internal Helper Methods
  2. Top-Level Convenience Functions

  3. AudioParser Class – Metadata API

  4. AudioInfo Module – Format-Specific Parsers

  5. SDL2 Integration Layer

  6. Constants Reference

  7. Exception Handling

AudioLibrary Class – Complete API

The AudioLibrary class is the main interface for audio playback, control, and metadata extraction. It manages all SDL2 resources, provides non-blocking playback, and maintains the DAP (Dvs Audio Playlist) recording system.

Initialization

__init__(frequency: int = 44100, format: int = MIX_DEFAULT_FORMAT, channels: int = 2, chunksize: int = 2048) -> None

Description:
Initializes the SDL2 audio subsystem and SDL2_mixer library. This method must be called before any playback operations. It sets up the audio device with the specified parameters and registers an exit handler for automatic resource cleanup.

Parameters:

Parameter Type Default Description
frequency int 44100 Audio sample rate (Hz). Common values: 44100 (CD quality), 48000 (DVD/video), 22050 (voice). Higher values improve quality but consume more CPU.
format int MIX_DEFAULT_FORMAT Audio sample format. Typically AUDIO_S16SYS (16-bit signed, system endianness). See constants reference for alternatives.
channels int 2 Number of audio channels. 1 = mono, 2 = stereo. Mono files will be automatically upmixed to stereo if the device is configured for stereo.
chunksize int 2048 Buffer size in samples. Larger values reduce CPU usage but increase latency. For real-time applications, smaller values (1024) may be better.

Raises:

  • RuntimeError: If SDL2 initialization fails (e.g., no audio device available).
  • RuntimeError: If mixer initialization fails (e.g., unsupported format).

Example:

from ap_ds import AudioLibrary

# Default configuration (CD quality, stereo, low latency)
lib = AudioLibrary()

# Custom configuration for voice playback
lib_voice = AudioLibrary(frequency=22050, channels=1, chunksize=1024)

Internal Behavior:

  1. Calls SDL_Init(SDL_INIT_AUDIO) to initialize the audio subsystem.
  2. Calls Mix_OpenAudio(frequency, format, channels, chunksize) to open the mixer.
  3. Registers self.cleanup_function with atexit to ensure resource cleanup.
  4. Initializes internal data structures:
    • _audio_cache: Dict[str, Mix_Chunk] – Cached sound effects
    • _music_cache: Dict[str, Mix_Music] – Cached music tracks
    • _channel_info: Dict[int, Dict] – Active playback sessions
    • _aid_to_filepath: Dict[int, str] – AID to file path mapping
    • _aid_counter: int – Sequential AID generator
    • _dap_recordings: List[Dict] – DAP history list

Playback Methods

play_from_file(file_path: str, loops: int = 0, start_pos: float = 0.0) -> int

Description:
Loads and plays an audio file directly from disk. The file is loaded into memory and played immediately. For .ap-ds-dap files, this method records metadata but does not play audio (DAP files contain metadata only).

Parameters:

Parameter Type Default Description
file_path str Required Full path to the audio file. Supported formats: MP3, WAV, FLAC, OGG
loops int 0 Number of loops after the first play. 0 = play once, -1 = loop indefinitely.
start_pos float 0.0 Starting position in seconds. May not be supported for sound effects (short WAV files).

Note: AAC files support metadata parsing but cannot be played or have other operations performed on them due to SDL2 limitations.

Returns:
int – A unique Audio ID (AID) identifying this playback instance. This ID can be used with all control methods.

Raises:

  • FileNotFoundError: If the file does not exist.
  • RuntimeError: If the file format is unsupported or the audio cannot be loaded.
  • RuntimeError: If playback fails (e.g., no available channel).

Behavior by File Type:

File Type Mode Seek Support Fade Support
MP3, OGG, FLAC Music (Mix_PlayMusic) Yes Yes
WAV (duration ≥ threshold) Music (Mix_PlayMusic) Yes Yes
WAV (duration < threshold) Sound effect (Mix_PlayChannel) No No
Other formats Sound effect (Mix_PlayChannel) No No

Example:

# Play a song once from the beginning
aid = lib.play_from_file("song.mp3")

# Loop a short sound effect 5 times
aid = lib.play_from_file("beep.wav", loops=5)

# Start playing from 30 seconds in
aid = lib.play_from_file("podcast.mp3", start_pos=30.0)

# Loop indefinitely (background music)
aid = lib.play_from_file("ambient.ogg", loops=-1)

Internal Workflow:

  1. Increments _aid_counter to generate a new AID.
  2. Calls _add_to_dap_recordings() to record metadata (if available).
  3. Determines playback mode via _is_music_file().
  4. For music files:
    • Calls Mix_LoadMUS() to load the file.
    • Calls Mix_PlayMusic() to start playback.
    • Stores the Mix_Music object in _music_cache.
    • Sets channel = -1 (music uses a dedicated channel).
  5. For sound effects:
    • Calls Mix_LoadWAV() to load the file.
    • Calls Mix_PlayChannel(-1, audio, loops) to play on the first available channel.
    • Stores the Mix_Chunk object in _audio_cache.
    • Returns the actual channel number.
  6. Stores playback information in _channel_info[channel].
  7. If start_pos > 0, calls _seek_audio().

play_from_memory(file_path: str, loops: int = 0, start_pos: float = 0.0) -> int

Description:
Plays an audio file that has been preloaded into memory via new_aid(). This method is faster than play_from_file() for repeated plays of the same file since the file is already cached.

Parameters: Same as play_from_file().

Returns: int – Audio ID (AID).

Raises:

  • ValueError: If the file has not been preloaded via new_aid().
  • RuntimeError: If playback fails.

Example:

# Preload sound effects
lib.new_aid("gunshot.wav")
lib.new_aid("explosion.wav")

# Play from memory (very fast, no disk I/O)
aid = lib.play_from_memory("gunshot.wav")

Internal Workflow:

  1. Generates a new AID.
  2. Records to DAP.
  3. Checks if the file is in _music_cache or _audio_cache.
  4. Plays from the cached object.
  5. Records playback info in _channel_info.

new_aid(file_path: str) -> int

Description:
Preloads an audio file into memory without playing it. This is useful for caching sound effects or music tracks that will be played multiple times. The file is loaded once and stored in the appropriate cache.

Parameters:

Parameter Type Description
file_path str Path to the audio file to cache.

Returns: int – Audio ID (AID) for the cached file.

Raises:

  • FileNotFoundError: If the file does not exist.
  • RuntimeError: If the file format is unsupported or loading fails.

Example:

# Cache commonly used sounds
sounds = {
    'hit': lib.new_aid("hit.wav"),
    'jump': lib.new_aid("jump.wav"),
    'coin': lib.new_aid("coin.wav")
}

# Play from memory later
lib.play_from_memory(sounds['hit'])

Internal Workflow:

  1. Increments AID counter.
  2. Records to DAP.
  3. Determines music vs sound effect via _is_music_file().
  4. If music:
    • Calls Mix_LoadMUS().
    • Stores in _music_cache[file_path].
  5. If sound effect:
    • Calls Mix_LoadWAV().
    • Stores in _audio_cache[file_path].
  6. Maps AID to file path in _aid_to_filepath.

Control Methods

play_audio(aid: int) -> None

Description:
Resumes playback of a paused audio instance. If the audio is not paused, this method has no effect.

Parameters:

Parameter Type Description
aid int Audio ID returned by play_from_file(), play_from_memory(), or new_aid().

Raises:

  • ValueError: If the AID is invalid or the audio is not paused.

Example:

aid = lib.play_from_file("song.mp3")
time.sleep(2)
lib.pause_audio(aid)      # Pause after 2 seconds
time.sleep(1)
lib.play_audio(aid)       # Resume after 1 second of pause

Internal Behavior:

  1. Finds the channel associated with the AID via _find_channel_by_aid().
  2. Retrieves playback info from _channel_info.
  3. If audio is paused (info['paused'] is True):
    • For music (is_music is True): Calls Mix_ResumeMusic().
    • For sound effects: Calls Mix_Resume(channel).
    • Resets info['paused'] to False.
    • Adjusts info['start_time'] to account for pause duration.

pause_audio(aid: int) -> None

Description:
Pauses the audio instance specified by the AID. The audio can later be resumed with play_audio().

Parameters: Same as play_audio().

Raises:

  • ValueError: If the AID is invalid or the audio is already paused.

Example:

aid = lib.play_from_file("song.mp3")
time.sleep(5)
lib.pause_audio(aid)

Internal Behavior:

  1. Finds the channel associated with the AID.
  2. Retrieves playback info.
  3. If not already paused:
    • For music: Mix_PauseMusic().
    • For sound effects: Mix_Pause(channel).
    • Sets info['paused'] = True.
    • Records paused_position = time.time() - info['start_time'].

stop_audio(aid: int) -> float

Description:
Stops playback of the specified audio instance and releases associated resources. The audio cannot be resumed after stopping.

Parameters: Same as play_audio().

Returns: float – Total playback duration in seconds before stopping.

Raises:

  • ValueError: If the AID is invalid.

Example:

aid = lib.play_from_file("song.mp3")
time.sleep(10)
duration = lib.stop_audio(aid)
print(f"Played {duration:.2f} seconds")

Internal Behavior:

  1. Finds the channel associated with the AID.
  2. Calculates played_time (current time minus start time, or paused position).
  3. If music: Mix_HaltMusic().
  4. If sound effect: Mix_HaltChannel(channel).
  5. Removes the entry from _channel_info.
  6. Returns played_time.

seek_audio(aid: int, position: float) -> None

Description:
Seeks to a specific position (in seconds) within an audio track. This method only works for music-mode files (MP3, OGG, FLAC, and long WAV files). Sound effects (short WAVs) do not support seeking.

Parameters:

Parameter Type Description
aid int Audio ID.
position float Target position in seconds. Must be between 0 and the total duration.

Raises:

  • ValueError: If the AID is invalid.
  • RuntimeError: If the audio is not seekable (sound effect mode).

Example:

# Seek to 30 seconds into the track
lib.seek_audio(aid, 30.0)

Internal Behavior (Music):

  1. Stops current playback using Mix_HaltMusic().
  2. Reloads the music file using Mix_LoadMUS().
  3. Starts playback from the beginning using Mix_PlayMusic().
  4. If Mix_SetMusicPosition is available, seeks to the target position.
  5. Updates _channel_info with the new start time.

Internal Behavior (Sound Effect):

  1. Stops current playback using Mix_HaltChannel().
  2. Replays the cached chunk using Mix_PlayChannel().
  3. This effectively starts from the beginning; seeking is not supported.

Volume Methods

set_volume(aid: int, volume: int) -> bool

Description:
Sets the volume for the specified audio instance. Volume is scaled from 0 (mute) to 128 (maximum). The value is automatically clamped.

Parameters:

Parameter Type Description
aid int Audio ID.
volume int Volume value (0–128). Values outside this range will be clamped.

Returns: bool – True if the volume was set successfully, False otherwise.

Example:

# Set volume to 50% (64 out of 128)
lib.set_volume(aid, 64)

# Mute
lib.set_volume(aid, 0)

# Maximum
lib.set_volume(aid, 128)

Internal Behavior:

  1. Finds the channel associated with the AID.
  2. Clamps volume to the range [0, 128].
  3. If music: Calls Mix_VolumeMusic(volume).
  4. If sound effect: Calls Mix_Volume(channel, volume).

get_volume(aid: int) -> int

Description:
Returns the current volume of the specified audio instance.

Parameters: Same as set_volume().

Returns: int – Current volume (0–128). Returns 0 if the AID is invalid.

Example:

current = lib.get_volume(aid)
print(f"Current volume: {current}")

Internal Behavior:

  1. Finds the channel associated with the AID.
  2. If music: Calls Mix_VolumeMusic(-1) (-1 means get without setting).
  3. If sound effect: Calls Mix_Volume(channel, -1).

Fade & Transition Methods

fadein_music(aid: int, loops: int = -1, ms: int = 0) -> bool

Description:
Fades in music over the specified duration. This method stops any currently playing music and starts the specified track with a fade-in effect. Only works for music-mode files.

Parameters:

Parameter Type Default Description
aid int Required Audio ID of the music file.
loops int -1 Number of loops. -1 = infinite, 0 = once, n = n times.
ms int 0 Fade-in duration in milliseconds. 0 means no fade-in (start immediately).

Returns: bool – True if fade-in started successfully, False otherwise.

Example:

# Fade in over 2 seconds
lib.fadein_music(aid, loops=-1, ms=2000)

# Fade in with no looping (play once)
lib.fadein_music(aid, loops=0, ms=500)

Internal Behavior:

  1. Searches _channel_info for the AID and verifies it's music.
  2. Calls _add_to_dap_recordings() (records to DAP).
  3. Loads the music file if not already cached.
  4. Stops any current music with Mix_HaltMusic().
  5. Calls Mix_FadeInMusic(music, loops, ms).
  6. If successful, updates _channel_info with the new start time.

fadein_music_pos(aid: int, loops: int = -1, ms: int = 0, position: float = 0.0) -> bool

Description:
Fades in music starting from a specific position. This is useful for resuming playback from a saved timestamp.

Parameters:

Parameter Type Default Description
aid int Required Audio ID.
loops int -1 Number of loops.
ms int 0 Fade-in duration in milliseconds.
position float 0.0 Starting position in seconds.

Returns: bool – True if successful, False otherwise.

Example:

# Fade in over 1 second starting from the 30-second mark
lib.fadein_music_pos(aid, loops=-1, ms=1000, position=30.0)

Internal Behavior:

  1. Checks if Mix_FadeInMusicPos is available in the loaded SDL2_mixer.
  2. Same as fadein_music() but calls Mix_FadeInMusicPos() with the position parameter.

fadeout_music(ms: int = 0) -> bool

Description:
Fades out the currently playing music over the specified duration.

Parameters:

Parameter Type Default Description
ms int 0 Fade-out duration in milliseconds. 0 means stop immediately.

Returns: bool – True if fade-out started, False if no music is playing.

Example:

# Smooth fade out over 3 seconds
lib.fadeout_music(ms=3000)

# Stop immediately
lib.fadeout_music(ms=0)

Internal Behavior:

  1. Calls Mix_FadeOutMusic(ms).
  2. Returns True if the return value is 1 (success), False otherwise.

is_music_playing() -> bool

Description:
Checks if any music is currently playing.

Returns: bool – True if music is playing, False otherwise.

Example:

if lib.is_music_playing():
    print("Music is playing")
else:
    print("Music is stopped or paused")

Internal Behavior: Calls Mix_PlayingMusic().

is_music_paused() -> bool

Description:
Checks if music is paused.

Returns: bool – True if paused, False otherwise.

Internal Behavior: Calls Mix_PausedMusic().

get_music_fading() -> int

Description:
Returns the current fade status of the music.

Returns: int – One of:

  • 0 (MUS_NO_FADING): No fade in progress.
  • 1 (MUS_FADING_OUT): Fading out.
  • 2 (MUS_FADING_IN): Fading in.

Example:

state = lib.get_music_fading()
if state == 1:
    print("Fading out...")

Internal Behavior: Calls Mix_FadingMusic().

Metadata Methods

get_audio_duration(source: Union[str, int], is_file: bool = False) -> Union[int, Tuple[int, str]]

Description:
Returns the duration of an audio file in seconds. Can accept either a file path (string) or an AID (integer).

Parameters:

Parameter Type Default Description
source str or int Required File path or AID.
is_file bool False If True, treats source as a file path; if False, treats it as an AID.

Returns:

  • On success: int – Duration in seconds (floor).
  • On failure: Tuple[int, str] – (0, error_message).

Example:

# Via file path
duration = lib.get_audio_duration("song.mp3", is_file=True)

# Via AID
aid = lib.play_from_file("song.mp3")
duration = lib.get_audio_duration(aid, is_file=False)

Internal Behavior:

  1. If is_file is True, calls _get_duration_by_filepath(str(source)).
  2. If is_file is False, looks up the file path from _aid_to_filepath, then calls _get_duration_by_filepath().

get_audio_metadata(source: Union[str, int], is_file: bool = False) -> Optional[Dict]

Description:
Returns full metadata for an audio file: duration, sample rate, channels, bitrate, and format.

Parameters: Same as get_audio_duration().

Returns: Dict or None (if parsing fails).

Dictionary Structure:

{
    'path': str,           # Full file path
    'format': str,         # File extension (mp3, wav, etc.)
    'duration': int,       # Duration in seconds (floor)
    'length': float,       # Exact duration in seconds (float)
    'sample_rate': int,    # Sample rate in Hz
    'channels': int,       # 1 (mono) or 2 (stereo)
    'bitrate': int         # Bitrate in bps
}

Example:

metadata = lib.get_audio_metadata("song.flac", is_file=True)
if metadata:
    print(f"Sample rate: {metadata['sample_rate']} Hz")
    print(f"Bitrate: {metadata['bitrate'] / 1000:.0f} kbps")

get_audio_metadata_by_path(file_path: str) -> Optional[Dict]

Description:
Convenience method equivalent to get_audio_metadata(file_path, is_file=True).

Parameters:

Parameter Type Description
file_path str Path to the audio file.

Returns: Dict or None.

get_audio_metadata_by_aid(aid: int) -> Optional[Dict]

Description:
Convenience method equivalent to get_audio_metadata(aid, is_file=False).

Parameters:

Parameter Type Description
aid int Audio ID.

Returns: Dict or None.

Batch Parsing Methods (New in v v3.1.0/3.1.1)

batch_get_metadata(file_paths: Union[List[str], str], max_workers: Optional[int] = None, show_progress: bool = False) -> List[Dict]

Description:
Parses multiple audio files in parallel using multi-processing. On Python 3.15t (GIL-less environment), this achieves true multi-core parallel acceleration.

Parameters:

Parameter Type Default Description
file_paths List[str] or str Required List of file paths, or a single directory path string. If a directory is provided, it will be recursively scanned for all supported audio files.
max_workers Optional[int] None Maximum number of worker processes. Defaults to the number of CPU cores.
show_progress bool False If True, prints progress to stdout.

Returns: List[Dict] – List of metadata dictionaries. Files that fail to parse are omitted.

Performance Reference:

  • 120 MP3 files, 8-process parallel: 0.33 seconds
  • Speedup vs serial parsing: 4.13x
  • Compared to Mutagen (single-threaded): 2.94x faster

Example:

from ap_ds import AudioLibrary

lib = AudioLibrary()

# Batch parse an entire folder
results = lib.batch_get_metadata("/music/playlist/", max_workers=8, show_progress=True)

for meta in results:
    print(f"{meta['path']}: {meta['duration']}s, {meta['bitrate']}bps")

# Parse a list of files
files = ["song1.mp3", "song2.flac", "song3.ogg"]
results = lib.batch_get_metadata(files)

Internal Behavior:

  1. If file_paths is a directory path, recursively scans to collect all supported audio files (.mp3, .wav, .flac, .ogg, .aac).
  2. Creates a process pool using ProcessPoolExecutor, assigning files to worker processes.
  3. Each worker process calls _parse_single_file() to parse a single file.
  4. Collects and returns all successfully parsed metadata.

batch_get_duration(file_paths: Union[List[str], str], max_workers: Optional[int] = None) -> Dict[str, int]

Description:
Batch gets durations for multiple audio files.

Parameters:

Parameter Type Default Description
file_paths List[str] or str Required List of file paths, or a single directory path string.
max_workers Optional[int] None Maximum number of worker processes. Defaults to the number of CPU cores.

Returns: Dict[str, int] – {file_path: duration_in_seconds} mapping. Files that fail to parse are omitted.

Example:

durations = lib.batch_get_duration("/music/playlist/")
for path, duration in durations.items():
    print(f"{path}: {duration} seconds")

batch_get_metadata_by_type(file_paths: Union[List[str], str], file_type: str, max_workers: Optional[int] = None) -> List[Dict]

Description:
Batch parses audio files but only returns results for the specified format.

Parameters:

Parameter Type Default Description
file_paths List[str] or str Required List of file paths, or a single directory path string.
file_type str Required File extension filter (e.g., "mp3", "flac").
max_workers Optional[int] None Maximum number of worker processes. Defaults to CPU cores.

Returns: List[Dict] – List of metadata dictionaries matching the specified format.

Example:

# Parse only MP3 files
mp3_results = lib.batch_get_metadata_by_type("/music/", "mp3")

DAP System Methods

save_dap_to_json(save_path: str) -> bool

Description:
Saves all current DAP records in memory to a JSON file. The file extension must be .ap-ds-dap.

Parameters:

Parameter Type Description
save_path str Output file path (must end with .ap-ds-dap).

Returns: bool – True if successful, False otherwise.

Raises:

  • ValueError: If the file extension is not .ap-ds-dap.

Example:

if lib.save_dap_to_json("my_playlist.ap-ds-dap"):
    print("Playlist saved!")
else:
    print("Save failed")

File Format:

[
  {
    "path": "/music/song1.mp3",
    "duration": 240,
    "bitrate": 320000,
    "channels": 2
  },
  {
    "path": "/music/song2.ogg",
    "duration": 180,
    "bitrate": 128000,
    "channels": 2
  }
]

get_dap_recordings() -> List[Dict]

Description:
Returns a copy of all current DAP records in memory.

Returns: List[Dict] – List of record dictionaries.

Example:

records = lib.get_dap_recordings()
for rec in records:
    print(f"{rec['path']}: {rec['duration']}s")

clear_dap_recordings() -> None

Description:
Clears all DAP records from memory. This operation is irreversible unless you have previously saved the records.

Example:

lib.clear_dap_recordings()
print(f"Remaining records: {len(lib.get_dap_recordings())}")  # 0

_add_to_dap_recordings(file_path: str) -> None

Description:
Internal method that adds a file to the DAP recording list. Automatically called by play_from_file(), play_from_memory(), and new_aid().

Parameters:

Parameter Type Description
file_path str Path to the audio file.

Internal Behavior:

  1. Calls get_audio_metadata_by_path(file_path) to extract metadata.
  2. If metadata is available, creates a record containing path, duration, bitrate, and channels.
  3. Uses O(1) set-based deduplication (v v3.1.0/3.1.1 optimization) to avoid duplicate records.

Resource Management

clear_memory_cache() -> None

Description:
Releases all cached audio data from memory (both sound effects and music). After calling this method, any subsequent playback will need to reload files from disk.

Example:

lib.clear_memory_cache()
print("All cached audio cleared")

Internal Behavior:

  1. Iterates through _audio_cache and calls Mix_FreeChunk() for each.
  2. Iterates through _music_cache and calls Mix_FreeMusic() for each.
  3. Clears both dictionaries.

cleanup_function() -> None

Description:
Registered with atexit to clean up all resources when the Python interpreter exits. This method:

  • Clears the memory cache.
  • Closes the audio device with Mix_CloseAudio().
  • Shuts down SDL with SDL_Quit().

Users should not call this method directly; it is automatically invoked during interpreter shutdown.

Internal Helper Methods

These methods are for internal use but are documented here for completeness.

_find_channel_by_aid(aid: int) -> Optional[int]

Description:
Searches _channel_info for the channel associated with the given AID.

Returns: int – Channel number, or None if not found.

_get_file_path_by_aid(aid: int) -> Optional[str]

Description:
Searches _channel_info for the file path associated with the given AID.

Returns: str – File path, or None if not found.

_is_music_file(file_path: str) -> bool

Description:
Determines whether a file should be treated as music (seekable) or sound effect (non-seekable). The decision is based on:

  • File extension (.mp3, .ogg, .flac → music).
  • For WAV files: compares duration against WAV_THRESHOLD.

Returns: bool – True for music mode, False for sound effect mode.

_seek_audio(channel: int, position: float) -> None

Description:
Internal method that performs the actual seek operation on the given channel.

_get_duration_by_filepath(file_path: str) -> Union[int, Tuple[int, str]]

Description:
Internal method that extracts duration using the audio_parser. Returns an integer (success) or a tuple (0, error_message) (failure).

_get_file_duration(file_path: str) -> float

Description:
Internal method that returns duration as a float, defaulting to 0.0 on error.

Top-Level Convenience Functions

ap_ds exports the following convenience functions directly at the package top level, allowing users to call them quickly without creating an AudioLibrary instance.

batch_get_metadata(file_paths, max_workers=None, show_progress=False) -> List[Dict]

Description:
Batch parses audio files. Equivalent to AudioLibrary.batch_get_metadata().

Example:

from ap_ds import batch_get_metadata

results = batch_get_metadata("/music/", max_workers=8)

batch_get_duration(file_paths, max_workers=None) -> Dict[str, int]

Description:
Batch gets audio durations. Equivalent to AudioLibrary.batch_get_duration().

Example:

from ap_ds import batch_get_duration

durations = batch_get_duration("/music/")

batch_get_metadata_by_type(file_paths, file_type, max_workers=None) -> List[Dict]

Description:
Filters batch parsing by format. Equivalent to AudioLibrary.batch_get_metadata_by_type().

Example:

from ap_ds import batch_get_metadata_by_type

mp3s = batch_get_metadata_by_type("/music/", "mp3")

is_full_performance() -> bool

Description:
Checks whether the current runtime is in full-performance mode (Python 3.15t + profiling available).

Returns: bool – True if in full-performance mode, False otherwise.

Example:

from ap_ds import is_full_performance

if is_full_performance():
    print("🚀 Full-performance mode!")

get_runtime_info() -> dict

Description:
Gets runtime environment information.

Returns: dict – Diagnostic information including Python version, GIL status, profiling availability, CPU core count, etc.

Example:

from ap_ds import get_runtime_info

info = get_runtime_info()
print(info["python_version"], info["gil_enabled"])

AudioParser Class – Metadata API

The AudioParser class provides a unified interface for extracting metadata from audio files. It is used internally by AudioLibrary but can also be used directly.

get_audio_parser() -> AudioParser

Description:
Singleton factory function that returns an AudioParser instance. The same instance is reused across multiple calls.

Note: This function is located in the ap_ds.audio_parser submodule and must be imported from there.

Example:

from ap_ds.audio_parser import get_audio_parser

parser = get_audio_parser()
metadata = parser.get_audio_metadata("song.mp3")

AudioParser.get_audio_metadata(file_path: str) -> Optional[Dict]

Description:
Returns metadata for the given audio file. Delegates to format-specific parsers in audio_info.py.

Parameters:

Parameter Type Description
file_path str Path to the audio file.

Returns: Dict or None (if parsing fails).

Dictionary Structure:

{
    'path': file_path,
    'format': ext,           # e.g., 'mp3', 'wav'
    'duration': int(info.length),
    'length': float(info.length),
    'sample_rate': info.sample_rate,
    'channels': info.channels,
    'bitrate': info.bitrate
}

AudioParser.get_audio_duration(file_path: str) -> int

Description:
Returns the duration of an audio file in seconds (floor).

Returns: int – Duration in seconds, or 0 on error.

AudioParser.batch_get_metadata(file_paths, max_workers=None, show_progress=False) -> List[Dict]

Description:
Batch parsing method for the AudioParser class. Equivalent to the top-level batch_get_metadata() function.

Example:

from ap_ds.audio_parser import get_audio_parser

parser = get_audio_parser()
results = parser.batch_get_metadata("/music/", max_workers=8)

AudioParser.batch_get_duration(file_paths, max_workers=None) -> Dict[str, int]

Description:
Batch get duration method for the AudioParser class. Equivalent to the top-level batch_get_duration() function.

AudioParser.batch_get_metadata_by_type(file_paths, file_type, max_workers=None) -> List[Dict]

Description:
Format-filtered batch parsing method for the AudioParser class. Equivalent to the top-level batch_get_metadata_by_type() function.

Legacy Methods (Retained for Backward Compatibility)

These methods are aliases and behave identically to get_audio_duration():

  • get_ogg_duration(file_path)
  • get_flac_duration(file_path)
  • get_mp3_duration(file_path)
  • get_wav_duration(file_path)
  • get_duration_by_extension(file_path)

AudioInfo Module – Format-Specific Parsers

The audio_info.py module contains low-level parsers for each supported format. Each parser returns a StreamInfo object with the following attributes:

  • length: float – Duration in seconds (precise).
  • sample_rate: int – Sample rate in Hz.
  • channels: int – Number of audio channels.
  • bitrate: int – Bitrate in bps.

StreamInfo Class

Attributes:

  • length (float) – Duration in seconds.
  • sample_rate (int) – Sample rate in Hz.
  • channels (int) – Number of channels (1 = mono, 2 = stereo).
  • bitrate (int) – Bitrate in bps.

String Representation: "<StreamInfo length=3.141593s rate=44100Hz channels=2 bitrate=320000bps>"

WAVFile Class

Parser: Reads RIFF chunks, extracts fmt and data chunks.

Accuracy: 100% (based on file structure, no heuristics).

Method:

  1. Reads RIFF header and WAVE identifier.
  2. Scans fmt (format) and data chunks.
  3. Extracts from fmt: channels, sample_rate, block_align.
  4. Extracts from data: data_size.
  5. Calculates total_frames = data_size // block_align.
  6. Calculates length = total_frames / sample_rate.
  7. Calculates bitrate = sample_rate * block_align * 8 // channels.

FLACFile Class

Parser: Reads the STREAMINFO block (mandatory in all FLAC files).

Accuracy: 100% (from metadata).

Method:

  1. Validates fLaC magic bytes.
  2. Iterates through metadata blocks.
  3. For block type 0 (STREAMINFO):
    • Extracts sample_rate from bytes 10–12.
    • Extracts channels from byte 12.
    • Extracts total_samples from bytes 13–17.
    • Calculates length = total_samples / sample_rate.
    • Calculates bitrate = file_size * 8 / length.
  4. Returns StreamInfo.

MP3File Class

Parser: Frame-by-frame scanner that counts frames and accumulates sample counts.

Accuracy: >98% (limited by variable bitrate and incomplete last frames).

Method:

  1. Scans the file byte by byte looking for 0xFF (frame sync).
  2. Reads the next 3 bytes to form the frame header.
  3. Extracts:
    • bitrate from bits 4–7 of byte 1.
    • sample_rate from bits 2–3 of byte 1.
  4. Calculates frame length: 144000 * bitrate / sample_rate.
  5. Advances the file pointer by the frame length.
  6. Increments total_frames.
  7. After scanning, calculates:
    • length = total_frames * 1152 / sample_rate (1152 samples per MP3 frame).
    • bitrate = file_size * 8 / length.

OGGFile Class

Parser: Reads Ogg pages, extracts granular position (total samples) from the last page.

Accuracy: 99.99% (granular position is precise, may be slightly off if file is truncated).

Method:

  1. Scans Ogg pages (each begins with OggS).
  2. Extracts granular position from bytes 6–13.
  3. Keeps the maximum granular position (last page).
  4. Also reads the first packet to extract sample_rate and channels from the Vorbis identification header.
  5. Calculates length = last_granule / sample_rate.
  6. Calculates bitrate = file_size * 8 / length.

AACFile Class

Parser: Reads ADTS (Audio Data Transport Stream) frames, accumulates sample counts.

Accuracy: >99% (based on frame counting, similar to MP3).

Method:

  1. Scans for 0xFFF (ADTS sync word).
  2. Reads the next 7 bytes as the header.
  3. Extracts:
    • sample_rate from bits 2–5 of byte 2.
    • channels from bits 6–7 of byte 2 and byte 3.
    • frame_length from bytes 3–5.
  4. Adds 1024 to total_samples (samples per AAC frame).
  5. Jumps forward by frame_length - 7.
  6. Calculates length = total_samples / sample_rate.
  7. Calculates bitrate = file_size * 8 / length.

open_audio(filename: str) -> FileType

Description:
Factory function that returns an appropriate parser instance based on the file extension.

Parameters:

Parameter Type Description
filename str Path to the audio file.

Returns: An instance of WAVFile, FLACFile, MP3File, OGGFile, or AACFile.

Raises: ValueError if the format is unsupported.

SDL2 Integration Layer

The player.py module contains low-level bindings to SDL2 and SDL2_mixer using ctypes. All functions have argtypes and restype properly set to ensure cross-platform stability.

Global SDL2 Functions

Function C Binding Description
SDL_Init(flags) _sdl_lib.SDL_Init Initializes SDL subsystems.
SDL_Quit() _sdl_lib.SDL_Quit Shuts down SDL.
SDL_GetError() _sdl_lib.SDL_GetError Returns the last SDL error message.
SDL_Delay(ms) _sdl_lib.SDL_Delay Sleeps for the specified number of milliseconds.
SDL_RWFromFile(file, mode) _sdl_lib.SDL_RWFromFile Opens a file for reading using SDL's RWops.

Global SDL2_mixer Functions

Function C Binding Description
Mix_OpenAudio(freq, format, channels, chunksize) _mix_lib.Mix_OpenAudio Opens the audio device.
Mix_CloseAudio() _mix_lib.Mix_CloseAudio Closes the audio device.
Mix_LoadWAV_RW(rwops, freesrc) _mix_lib.Mix_LoadWAV_RW Loads a WAV file into a Mix_Chunk.
Mix_LoadMUS_RW(rwops, freesrc) _mix_lib.Mix_LoadMUS_RW Loads a music file into a Mix_Music.
Mix_FreeChunk(chunk) _mix_lib.Mix_FreeChunk Frees a Mix_Chunk.
Mix_FreeMusic(music) _mix_lib.Mix_FreeMusic Frees a Mix_Music.
Mix_PlayChannel(channel, chunk, loops) _mix_lib.Mix_PlayChannel Plays a chunk on a channel.
Mix_PlayMusic(music, loops) _mix_lib.Mix_PlayMusic Plays music.
Mix_Pause(channel) _mix_lib.Mix_Pause Pauses a channel.
Mix_PauseMusic() _mix_lib.Mix_PauseMusic Pauses music.
Mix_Resume(channel) _mix_lib.Mix_Resume Resumes a channel.
Mix_ResumeMusic() _mix_lib.Mix_ResumeMusic Resumes music.
Mix_HaltChannel(channel) _mix_lib.Mix_HaltChannel Stops a channel.
Mix_HaltMusic() _mix_lib.Mix_HaltMusic Stops music.
Mix_Volume(channel, volume) _mix_lib.Mix_Volume Sets/gets channel volume.
Mix_VolumeMusic(volume) _mix_lib.Mix_VolumeMusic Sets/gets music volume.
Mix_Playing(channel) _mix_lib.Mix_Playing Checks if a channel is playing.
Mix_PlayingMusic() _mix_lib.Mix_PlayingMusic Checks if music is playing.
Mix_Paused(channel) _mix_lib.Mix_Paused Checks if a channel is paused.
Mix_PausedMusic() _mix_lib.Mix_PausedMusic Checks if music is paused.
Mix_FadeInMusic(music, loops, ms) _mix_lib.Mix_FadeInMusic Fades in music.
Mix_FadeInMusicPos(music, loops, ms, pos) _mix_lib.Mix_FadeInMusicPos Fades in music from a position.
Mix_FadeOutMusic(ms) _mix_lib.Mix_FadeOutMusic Fades out music.
Mix_FadingMusic() _mix_lib.Mix_FadingMusic Returns fade status.
Mix_SetMusicPosition(pos) _mix_lib.Mix_SetMusicPosition Seeks within music (if supported).

Constants Reference

SDL Initialization Flags

Constant Value Description
SDL_INIT_TIMER 0x00000001 Timer subsystem.
SDL_INIT_AUDIO 0x00000010 Audio subsystem.
SDL_INIT_VIDEO 0x00000020 Video subsystem.
SDL_INIT_JOYSTICK 0x00000200 Joystick subsystem.
SDL_INIT_HAPTIC 0x00001000 Haptic (force feedback) subsystem.
SDL_INIT_GAMECONTROLLER 0x00002000 Game controller subsystem.
SDL_INIT_EVENTS 0x00004000 Events subsystem.
SDL_INIT_EVERYTHING 0x0000F231 All subsystems.

Audio Formats

Constant Value Description
AUDIO_U8 0x0008 Unsigned 8-bit samples.
AUDIO_S8 0x8008 Signed 8-bit samples.
AUDIO_U16LSB 0x0010 Unsigned 16-bit, little-endian.
AUDIO_S16LSB 0x8010 Signed 16-bit, little-endian.
AUDIO_U16MSB 0x1010 Unsigned 16-bit, big-endian.
AUDIO_S16MSB 0x9010 Signed 16-bit, big-endian.
AUDIO_U16 AUDIO_U16LSB System-endian unsigned 16-bit.
AUDIO_S16 AUDIO_S16LSB System-endian signed 16-bit.
AUDIO_S32LSB 0x8020 Signed 32-bit, little-endian.
AUDIO_S32MSB 0x9020 Signed 32-bit, big-endian.
AUDIO_S32 AUDIO_S32LSB System-endian signed 32-bit.
AUDIO_F32LSB 0x8120 Float 32-bit, little-endian.
AUDIO_F32MSB 0x9120 Float 32-bit, big-endian.
AUDIO_F32 AUDIO_F32LSB System-endian float 32-bit.
MIX_DEFAULT_FORMAT AUDIO_S16SYS Default format (signed 16-bit, system endian).

Mixer Initialization Flags

Constant Value Description
MIX_INIT_FLAC 0x00000001 FLAC support.
MIX_INIT_MOD 0x00000002 MOD (tracker) support.
MIX_INIT_MP3 0x00000008 MP3 support.
MIX_INIT_OGG 0x00000010 OGG Vorbis support.
MIX_INIT_MID 0x00000020 MIDI support.
MIX_INIT_OPUS 0x00000040 Opus support.

Music Type Constants (Returned by Mix_GetMusicType)

Constant Value Description
MUS_NONE 0 No music loaded.
MUS_CMD 1 External command (rare).
MUS_WAV 2 WAV file.
MUS_MOD 3 MOD tracker file.
MUS_MID 4 MIDI file.
MUS_OGG 5 OGG Vorbis.
MUS_MP3 6 MP3.
MUS_FLAC 7 FLAC.
MUS_OPUS 8 Opus.

Fade Status Constants

Constant Value Description
MUS_NO_FADING 0 No fade in progress.
MUS_FADING_OUT 1 Fading out.
MUS_FADING_IN 2 Fading in.

Exception Handling

All methods in AudioLibrary raise exceptions under the following conditions:

Exception When Raised
FileNotFoundError The specified file does not exist.
RuntimeError SDL2 initialization fails, mixer initialization fails, audio file loading fails, playback fails.
ValueError Invalid AID, unsupported operation (e.g., seeking on a sound effect).
ImportError Unable to load SDL2 libraries (Windows/macOS download failure, Linux system library missing).

Example:

try:
    lib.play_from_file("nonexistent.mp3")
except FileNotFoundError as e:
    print(f"File not found: {e}")
except RuntimeError as e:
    print(f"Playback error: {e}")

End of API Reference

This document covers all public and internal APIs for the ap_ds library version v3.1.0/3.1.1. For additional examples and usage patterns, please refer to the main README.md.

Environment Variables

1. AP_DS_HIDE_SUPPORT_PROMPT

Purpose: Controls whether the startup banner is displayed when importing the library.

Default: Not set (banner displayed)

Behavior:

  • When set to 1, the startup message is completely suppressed.
  • Useful for GUI applications, daemons, or any environment where console output should be minimized.

Usage:

# Linux/macOS
export AP_DS_HIDE_SUPPORT_PROMPT=1

# Windows Command Prompt
set AP_DS_HIDE_SUPPORT_PROMPT=1

# Windows PowerShell
$env:AP_DS_HIDE_SUPPORT_PROMPT=1

Code Example:

import os
os.environ['AP_DS_HIDE_SUPPORT_PROMPT'] = '1'
import ap_ds  # No banner output

2. AP_DS_WAV_THRESHOLD

Purpose: Determines whether a WAV file is played as a sound effect (non-seekable) or as a music file (seekable, with fade support).

Default: 6 seconds

Behavior:

  • Files with duration less than the threshold: treated as sound effects (using Mix_PlayChannel). Seek operations are not supported.
  • Files with duration greater than or equal to the threshold: treated as music (using Mix_PlayMusic). Full seek and fade operations are supported.
  • If the threshold is set to 30 or higher, it is automatically reset to 6 to prevent potential memory issues.
  • Negative values are also reset to 6.
  • Invalid (non-numeric) values fall back to the default.

Why This Setting Exists:
SDL2_mixer has two different playback mechanisms: sound effects (channels) and music. Sound effects are lightweight and suitable for short clips but cannot seek or fade. Music tracks support these advanced features but consume slightly more resources. This threshold allows automatic selection of the appropriate mechanism based on file duration.

Usage:

# Set threshold to 10 seconds
export AP_DS_WAV_THRESHOLD=10

# Use a very low threshold (all WAVs become sound effects)
export AP_DS_WAV_THRESHOLD=0

# Use a high threshold (only very long WAVs become music)
export AP_DS_WAV_THRESHOLD=20

Validation Rules:

# Internal validation logic
if WAV_THRESHOLD >= 30:
    WAV_THRESHOLD = 6  # Prevent memory issues
elif WAV_THRESHOLD < 0:
    WAV_THRESHOLD = 6

3. AP_DS_SDL2_PATH and AP_DS_SDL2_MIXER_PATH (Linux Only)

Purpose: Specifies custom paths to the SDL2 and SDL2_mixer shared libraries on Linux systems. Used when system-installed libraries are not found or when users have compiled their own versions.

Default: Not set; libraries are searched in standard system paths (/usr/lib, /usr/local/lib, etc.) and package manager locations.

Behavior:

  • If both variables are set and the files exist, they are loaded immediately without further search.
  • After successful loading, the paths are automatically saved to ~/.config/ap_ds/sdl_paths.conf for future runs.
  • If the saved paths become invalid (files missing), the library falls back to the normal search flow.

Usage:

export AP_DS_SDL2_PATH=/usr/local/lib/libSDL2.so
export AP_DS_SDL2_MIXER_PATH=/usr/local/lib/libSDL2_mixer.so

Configuration File: After the first successful manual configuration, the paths are saved to:

~/.config/ap_ds/sdl_paths.conf

File contents:

SDL2_PATH=/path/to/libSDL2.so
SDL2_MIXER_PATH=/path/to/libSDL2_mixer.so

4. AP_DS_SUPPRESS_WARNINGS (New in v v3.1.0/3.1.1)

Purpose: Suppresses runtime downgrade warnings (e.g., GIL-enabled warnings).

Default: Not set (warnings displayed)

Behavior:

  • When set to 1, all runtime downgrade warnings are completely suppressed.
  • Useful for production environments or scenarios where you don't want to see warning output.

Usage:

export AP_DS_SUPPRESS_WARNINGS=1

5. AP_DS_SHOW_CONGRATS (New in v v3.1.0/3.1.1)

Purpose: Controls whether the congratulations message for full-performance mode is displayed.

Default: Not set (congratulations displayed)

Behavior:

  • When set to 0, hides the congratulations message for full-performance mode (GIL disabled).
  • Useful for quiet mode or headless environments.

Usage:

export AP_DS_SHOW_CONGRATS=0

6. AP_DS_SKIP_AUTO_CHECK (New in v v3.1.0/3.1.1)

Purpose: Controls whether the automatic runtime self-check on import is skipped.

Default: 0 (self-check executed)

Behavior:

  • When set to 1, skips the runtime self-check output on import.
  • Useful for test environments or reducing startup noise.

Usage:

export AP_DS_SKIP_AUTO_CHECK=1

v v3.1.0/3.1.1 – Detailed Release Overview

What is v v3.1.0/3.1.1?

v v3.1.0/3.1.1 is a major feature release of ap_ds (non-LTS), focusing on batch parsing performance and Python 3.15t free-threading support. It introduces a brand-new batch parsing API using ProcessPoolExecutor for true multi-core parallelism, and fully adapts to Python 3.15t's GIL-less environment.

Version Type: Feature release (non-LTS)
Support Period: Approximately 6 months
Target Users: Developers needing batch parsing, early adopters wanting to experience Python 3.15t features
Production Recommendation: Continue using v3.0.0 LTS, wait for v4.0.0 LTS

Major New Features

1. Batch Parsing API

API Description
batch_get_metadata() Batch parse audio files, returns full metadata list
batch_get_duration() Batch get audio durations, returns {path: duration}
batch_get_metadata_by_type() Filter batch parsing by format (e.g., parse only MP3)

These APIs use ProcessPoolExecutor for process-level parallelism, achieving true multi-core parallel acceleration in Python 3.15t (GIL-less) environments.

2. Performance Improvements

120 MP3 File Test Results:

Method Time Speedup
Serial parsing 1.367s 1.00x
8-process parallel 0.331s 4.13x

Compared to Mutagen:

Library Method 120 Files Time
Mutagen Single-threaded 0.973s
ap_ds v v3.1.0/3.1.1 8-process parallel 0.331s (2.94x faster)

3. Python 3.15t Free-Threading Support

  • True GIL-less parallelism: Batch parsing scales linearly on multi-core CPUs
  • Runtime self-check: Automatically detects GIL status on import and provides clear prompts
  • Full-performance users see 🎉 ap_ds: GIL disabled (free-threading mode)
  • Non-full-performance users see ⚠️ ap_ds: GIL is enabled (multi-core parallelism limited)

4. DAP Deduplication Optimization

_add_to_dap_recordings() upgraded from O(n) linear scan to O(1) set-based deduplication, delivering significant performance gains for large playlists. O(n) fallback mechanism is retained to ensure stability in edge cases.

5. Startup Acceleration: Lazy Import

Python 3.15+ users automatically benefit from lazy import – heavy modules (ctypes, urllib.request, struct, json, etc.) are loaded on demand, significantly speeding up import ap_ds.

6. New Environment Variables

Variable Default Description
AP_DS_SUPPRESS_WARNINGS Not set Set to 1 to suppress downgrade warnings
AP_DS_SHOW_CONGRATS Not set Set to 0 to hide full-performance congratulations
AP_DS_SKIP_AUTO_CHECK 0 Set to 1 to skip import-time self-check

7. Runtime Diagnostics

Two new standalone utility functions, usable without instantiation:

from ap_ds import is_full_performance, get_runtime_info

if is_full_performance():
    print("🚀 Full-performance mode!")

info = get_runtime_info()
print(info["python_version"], info["gil_enabled"])

Version Relationship

Version Type Support Period Use Case
v3.0.0 LTS Long-Term Support Until March 2031 Production environments
v v3.1.0/3.1.1 Feature Release ~6 months Early adopters / batch parsing needs
v4.0.0 LTS (planned) Long-Term Support TBD Based on Python 3.15 stable release

Upgrade Recommendations

User Type Recommendation
Production environment Continue using v3.0.0 LTS, wait for v4.0.0 LTS
Development/Testing Upgrade to v v3.1.0/3.1.1 to experience new features
Need batch parsing Must upgrade to v v3.1.0/3.1.1
Python 3.15t users Must upgrade to v v3.1.0/3.1.1 to leverage GIL-less advantages
pip install --upgrade ap_ds

Technical Architecture

Core Components

ap_ds/
├── __init__.py          # Package entry point, version import, banner display
├── player.py            # Main AudioLibrary class, SDL2 bindings, playback logic
├── audio_parser.py      # Metadata parser factory, unified API
├── audio_info.py        # Format-specific parsers (WAV, FLAC, MP3, OGG, AAC)
└── _version.py          # Auto-generated version file (created at build time)

1. Player Module (player.py)

Purpose: The core of the library. Manages SDL2 initialization, audio playback, caching, and DAP recording.

Key Classes & Functions:

  • AudioLibrary: The main class for user interaction
    • Initialization: __init__(frequency, format, channels, chunksize) – Sets up SDL audio and mixer
    • Playback: play_from_file(), play_from_memory(), new_aid()
    • Control: pause_audio(), play_audio(), stop_audio(), seek_audio()
    • Volume: set_volume(), get_volume()
    • Fade: fadein_music(), fadein_music_pos(), fadeout_music(), is_music_playing(), is_music_paused(), get_music_fading()
    • Metadata: get_audio_duration(), get_audio_metadata()
    • Batch Parsing (v v3.1.0/3.1.1): batch_get_metadata(), batch_get_duration(), batch_get_metadata_by_type()
    • DAP: save_dap_to_json(), get_dap_recordings(), clear_dap_recordings()

Internal Data Structures:

  • self._audio_cache: dict[str, Mix_Chunk] – Cached sound effects (short files)
  • self._music_cache: dict[str, Mix_Music] – Cached music tracks
  • self._channel_info: dict[int, dict] – Active playback sessions, keyed by SDL channel ID
  • self._aid_to_filepath: dict[int, str] – AID (Audio ID) to file path mapping
  • self._dap_recordings: list[dict] – DAP history records
  • self._dap_records_set: set[str] – O(1) deduplication set (v v3.1.0/3.1.1)

Platform Abstraction:

  • import_sdl2(): Intelligently loads SDL2 libraries on Windows, macOS, and Linux
  • download_sdl_libraries(): Downloads platform-specific binaries with hash verification
  • check_sdl_libraries_exist(): Verifies required files are present
  • load_sdl2_from_directory(): Loads libraries from package directory or system paths

SDL2 Bindings:

  • All SDL2 and SDL2_mixer functions are bound via ctypes
  • Function prototypes (argtypes, restype) are set for all platforms to prevent segfaults
  • Binding code is executed unconditionally to ensure cross-platform stability

2. Metadata Parser Module (audio_parser.py)

Purpose: Provides a unified interface for audio metadata extraction. Handles format detection and dispatches to the appropriate parser.

Key Functions:

  • get_audio_parser(): Singleton factory returning an AudioParser instance
  • AudioParser.get_audio_metadata(file_path): Returns a dictionary containing path, format, duration, length, sample_rate, channels, bitrate
  • AudioParser.batch_get_metadata(): Batch parsing (v v3.1.0/3.1.1)

Fallback Behavior: If a format-specific parser fails, returns None. No exceptions are raised during metadata parsing; errors are logged.

3. Format-Specific Parsers (audio_info.py)

Purpose: Low-level parsers for each supported format. Written in pure Python with no external library dependencies.

Format Parser Class Accuracy Method
WAV WAVFile 100% Reads RIFF chunks, extracts sample rate, channels, block align, and data size. Calculates exact duration from total frames.
FLAC FLACFile 100% Reads the STREAMINFO block (always present). Extracts sample rate, channels, and total samples from metadata.
MP3 MP3File >98% Frame-by-frame scan using MP3 frame headers. Accumulates sample count and estimates duration based on sample rate.
OGG OGGFile 99.99% Reads Ogg pages, extracts granular position (total samples) from the last page. Requires scanning the entire file but is highly accurate.
AAC (ADTS) AACFile >99% Frame-by-frame ADTS header parsing, accumulating sample count. Accurate for files with consistent frame sizes.

4. Batch Parsing Architecture (New in v v3.1.0/3.1.1)

Core Mechanism: Uses ProcessPoolExecutor for process-level parallelism. Each subprocess independently parses files, avoiding file handle contention.

Advantages:

  • Fully stable on Windows with free-threading builds
  • True multi-core parallelism, not limited by GIL
  • Process isolation eliminates resource contention

Workflow:

  1. User calls batch_get_metadata()
  2. If a directory is provided, recursively scans to collect all supported audio files
  3. Creates a ProcessPoolExecutor process pool
  4. Each file is submitted to a worker process
  5. Each worker process calls _parse_single_file() to parse a single file
  6. Collects and returns all successfully parsed metadata

5. DAP (Dvs Audio Playlist) System

Purpose: Automatically records playback history, storing only metadata so applications can build listening histories without storing audio data.

Workflow:

  1. User calls play_from_file() or play_from_memory()
  2. Method calls _add_to_dap_recordings(file_path)
  3. _add_to_dap_recordings() retrieves metadata via get_audio_metadata_by_path()
  4. Creates a record containing path, duration, bitrate, and channels
  5. Adds to _dap_recordings after checking for duplicates using O(1) set-based deduplication (v v3.1.0/3.1.1)
  6. User can call save_dap_to_json() at any time to persist the list to an .ap_ds-dap JSON file

Memory Characteristics:

  • Per record: ~150 bytes (path string + three integers)
  • 10,000 records: ~1.5 MB RAM, ~2-3 MB JSON file
  • No audio data is stored

6. SDL2 Integration & ctypes Bindings

Why ctypes?
ctypes is part of the Python standard library, so it adds no external dependencies. It allows direct calling of C functions from shared libraries (DLLs, dylibs, .so files). This is the most lightweight way to interface with SDL2.

Function Prototypes:
argtypes and restype are set for every SDL2 and SDL2_mixer function used. This is critical for cross-platform stability:

  • Without argtypes, Python may pass incorrect arguments, leading to segfaults
  • Without restype, Python may misinterpret return values (especially for pointer types)

7. Error Handling Philosophy

ap_ds follows a fail-fast, fail-clearly philosophy:

  • File not found: FileNotFoundError with the path included
  • Unsupported format: RuntimeError with a descriptive message
  • SDL2 initialization failure: RuntimeError with the SDL error string
  • Download failure: Exception with failure details (hash mismatch, network error, etc.)

Why not structured error codes?
Structured error codes (like -1 for failure, 0 for success) are common in C APIs but are not Pythonic in Python. Python exceptions are preferred because they force callers to explicitly handle errors and provide rich context.

Frequently Asked Questions

1. Which version should I use for production?

v3.0.0 LTS is the recommended version for all production deployments. It receives five years of security updates and critical bug fixes. No breaking changes will be introduced during the LTS period.

v v3.1.0/3.1.1 is a feature release (non-LTS) suitable for users who need batch parsing and Python 3.15t support.

2. Can I use ap_ds in commercial products?

Yes, absolutely. The license explicitly allows commercial use, including integration into commercial products, cloud services, and SaaS platforms, completely free of charge. You must comply with the attribution requirements.

3. Why is the library so small?

ap_ds focuses on playing and parsing the four most common formats, avoiding bloat from editing/transcoding features. It builds on the efficient SDL2 C library and uses only the Python standard library. Total size is 2.5MB on Windows and 3.36MB on macOS.

4. How accurate is MP3 duration parsing?

MP3 duration parsing is >98% accurate. This is due to the format's variable header complexity. WAV and FLAC are guaranteed 100% accurate, while OGG is 99.99%.

5. Does it work on embedded devices?

Yes! v3.0.0 LTS has been tested on Orange Pi 4 Pro and Raspberry Pi 5 (ARM64) running Ubuntu 22.04. Memory growth is minimal (~4MB after extensive testing). Audio output via 3.5mm works without modification.

6. What are the system requirements?

  • Windows: Windows 7+, Python 3.7+
  • macOS: macOS 10.9+, Python 3.7+
  • Linux: Modern distributions, Python 3.7+, SDL2 libraries (auto-installed via package manager where possible)
  • Embedded: ARM64 devices running Ubuntu 22.04 or similar

7. How do I suppress the startup banner?

Set the environment variable AP_DS_HIDE_SUPPORT_PROMPT=1 before importing the library:

import os
os.environ['AP_DS_HIDE_SUPPORT_PROMPT'] = '1'
import ap_ds

8. How do I configure the WAV threshold?

Set AP_DS_WAV_THRESHOLD to the desired number of seconds. Files shorter than this threshold will be treated as sound effects (no seek support), while files equal to or longer than the threshold will be treated as music (full seek and fade support).

9. Why does v v3.1.0/3.1.1 use multi-processing instead of multi-threading?

In Python 3.15t (GIL-less) environments, multi-threaded file I/O on Windows suffers from file handle contention issues, leading to BrokenProcessPool errors. Multi-processing (ProcessPoolExecutor) completely avoids this through process isolation, achieving true parallelism with 100% stability.

10. How many processes should I use for batch parsing?

We recommend setting max_workers to the number of CPU cores or slightly lower. For most systems, 4-8 processes is optimal. With fewer files, process startup overhead may outweigh parallelism benefits; with >100 files, the parallelism advantage is significant.

11. What is Free-Threading Python?

Free-Threading Python (filename with t) is a Python build that removes the GIL (Global Interpreter Lock). This means multiple threads can execute Python bytecode simultaneously, enabling true multi-core parallelism. Both Python 3.14t and 3.15t support this feature.

12. How do I verify I'm using the free-threading version?

Run the following command:

python3.14t -c "import sys; print(sys._is_gil_enabled())"

A False output means GIL is disabled (free-threading version).

13. How does v v3.1.0/3.1.1 compare to Mutagen?

Comparison Mutagen ap_ds v v3.1.0/3.1.1
Batch parsing ❌ No ✅ Yes
Multi-core parallelism ❌ Single-threaded ✅ 8-process parallel
120 files time 0.973s 0.331s
Python 3.15t support ❌ No ✅ Full support

ap_ds v v3.1.0/3.1.1 is 2.94x faster than Mutagen.

14. Will v3.0.0 LTS receive v v3.1.0/3.1.1's new features?

No. LTS releases receive only security updates and critical bug fixes, no new features. This is to ensure API stability and predictability for LTS users. New features will only be available in feature releases (v v3.1.0/3.1.1, v3.2.0, etc.).

15. When is the next LTS release?

v4.0.0 LTS is planned for release after Python 3.15's stable release (expected October 2026). Once Python 3.15 is stable and all free-threading-related issues are resolved, ap_ds will release a new LTS version based on it.

16. How do I get technical support?

Free support is available through:

Response times: Within 7 business days for standard inquiries, 48 hours for critical issues.

Version History

v v3.1.0/3.1.1 (July 2026) – Performance & Batch Parsing Edition

This is a major feature release of ap_ds (non-LTS), focusing on batch parsing performance and Python 3.15t free-threading support.

🚀 New Features

1. Batch Parsing API (Brand New)

API Description
batch_get_metadata() Batch parse audio files, returns full metadata list
batch_get_duration() Batch get audio durations, returns {path: duration}
batch_get_metadata_by_type() Filter batch parsing by format (e.g., parse only MP3)

2. Python 3.15t Free-Threading Support

  • Fully adapted for Python 3.15t (GIL-less) environments
  • Runtime self-check: Automatically detects GIL status on import
  • Full-performance users see 🎉 ap_ds: GIL disabled (free-threading mode)
  • Non-full-performance users see ⚠️ ap_ds: GIL is enabled (multi-core parallelism limited)

3. DAP Deduplication Optimization

_add_to_dap_recordings() upgraded from O(n) linear scan to O(1) set-based deduplication, delivering significant performance gains for large playlists. O(n) fallback mechanism is retained to ensure stability in edge cases.

4. Startup Acceleration: Lazy Import

Python 3.15+ users automatically benefit from lazy import – heavy modules (ctypes, urllib.request, struct, json, etc.) are loaded on demand, significantly speeding up import ap_ds.

5. Runtime Diagnostic Functions

Two new standalone utility functions, usable without instantiation:

  • is_full_performance() – Checks if running in full-performance mode (3.15t + profiling)
  • get_runtime_info() – Returns diagnostic info: Python version, GIL status, profiling status, etc.

6. Runtime Self-Check

Automatically executes a runtime self-check on import, printing environment information (Python version, GIL status, profiling availability, performance mode, CPU core count, etc.). Can be skipped with AP_DS_SKIP_AUTO_CHECK=1.

7. New Environment Variables

Variable Default Description
AP_DS_SUPPRESS_WARNINGS Not set Set to 1 to suppress downgrade warnings
AP_DS_SHOW_CONGRATS Not set Set to 0 to hide full-performance congratulations
AP_DS_SKIP_AUTO_CHECK 0 Set to 1 to skip import-time self-check

⚡ Performance Improvements

120 MP3 File Test Results:

Method Time Speedup
Serial parsing 1.367s 1.00x
8-process parallel 0.331s 4.13x

Compared to Mutagen:

Library Method 120 Files Time
Mutagen Single-threaded 0.973s
ap_ds v v3.1.0/3.1.1 8-process parallel 0.331s (2.94x faster)

Compared to v3.0.0:

Test v3.0.0 (with GIL) v v3.1.0/3.1.1 (without GIL) Conclusion
8-way 120 files 1.285s (0.66x) ❌ 0.331s (4.13x) 🚀 v3.1.0/3.1.1 is 3.88x faster
Best time 0.789s (2 threads) 0.331s (8 processes) 🚀 ** v3.1.0/3.1.1 is 2.38x faster**

🔧 Changes

Underlying Improvements:

  • Batch parsing uses ProcessPoolExecutor instead of ThreadPoolExecutor
  • Achieves true multi-core parallelism in Python 3.15t (GIL-less) environments
  • Completely removed profiling-related code (avoiding performance overhead)

New APIs:

  • AudioLibrary.batch_get_metadata()
  • AudioLibrary.batch_get_duration()
  • AudioLibrary.batch_get_metadata_by_type()
  • AudioLibrary.is_full_performance() → changed to top-level function
  • AudioLibrary.get_runtime_info() → changed to top-level function

API Changes:

  • No breaking changes – fully backward compatible with v3.0.0

Documentation Updates:

  • Added complete batch parsing API documentation
  • Added environment variable documentation
  • Added performance comparison data (vs Mutagen, vs v3.0.0)
  • Updated apds.top as the official primary repository

Dependency Changes:

  • No new external dependencies
  • Maintains zero Python external dependencies

🐛 Fixed Issues

  • Fixed ThreadPoolExecutor file handle contention causing crashes on Windows + Python 3.15t
  • Fixed ProcessPoolExecutor subprocess re-executing module-level code causing crashes (protected with if __name__ == "__main__")

📦 Version Relationship

Version Type Support Period
v3.0.0 LTS Long-Term Support Until March 2031
v v3.1.0/3.1.1 Feature Release ~6 months

🔗 Repository Changes

  • Official Primary Repository: https://apds.top (Permanent official home)
  • Primary Mirror: GitCode
  • China Mirror: Gitee
  • Deprecated: GitHub (dvs-web/ap_ds), GitLab (JiHu)

⬆️ Upgrade Command

pip install --upgrade ap_ds

⚠️ Note: v v3.1.0/3.1.1 is a feature release (non-LTS). For production environments, please continue using v3.0.0 LTS.


v3.0.0 LTS (March 22, 2026) – First Long-Term Support Release

This is ap_ds's first LTS release. After years of refinement, extensive real-world testing, and a thorough internal resource management refactor, this release is production-ready for mission-critical applications, enterprise deployments, and personal projects.

New Features:

  • Deterministic resource cleanup – Replaced unreliable __del__ finalizers with explicit exit-time handlers
  • Hash-verified downloads – Every downloaded SDL2 library is validated against hardcoded SHA-256 hashes before use
  • Full test coverage – Tested across all platforms with zero memory leaks
  • 5-year support period – Until March 22, 2031, with free technical support

No breaking changes – Fully backward compatible with v2.x.

pip install --upgrade ap_ds

v2.4.2 (March 22, 2026) – Development Mistake

This version was accidentally uploaded with a development-stage player.py file. While it technically works, it may contain subtle issues and is not recommended for use in any real project.

⚠️ This version was a development mistake and is intended only for curiosity – please do not use it in production.

v2.4.1 (March 1, 2026) – Documentation Update

Updated PyPI documentation to fully reflect v2.4.0's new features, including detailed API descriptions, usage examples, and environment variable documentation.

Changes:

  • Updated PyPI project description
  • Added detailed examples for all new fade functions
  • Documented AP_DS_HIDE_SUPPORT_PROMPT environment variable
  • Improved quick-start guide

Note: This release contains no code changes – only documentation improvements.

v2.4.0 (March 1, 2026) – Audio Effects & Engineering Improvements

Introducing professional audio transitions and important internal engineering upgrades.

🎵 New Audio Control Functions:

Function Description
fadein_music(aid, loops=-1, ms=0) Fades in music over the specified milliseconds
fadein_music_pos(aid, loops=-1, ms=0, position=0.0) Fades in music from a specified position
fadeout_music(ms=0) Fades out currently playing music
is_music_playing() Checks if music is currently playing
is_music_paused() Checks if music is paused
get_music_fading() Gets the current fade status

🧠 Engineering Improvements:

  • Cleaner startup banner, controllable via AP_DS_HIDE_SUPPORT_PROMPT
  • Centralized version management
  • Robust import system (dual-layer fallback)
  • Unified project URLs

No breaking changes – All existing code continues to work.

v2.3.6 (February 27, 2026) – Documentation Update

Updated PyPI documentation with detailed license information and version history, added more examples.

v2.3.5 (February 26, 2026) – Stability Optimization & Embedded Validation

Six-dimensional test coverage:

  1. Library loading & initialization
  2. Playback testing (MP3, FLAC, OGG, WAV)
  3. Seek testing
  4. Memory pressure & leak detection (~4MB growth)
  5. Metadata parsing accuracy
  6. DAP system validation

Embedded Platform Support:

  • Orange Pi 4 Pro (Allwinner A733)
  • Raspberry Pi 5 (BCM2712)

Bug Fixes:

  • Fixed WAV files being incorrectly treated as sound effects – configurable via AP_DS_WAV_THRESHOLD

v2.3.4 (February 10, 2026) – Linux Smart Import System

Revolutionary Linux support improvements with four-layer fallback strategy:

  1. System library check
  2. User configuration check
  3. Automatic package manager installation (apt-get, dnf, pacman)
  4. Interactive guidance

Automatic Configuration Saving:

  • Environment variables (AP_DS_SDL2_PATH, AP_DS_SDL2_MIXER_PATH)
  • Persistent configuration file (~/.config/ap_ds/sdl_paths.conf)

v2.3.3 (February 9, 2026) – Critical Bug Fix & Platform Stabilization

🚨 Critical Update: Fixed a severe segfault that caused the library to fail on macOS and Linux.

Root Cause: Earlier versions only defined C function prototypes (ctypes argtypes/restype) on Windows, leading to memory access violations on other operating systems.

Solution: All necessary C function bindings are now defined unconditionally after loading the SDL2 libraries.

v2.3.2 (February 9, 2026) – Linux Support Enhancement

Expanded Linux support with interactive setup.

Interactive Linux Support:

  1. Use system-installed libraries
  2. Specify compiled .so file path
  3. Get detailed compilation instructions

v2.3.1 (February 9, 2026) – Documentation Update

Improved README.md with better examples and explanations. Fixed minor errors in documentation examples.

v2.3.0 (January 31, 2026) – DAP Recording System

Introducing the DAP (Dvs Audio Playlist) system.

Core Features:

  • Intelligent auto-recording: Automatically triggered in play_from_file(), play_from_memory()
  • Lightweight design: Metadata only, no audio data
  • Standardized file format: .ap_ds-dap extension, JSON format
  • Intelligent deduplication: Automatically avoids duplicate records for the same file

New APIs:

  • _add_to_dap_recordings(file_path) – Internal use
  • save_dap_to_json(save_path) – Save as JSON
  • get_dap_recordings() – Get all records
  • clear_dap_recordings() – Clear records

v2.2.0 (January 19, 2026) – Cross-Platform Revolution

From single-platform to cross-platform.

Major New Features:

1. Full macOS Support

  • Automatic download and installation of SDL2.framework, SDL2_mixer.framework
  • Intelligent .dmg file extraction and framework loading
  • Maintains extreme lightness: only 3.36MB (vs Windows 2.5MB)

2. Enhanced Automatic Dependency Management

  • Cross-platform intelligent download strategy
  • Full error handling and retry mechanism
  • Local caching of dependency files

v2.1.4 (January 18, 2026) – Stable Release

Production-ready stable version.

  • Core stability: Extensively tested, no known critical bugs
  • Extremely lightweight: Only 2.5MB complete solution
  • Full documentation: Detailed technical manual and examples

v2.1.0 (December 26, 2025) – Feature Enhancement

Professional feature expansion.

New Features:

  • Metadata enhancement: More precise audio information parsing
  • Playback accuracy improvements: Better time control and seeking

v2.0.0 (November 5, 2025) – Architecture Refactor

Introducing the modern audio management system.

Major Improvements:

  • AID System: Unified audio instance management
  • Architecture Refactor: Modular design for improved maintainability
  • Smart Memory Management: Automatic cleanup of unused audio resources
  • State Management: Unified playback state tracking

v1.0.0 (July 8, 2025) – Initial Release

Project birth, foundational functionality.

Core Features:

  • Basic audio playback: MP3, WAV, FLAC, OGG formats
  • Playback controls: Play, pause, stop, seek basic API
  • Volume control: Real-time volume adjustment (0-100%)
  • Lightweight design: ~2MB initial release

License

This project is licensed under the DVS Audio Library (ap_ds) Open Source License Version 2.0. The full license text follows. By using, copying, modifying, or distributing this software, you accept all terms and conditions of this license.



DVS Audio Library (ap_ds) Open Source License Version 2.0

Version: 2.0 Effective Date: March 22, 2026 Applies to: ap_ds version 2.4.1 and above (except for subsequent license updates) Project Homepage: https://www.dvsyun.top/ap_ds | https://apds.top


1. Definitions

1.1. "Software" means the DVS Audio Library (ap_ds) project and all its components, source code, object code, and related documentation. The official name of this project is "ap_ds", and the following names are also granted as officially recognized brand identifiers:

  • AP_DS
  • Audio Library By DVS
  • DVS Audio Player (All of the above names are case-insensitive and are considered officially recognized brand names.)

1.2. "Source Code" means the human-readable form of the Software, which is the basis for modification, study, and distribution.

1.3. "Modified Version" means any derivative work created by modifying, supplementing, translating, or otherwise altering the Software, in whole or in part.

1.4. "Distribute" means making the Software or a Modified Version available to any third party by any means or medium.

1.5. "You" means any individual or legal entity exercising the rights granted under this License.

1.6. "Independent Brand" means a completely new project name, logo, and brand identity that has no confusing association with the official names of the Software (including but not limited to "ap_ds", "AP_DS", "Audio Library By DVS", "DVS Audio Player", and any variants thereof).


2. Grant of License

Subject to the terms and conditions of this License, the Author hereby grants You a perpetual, worldwide, royalty-free, non-exclusive, irrevocable right to:

2.1. Use and Run: Run the Software on any computer system for any lawful purpose.

2.2. Copy and Distribute: Make any number of copies of the Software and Distribute them.

2.3. Study and Modify: Study the Software's Source Code and make any modifications to meet Your needs.

2.4. Integrate and Commercially Use: Integrate the Software into Your products or projects, and use it in any commercial context, including but not limited to commercial product integration, cloud service deployment, selling solutions incorporating the Software, and internal corporate use.


3. Obligations and Restrictions

3.1. Attribution and Source Identification

Any time the Software or a Modified Version is used, Distributed, or integrated, You must:

a) Retain Original Copyright Notices: Keep intact all original copyright, patent, and trademark notices in all copies of the Software.

b) Provide Prominent Source Attribution: Clearly and conspicuously state the following information in the software documentation, official website, user interface, or related materials: Based on DVS Audio Library (ap_ds) v[version number] Original Author: Dvs (DvsXT) Project Homepage: https://www.dvsyun.top/ap_ds | https://apds.top

c) Add Notice for Modified Versions: If You Distribute a Modified Version, in addition to the attribution above, You must add the following notice: This is a modified version maintained by [Your Name/Organization]. Support: [Your Contact Information]. This version is not the official version and is not affiliated with the original author.

3.2. Brand Protection

To prevent brand confusion and project fragmentation, Modified Versions must comply with the following strict rules:

a) Prohibition on Using Original Brand Names: You must not name a Modified Version "ap_ds", "AP_DS", "Audio Library By DVS", "DVS Audio Player", or any variant, combination, or derivative that could cause confusion.

b) Requirement for Independent Brand: Modified Versions must use a completely independent project name and establish their own independent project identity, documentation, and community.

c) Maintainer Responsibility Statement: The distributor of a Modified Version must state prominently on their project homepage or in a conspicuous location: This project is based on DVS Audio Library (ap_ds) but has evolved independently and is fully maintained by [Your Name]. For the original version, please visit: https://www.dvsyun.top/ap_ds or https://apds.top. The maintainer is solely responsible for any issues related to this project.

3.3. Quality Commitment for Modified Versions

If You Distribute a Modified Version, You must:

a) Clearly State the Nature of Modifications: Clearly indicate that this is a modified version and list the key modifications and compatibility notes compared to the original version.

b) Provide Technical Support: Provide a valid means of technical support contact for the Modified Version You distribute, and define the scope of support.

c) Not Mislead Users: You must not imply in any way that Your Modified Version is officially endorsed, supported, or is a continuation of the original project.

3.4. Prohibited Uses

You must not use the Software for any illegal activities, malicious purposes, or actions that violate local laws or regulations, including but not limited to: a) Disrupting computer systems or network security. b) Distributing malware or viruses. c) Infringing on the intellectual property or privacy rights of others.


4. Patent Grant

4.1. Patent License: The Author hereby grants You a worldwide, royalty-free, non-exclusive, non-transferable patent license to make, use, sell, offer for sale, import, or otherwise transfer the Software.

4.2. Patent Defense Termination: If You or Your affiliates file a patent infringement lawsuit against the Author regarding the Software, all rights granted to You under this License will automatically and immediately terminate.


5. Technical Transparency and Security

5.1. Right to Security Review: Any user has the right to conduct a security audit of the Software's Source Code. Commercial users may engage third-party professionals for this purpose.

5.2. Security Reporting: Reporting discovered security issues to the original Author (me@dvsyun.top) is encouraged, and public disclosure after resolution is supported.

5.3. No Backdoors Commitment: The officially released version commits to containing no malicious code, backdoors, or user-data collection features without explicit user consent.


6. Disclaimer of Warranty and Limitation of Liability

6.1. Disclaimer of Warranty: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND ABSENCE OF ERRORS.

6.2. Limitation of Liability: TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES (INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA LOSS, OR BUSINESS INTERRUPTION) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE.


7. License Management and Termination

7.1. Version Control: This License is version 2.0. Subsequent versions will be published on the project homepage. You may choose to follow the terms of this version or any later version.

7.2. Compatibility: This License is compatible with the MIT, BSD 3-Clause, and Apache 2.0 licenses.

7.3. Automatic Termination: Your rights under this License will terminate automatically if You fail to comply with its terms. However, if You cease all non-compliance and cure all violations within 30 days of receiving notice from the copyright holder, and the copyright holder has not terminated Your rights within that period, Your rights will be reinstated.


8. Governing Law and Dispute Resolution

8.1. Governing Law: This License shall be governed by the laws of the People's Republic of China, without regard to its conflict of law provisions.

8.2. Dispute Resolution: Any dispute arising out of or in connection with this License shall first be resolved through friendly negotiation. If negotiation fails, either party may submit the dispute to the competent people's court located in the project author's domicile.


9. Contact Information

9.1. Licensing and Inquiries:

9.2. Technical Support:

  • Priority should be given to submitting issues via GitCode Issues.
  • Urgent matters can be directed to the emails above.

BY USING, COPYING, MODIFYING, OR DISTRIBUTING THE SOFTWARE, YOU ACCEPT ALL TERMS AND CONDITIONS OF THIS LICENSE.



Final Note

ap_ds is built on a simple philosophy: focus on playback and parsing, stay lightweight, and let developers build great applications.

We welcome feedback, bug reports, and contributions. If you have questions or concerns, please contact us through the official channels.

Thank you for using ap_ds!

Release files for ap-ds 3.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ap-ds 3.1.1
File Size Uploaded
ap_ds-3.1.1.tar.gz 140.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ap-ds 3.1.1
File Interpreter ABI Platform
ap_ds-3.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 218.5 kB

Release files / ap_ds-3.1.1.tar.gz

Download URL ap_ds-3.1.1.tar.gz
Size 140.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2dd0d0053a7266ccefd6d99df3f7fc31fcf942db29794b94947e7abbbff4f471
BLAKE2b-256 checksum
How to use checksums
e13823a62ce74aaae54041d17f279794f13172d587207c26b124e15ff5339d41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.4

Release files / ap_ds-3.1.1-py3-none-any.whl

Download URL ap_ds-3.1.1-py3-none-any.whl
Size 77.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f1f7c98489283236b02a3d3bf18bc089b071ed19d0f3f09f38e968020e5d0ebb
BLAKE2b-256 checksum
How to use checksums
d2e85b2f935ed76dc6a0f96e671b8c24600dfbe9899ff73373466a4039aac59e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.4

Release history Release notifications | RSS feed

4.0.1

2 release files

4.0.0

2 release files

3.1.2

2 release files

This release

3.1.1 This release

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.4.2

2 release files

2.4.1

2 release files

2.4.0

2 release files

2.3.6

2 release files

2.3.5

2 release files

2.3.4

2 release files

2.3.3

2 release files

2.3.2

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.0

2 release files

1.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page