Metadata-Version: 2.4
Name: ap_ds
Version: 4.1.0rc1
Summary: Audio Player By DVS - Advanced audio processing and playback
Home-page: https://apds.top
Author: DVS
Author-email: me@dvsyun.top
License: Custom Open Source License
Project-URL: Documentation, https://apds.top
Project-URL: License Info, https://apds.top
Keywords: audio music player playback sdl2 dvs
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE.md
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: requires-python
Dynamic: summary

# 🎉 ap_ds 4.1.0 RC Prerelease — Opus Support, But Please Help Us Test!

> **⚠️ Important: 4.1.0 is an RC (Release Candidate) prerelease version for collecting feedback and bug reports.**
>
> **Opus support currently exists in ONLY this one mainline version (4.1.0). Future mainline versions (4.2.0+) will REMOVE Opus support. Opus will migrate to the AFS branch (ap-ds-afs) for independent development.**
>
> **If you find any issues, please contact us immediately:**
> - 📧 Primary email: me@dvsyun.top
> - 📧 Backup email: dvs6666@163.com
> - ⏱️ We promise: **Fix within 3 business days**
>
> **Your feedback is crucial to us!** Let's polish Opus support to perfection together.


> **"We're not just supporting a new format — we're opening a door to higher quality, smaller size, and a freer future."**
> — DVS Development Team, August 19, 2026


# ap_ds: Lightweight Python Audio Library

**Current Version: v4.1.0 RC – Opus Support Preview**

**Release Date: August 19, 2026**

**Version Status: 🚀 Release Candidate — Testing Feedback Welcome**


## 📢 To All Audio Developers

Friends, colleagues, music lovers, and all who have ever wrestled with audio formats late into the night:

**Today, we announce — ap_ds 4.1.0 RC is officially released, supporting the Opus audio format in the mainline branch for the very first time!**

But before the excitement takes over, we must be honest with you:

**This is an RC (Release Candidate) prerelease version.**

What does that mean? It means:

- ✅ All core features are implemented and tested
- ✅ 229 Opus-specific tests all pass
- ✅ Cross-platform (Windows/Linux/macOS) playback verified
- ⚠️ There may still be edge-case bugs we haven't discovered
- ⚠️ We need real users to verify it in different environments

**So we're handing this version to you — our users — to help us test.**

> **"4.1.0 is the only appearance of Opus in the mainline. After this, it will move to the AFS branch."**

This is part of ap_ds's dual-track strategy. We'll explain in detail in the following sections.


## 🚨 Important Notes on 4.1.0's Version Positioning

### 1. This Is an RC Prerelease

4.1.0 is **NOT** an LTS (Long-Term Support) version, nor is it the final stable release. It is a **Release Candidate**, intended for:

- Collecting real-world usage feedback
- Discovering bugs that we couldn't cover in testing
- Verifying cross-platform compatibility
- Gathering data for the final release

### 2. Opus Support Exists ONLY in 4.1.0 Mainline

**This is a very important statement:**

> **Opus support currently exists in ONLY version 4.1.0 of the ap_ds mainline branch.**
>
> **Future mainline versions (4.2.0, 4.3.0, etc.) will REMOVE Opus support.**
>
> **Opus and all future new formats will migrate to the AFS branch (ap-ds-afs), developing independently with 1.x version numbers.**

Here's what this means:

| Version | Opus Included? | Description |
|---------|---------------|-------------|
| **4.1.0 RC** | ✅ **Yes** | The ONLY mainline version with Opus |
| **4.2.0+ (mainline)** | ❌ **No** | Opus removed, back to lightweight positioning |
| **ap-ds-afs 1.0.0+** | ✅ **Yes** | Permanent home for Opus and all new formats |

**Why?**

Because ap_ds mainline's core promise is **2.5MB lightweight**. Opus support (including DLLs) increased the size to 3.87MB. 4.2.0 will directly remove Opus, returning to 2.5MB — more thorough than any optimization.

**So, if you need Opus support:**

- Short-term testing: Use 4.1.0 RC
- Long-term use: Switch to the AFS branch (`pip install ap-ds-afs`)

Both packages use the exact same import method (`from ap_ds import AudioLibrary`), so migration cost is zero.

### 3. Issue Reporting and Fix Commitment

**If you discover any issues while using 4.1.0 RC:**

1. **Contact the author immediately:**
   - Primary email: me@dvsyun.top
   - Backup email: dvs6666@163.com
   - Please CC both emails to ensure delivery

2. **We promise:**
   - **Fix within 3 business days**
   - Patch version (4.1.1) will be released as soon as possible

3. **When reporting, please provide:**
   - Operating system and version
   - Python version (`python --version`)
   - Full error message (if any)
   - Steps to reproduce
   - Audio file sample (if possible)

**Every piece of feedback helps us build a better ap_ds.** 🙏


## 🤔 Why Opus? — The Inevitability of a Technical Choice

### 1. The "Ceiling" of Quality and Compression

Opus is an open audio codec jointly developed by the **Xiph.Org Foundation** and **IETF**, combining **SILK** (for speech) and **CELT** (for general audio) algorithms, with adaptive bitrates from **6 kbps to 510 kbps**. This means:

- **At low bitrates (< 32 kbps)**, Opus voice clarity far exceeds MP3 and AAC
- **At medium-high bitrates (64–128 kbps)**, Opus quality rivals or even surpasses MP3 at 320kbps
- **Ultra-low latency (as low as 5ms)**, ideal for real-time communication and gaming

### 2. Open Source and Freedom — A Philosophical Fit

Opus uses a **BSD-style license** with no patent restrictions, completely free. This aligns perfectly with ap_ds's commitment to **openness, freedom, and zero burden**.

### 3. Mature Ecosystem and Clear Demand

Today, Opus is widely used in:

- **WebRTC** (real-time audio/video communication)
- **Discord, WhatsApp, Signal** and other instant messaging apps
- **Game engines** (Unity, Unreal both support Opus)
- **Audio streaming** (Radio, Podcasts)
- **Embedded devices** (low power, high compression)

As more audio content is published in Opus format, ap_ds as a general-purpose audio library must respond to this trend.

### 4. Paving the Way for the AFS Branch

Opus is the first member of the AFS (All-Format Support) branch. Through the 4.1.0 RC practice, we will validate the technical solution for cross-platform Opus playback, accumulating experience for the official release of the AFS branch.


## 😅 Why Wasn't Opus Supported Before? — The Story of Upstream Dependencies

That's a great question. To be honest, **we always wanted to support Opus**, since ap_ds 1.0. But the reality was:

**ap_ds's audio playback has always been built on SDL2 and SDL2_mixer.**

SDL2 is an excellent cross-platform multimedia library that handles low-level details like audio device abstraction, mixing, and buffering across Windows, macOS, and Linux. SDL2_mixer provides out-of-the-box support for MP3, WAV, OGG, FLAC, and other formats.

**But here's the problem — SDL2_mixer's Opus support has never been stable enough.**

Specifically:

| Issue | Description |
|-------|-------------|
| **Windows** | Official SDL2_mixer Windows binaries often omit Opus support or have incomplete compilation flags, causing `Mix_LoadMUS` to return NULL for `.opus` files |
| **macOS** | SDL2_mixer's Framework builds often have version mismatches with `libopusfile` and `libogg` dependencies, leading to runtime crashes |
| **Linux** | SDL2_mixer depends on system-installed `libopusfile-dev`, but package names and versions vary wildly across distributions |
| **API Inconsistency** | Even when Opus loads, SDL2_mixer's metadata extraction (duration, bitrate, etc.) often returns 0 or incorrect values |

**We tried various approaches:**

1. Compiling our own Opus-enabled SDL2_mixer binaries → size ballooned, maintenance cost too high
2. Forcing users to install `libopusfile-dev` on Linux → poor user experience, and Windows/macOS couldn't be solved
3. Waiting for upstream SDL2_mixer fixes → waited through multiple versions, issues persist

**Conclusion: SDL2_mixer's upstream support was insufficient for us to provide stable Opus playback.**

So, before 4.1.0, we made a difficult decision — **to temporarily not provide Opus support**, to avoid giving users an unstable experience.

### How Did 4.1.0 Solve It?

Since SDL2 wasn't working, we decided **not to use SDL2 at all**!

In 4.1.0, we completely bypass SDL2 and SDL2_mixer, building a dedicated playback pipeline for Opus:

```
User passes .opus file
       ↓
  Detects .opus extension
       ↓
  libopusfile decodes
       ↓
  Cross-platform audio output engine
   ├── Windows → winmm waveOut
   ├── Linux   → ALSA (libasound.so)
   └── macOS   → Core Audio AudioQueue
       ↓
  Audio output to speakers
```

This pipeline is completely independent of SDL2,不受 any limitations from SDL2_mixer.

**Meanwhile, Windows users don't need to manually find DLLs** — ap_ds will automatically download the required four DLLs (`libopusfile-0.dll`, `libopus-0.dll`, `libogg-0.dll`, `libopusurl-0.dll`) from `https://dvsyun.top/ap_ds/download/` on first run, with SHA256 hash verification to ensure file integrity.

**This is why 4.1.0 can support Opus, and why it couldn't before.** It's not that we didn't want to — it's that we had to find a solution that was reliable, stable, and truly cross-platform. Now, we have.


## 🧬 4.1.0 Technical Deep Dive — What Did We Actually Do?

### 1. New Modules and Architecture Refactoring

To integrate Opus support without affecting mainline stability, we carefully designed the project structure:

| File | Responsibility | Description |
|------|----------------|-------------|
| `opusplayer.py` | **Opus playback core** | Contains `OpusAudio` class, encapsulating all Opus playback, control, metadata, and batch processing APIs, completely independent of the SDL2 engine |
| `_opusdll.py` | **Opus library loader** | Handles cross-platform loading of `libopusfile` (Windows auto-download, Linux/macOS system detection + install guidance), provides unified `opusfile` handle |
| `player.py` (modified) | **Opus routing integration** | Auto-detects `.opus` files in `AudioLibrary` and forwards them to the `OpusAudio` sub-player, enabling completely transparent Opus playback |
| `__init__.py` (modified) | **Exports OpusAudio** | Users can directly `from ap_ds import OpusAudio` for standalone player, or use seamlessly via `AudioLibrary` |

**Key Design: AID 1:1 Mapping**

When a user plays via `AudioLibrary.play_from_file("song.opus")`, the library internally creates an `OpusAudio` instance and generates a sub-AID, then uses a dictionary `_aid_to_opus_aid` to map the main library AID to the sub-library AID. This way, all subsequent controls (pause, resume, volume, seek) are correctly routed to the Opus engine, while user code remains completely unaware of this forwarding logic.

```python
# User code — exactly the same as before!
from ap_ds import AudioLibrary

lib = AudioLibrary()
aid = lib.play_from_file("song.opus")  # Auto-routed to Opus engine
lib.pause_audio(aid)                   # Auto-routed to Opus engine
lib.seek_audio(aid, 30.0)              # Auto-routed to Opus engine
lib.stop_audio(aid)                    # Auto-routed to Opus engine
```

**Completely transparent, zero learning curve.**

### 2. Cross-Platform Playback Backends — Three Platform Engines Rewritten for Opus

Opus playback cannot depend on SDL2 (as detailed in the previous section), so we decided to **bypass SDL2 entirely and use native OS audio APIs** directly, paired with `libopusfile` decoding.

| Platform | Playback Solution | Tech Stack |
|----------|-------------------|------------|
| **Windows** | `winmm waveOut` | `libopusfile` decode → `waveOutWrite` multi-buffer output (4 buffers, 50ms/block) |
| **Linux** | **ALSA** | `libasound.so.2` → `snd_pcm_open` → `snd_pcm_writei` (direct PCM output) |
| **macOS** | **Core Audio AudioQueue** | Apple official C API → callback fills `AudioQueueBuffer` (consistent with official examples) |

**Platform Implementation Details:**

**Windows (`_play_worker_windows`):**
- Uses `waveOutOpen` to open the default audio device
- Uses `CreateEventW` + `WaitForSingleObject` to synchronize buffer completion events
- 4 buffers rotating to eliminate stuttering
- `WAVEHDR` struct uses `c_void_p` for 64-bit compatibility

**Linux (`_play_worker_linux`):**
- Uses `snd_pcm_open` to open `"default"` device
- Uses `snd_pcm_set_params` to set PCM parameters (S16_LE, interleaved mode)
- Uses `snd_pcm_writei` to write PCM data
- On `-EPIPE` (buffer underrun), calls `snd_pcm_recover` for automatic recovery

**macOS (`_play_worker_macos`):**
- Uses `AudioQueueNewOutput` to create output queue
- Uses `AudioQueueAllocateBuffer` to allocate buffers
- Callback `HandleOutputBuffer` decodes Opus and fills `mAudioData`
- Uses `AudioQueueStart` to start playback, `AudioQueueStop` to stop

This means that regardless of the user's operating system, Opus playback achieves native-level performance and stability.

### 3. Opus-Specific Error Code System (2001-2010)

To make Opus-related errors clearer and more traceable, we added 10 dedicated error codes:

| Code | Constant | Meaning | Suggested Action |
|------|----------|---------|------------------|
| 2001 | `AP_DS_ERR_OPUS_LIB_LOAD_FAILED` | `libopusfile` load failed | Check if DLL exists or is locked |
| 2002 | `AP_DS_ERR_OPUS_DLL_DEPENDENCY` | DLL dependency missing | Ensure `libopus-0.dll` and `libogg-0.dll` exist |
| 2003 | `AP_DS_ERR_OPUS_OPEN_FAILED` | Opus file open failed | File may be corrupted or not a valid Opus stream |
| 2004 | `AP_DS_ERR_OPUS_HEADER_CORRUPT` | OpusHead header corrupted | Header info invalid or corrupted |
| 2005 | `AP_DS_ERR_OPUS_TAGS_PARSE_FAILED` | Tags parse failed | Tag data corrupted or invalid format |
| 2006 | `AP_DS_ERR_OPUS_DECODE_FAILED` | Opus decode failed | Audio data corrupted |
| 2007 | `AP_DS_ERR_OPUS_SEEK_FAILED` | Opus seek failed | Stream may not support seeking to that position |
| 2008 | `AP_DS_ERR_OPUS_BITRATE_UNAVAILABLE` | Bitrate unavailable | Unable to determine bitrate for this Opus stream |
| 2009 | `AP_DS_ERR_OPUS_NOT_SEEKABLE` | Stream not seekable | This Opus stream does not support seeking |
| 2010 | `AP_DS_ERR_OPUS_CHANNEL_INVALID` | Invalid channel count | Opus stream has invalid channel count |

All Opus errors include:
- **Machine-readable error codes** (for programmatic handling)
- **Human-readable error messages** (for developer understanding)
- **Actionable suggestions** (for user problem-solving)

### 4. Automatic DLL Download and Hash Verification (Windows)

Windows users don't need to manually find DLLs. When ap_ds first detects that Opus support is needed, it will:

1. Check if the required four DLLs exist in the package directory
2. If missing or hash verification fails, download from `https://dvsyun.top/ap_ds/download/`
3. After download, perform SHA256 hash verification to ensure file integrity
4. If hash mismatches, automatically retry

**DLL File List and Hashes:**

| Filename | Size | SHA256 |
|----------|------|--------|
| `libopusfile-0.dll` | 55,884 bytes | `fc8ff75c5e0180e73b0528dc78c51ed0fb493741375cdc227f50c2a33cabf727` |
| `libopus-0.dll` | 500,112 bytes | `90aa25a0a6525d7da48a7ae8dd3306e45b0c28ce09a73d2a02b56cd95418d5be` |
| `libogg-0.dll` | 40,580 bytes | `3038ce8d161324a6349bf7c83b78493857ff6a3501e3adb3d541c6a07bd94a57` |
| `libopusurl-0.dll` | 76,772 bytes | `a6cde968a23f2d0067332a13718c52e265653a2c35d65862e8dff4cf2a0346d9` |

### 5. Cross-Platform Opus Library Loading Explained — Why Can't macOS Auto-Download Like SDL2?

#### Background: How Does SDL2's Auto-Download Work?

In ap_ds, the SDL2 loader (`_sdl2.py`) supports automatic download on Windows and macOS:

- **Windows**: Downloads `SDL2.dll` and `SDL2_mixer.dll` from CDN
- **macOS**: Downloads `SDL2.dmg` and `SDL2_mixer.dmg` from CDN, automatically mounts the DMG, extracts `SDL2.framework` and `SDL2_mixer.framework` to the package directory

This mechanism works well because **SDL2 officially provides precompiled macOS Framework installers** that can be directly downloaded, mounted, and extracted.

#### So Why Can't Opus Follow the Same Path?

**Because Opus officially does not provide macOS precompiled Framework packages.**

SDL2 has an official macOS download page with `.dmg` files containing complete `.framework` directory structures. But the Opus ecosystem (Xiph.Org Foundation) only provides source tarballs (`.tar.gz`), **no precompiled macOS binaries whatsoever**.

We searched carefully:

- Xiph.Org official website → source only
- Opus Codec official website → source only
- libopusfile official website → source only
- Major open-source mirror sites → source only
- Even third-party maintained precompiled Frameworks — no reliable, verifiable source found

**This is not an oversight on ap_ds's part; it's an objective gap in the Opus ecosystem.**

#### What About macOS Users Then?

In 4.1.0, we designed a **four-layer fallback mechanism** for macOS users:

```
Layer 1: Detect system-installed Opus libraries
   ├── ctypes.util.find_library("opusfile")
   ├── /opt/homebrew/lib/libopusfile.dylib (Apple Silicon Homebrew)
   ├── /usr/local/lib/libopusfile.dylib (Intel Homebrew)
   └── /opt/local/lib/libopusfile.dylib (MacPorts)
         ↓ If not found
Layer 2: Attempt MacPorts auto-installation
   └── sudo port install opus opusfile libogg
         ↓ If MacPorts doesn't exist or install fails
Layer 3: Attempt Homebrew auto-installation
   └── brew install opus opusfile libogg
         ↓ If Homebrew doesn't exist or install fails
Layer 4: Show manual installation guide (with complete commands and steps)
```

**In other words: ap_ds will do everything it can to automatically install Opus for the user, and only when all automatic options fail will it guide the user to manual operations.**

#### What About Linux? — Consistent with SDL2

Linux Opus loading strategy is fully consistent with SDL2:

```
Layer 1: User config file (~/.config/ap_ds/opus_paths.conf)
Layer 2: System libraries (ctypes.util.find_library("opusfile"))
Layer 3: Auto-installation (apt-get / dnf / pacman, interactive sudo password)
Layer 4: Interactive setup (guide user to manually specify .so path)
```

**Linux users will feel no surprises**, as this flow is almost identical to `_sdl2.py`'s Linux loading logic.

#### Why Do macOS Users See an "Apology" Rather Than a "Normal Prompt"?

Because macOS users are accustomed to SDL2's smooth "auto-download → extract → ready-to-use" experience. When they see that Opus requires extra installation steps, their first reaction may be "has ap_ds regressed?"

So in `_macos_apology()`, we candidly state:

> "We sincerely apologize. On macOS, SDL2 libraries can be downloaded automatically, but we could NOT find any precompiled Opus framework packages for macOS. This is a limitation of the Opus ecosystem, not of ap_ds."

**This is honesty, not excuse-making.** We don't want users to mistakenly think we "got lazy" and didn't implement auto-download — we're telling the truth: **Opus officially provides no macOS precompiled packages; we cannot download something that doesn't exist.**

#### Technical Comparison Summary

| Dimension | SDL2 (Windows/macOS) | Opus (Windows) | Opus (macOS) | Opus (Linux) |
|-----------|----------------------|----------------|--------------|--------------|
| **Official precompiled packages** | ✅ Yes (.dll / .dmg) | ✅ Yes (.dll) | ❌ **None** | ❌ None (but system repos have them) |
| **Auto-download solution** | ✅ Direct download | ✅ Direct download | ❌ **Cannot download** | ❌ N/A (uses system libs) |
| **Auto-installation solution** | ❌ N/A | ❌ N/A | ✅ MacPorts/Homebrew | ✅ apt/dnf/pacman |
| **User experience** | Out-of-box | Out-of-box | Extra steps (auto + guided) | Extra steps (auto + guided) |

#### What Have We Done to Compensate?

1. **Prioritize auto-installation**: On macOS, we try MacPorts first, then Homebrew, minimizing manual steps
2. **Clear error messages**: If auto-install fails, we provide complete, copy-paste-ready commands
3. **Humble and honest**: Directly state this is a limitation of the Opus ecosystem, not ap_ds's fault
4. **SDL2-consistent Linux experience**: Linux users see no difference

#### Quick Guide for macOS Users

If you're using ap_ds 4.1.0 to play Opus files on macOS, the easiest approach is:

```bash
# Using Homebrew (recommended)
brew install opus opusfile libogg

# Or using MacPorts
sudo port install opus opusfile libogg
```

After installation, ap_ds will automatically detect the system libraries without any extra configuration.

**We understand this isn't as convenient as "download and use," but until Opus officially provides macOS precompiled packages, this is the best solution available.**

### 6. OpusAudio Class — Complete API

The `OpusAudio` class provides an API almost identical to `AudioLibrary`, but entirely based on `libopusfile` and native audio output:

| Method | Function |
|--------|----------|
| `play_from_file(file_path, loops=0, start_pos=0.0)` | Play Opus file |
| `play_from_memory(file_path, loops=0, start_pos=0.0)` | Play from cache |
| `new_aid(file_path)` | Preload Opus file |
| `play_audio(aid)` | Resume playback |
| `pause_audio(aid)` | Pause playback |
| `stop_audio(aid)` | Stop playback, return elapsed time |
| `seek_audio(aid, position)` | Seek to position (seconds) |
| `set_volume(aid, volume)` | Set volume (0-128) |
| `get_volume(aid)` | Get volume |
| `fadein_music(aid, loops=-1, ms=0)` | Fade in playback |
| `fadein_music_pos(aid, loops=-1, ms=0, position=0.0)` | Fade in from position |
| `fadeout_music(ms=0)` | Fade out and stop |
| `is_music_playing()` | Whether currently playing |
| `is_music_paused()` | Whether currently paused |
| `get_music_fading()` | Get fade in/out status |
| `get_audio_metadata(file_path)` | Get Opus metadata |
| `get_audio_duration(file_path)` | Get Opus duration |
| `get_audio_extended_metadata(file_path)` | Get extended tags (title/artist/album etc.) |
| `batch_get_metadata(file_paths, max_workers=None)` | Batch parse Opus metadata |
| `batch_get_duration(file_paths, max_workers=None)` | Batch get Opus durations |
| `cleanup_function()` | Release all resources |

### 7. `_opusdll.py` — Cross-Platform Library Loader

Following the same design pattern as `_sdl2.py`, `_opusdll.py` provides:

**Windows Loading Flow:**
1. Check DLL files in package directory
2. If missing → auto-download
3. If exists but hash verification fails → re-download
4. Load into `ctypes.CDLL` and set function prototypes

**Linux Loading Flow:**
1. Check user config (`~/.config/ap_ds/opus_paths.conf`)
2. Check system libraries (`ctypes.util.find_library("opusfile")`)
3. Auto-installation (`apt-get`/`dnf`/`pacman`, interactive sudo password)
4. Interactive setup (guide user to manually specify `.so` path)

**macOS Loading Flow:**
1. Detect system libraries (`find_library` + common Homebrew/MacPorts paths)
2. Attempt MacPorts installation (`sudo port install opus opusfile libogg`)
3. Attempt Homebrew installation (`brew install opus opusfile libogg`)
4. Manual installation guidance

### 8. Library Size Changes — 4.1.0's "Temporary Weight Gain" and 4.2.0's "Slim-Down Return"

Due to the addition of `opusplayer.py` (~45KB) and `_opusdll.py` (~25KB), plus Windows DLLs (total ~673KB), **this 4.1.0 RC release temporarily grows to 3.87 MB (4,059,251 bytes).**

**But — please note — this size increase is "one-time," existing ONLY in this single 4.1.0 release.**

Why?

Because our strategy is very clear:

| Version | Opus Support | Size | Description |
|---------|--------------|------|-------------|
| **4.0.x** | ❌ | ~2.5MB | Stable release, no Opus |
| **4.1.0 RC** | ✅ | ~3.87MB | **Only mainline with Opus**, for feedback collection |
| **4.2.0+ (mainline)** | ❌ | **~2.5MB** | **Remove Opus, return to lightweight** |
| **ap-ds-afs 1.0.0+** | ✅ | ~2.8-3.5MB | Opus's permanent home |

**That is to say:**

- 4.1.0 RC's size increase is **temporary, intentional, and one-release-only**
- 4.2.0 mainline will **completely remove Opus code and DLLs**, naturally returning to 2.5MB
- All users needing Opus should switch to the **AFS branch (ap-ds-afs)**

**Our goal remains unchanged: keep ap_ds mainline extremely lightweight. 2.5MB is our brand promise, and we won't abandon it.**

No need for 4.1.1 to "optimize size" — because 4.2.0 will simply cut Opus out directly, more thoroughly than any optimization.


---

## ⚠️ IMPORTANT: Windows Multiprocessing Warning

### If you're using ap_ds 4.1.0 RC on Windows…

**Please read this carefully. Your program's ability to run depends on it.**

---

### What's the problem?

On Windows, Python's `multiprocessing` module uses the `spawn` method to create new processes. This means each child process will **re-import your main module**.

If you call `batch_get_metadata()` or any other batch parsing function that uses `ProcessPoolExecutor` directly at the top level of your script, child processes will **execute those calls again** when re-importing the main module, causing **infinite recursion** and ultimately a `BrokenProcessPool` error.

**Your program will crash. Directly.**

---

### Which APIs are affected?

All batch parsing functions that use `ProcessPoolExecutor`:

- `batch_get_metadata()`
- `batch_get_duration()`
- `batch_get_metadata_by_type()`

**The new Opus batch parsing in 4.1.0 RC is affected as well.**

---

### How to fix it?

**It's simple — wrap your batch parsing code inside `if __name__ == "__main__":`.**

#### ❌ Wrong (Crashes on Windows):

```python
from ap_ds import batch_get_metadata

# This will crash directly on Windows!
results = batch_get_metadata("/music/", max_workers=4)
print(f"Parsed {len(results)} files")
```

#### ✅ Correct:

```python
from ap_ds import batch_get_metadata

def main():
    results = batch_get_metadata("/music/", max_workers=4)
    print(f"Parsed {len(results)} files")

if __name__ == "__main__":
    main()
```

#### ✅ Works with config functions too:

```python
from ap_ds import batch_get_metadata

def load_config():
    return {"audio_dir": "/music/"}

def main():
    config = load_config()
    results = batch_get_metadata(config["audio_dir"], max_workers=4)
    print(f"Parsed {len(results)} files")

if __name__ == "__main__":
    main()
```

#### ✅ Jupyter Notebook Users:

Put the batch call inside a function, then execute it in a cell:

```python
def run_batch():
    from ap_ds import batch_get_metadata
    return batch_get_metadata("/music/", max_workers=4)

results = run_batch()
```

---

### Why don't Linux and macOS have this problem?

Linux and macOS use `fork` by default to create child processes, which **copy** the parent process's memory space without re-executing the main module code.

**However, we still recommend using `if __name__ == "__main__"` entry point protection on all platforms.** It's good programming practice and ensures cross-platform compatibility.

---

### Frequently Asked Questions

**Q: I only called `batch_get_metadata()` once in my script. Why does it recurse?**

A: Because on Windows, each child process re-imports your script. Without `if __name__ == "__main__"` protection, the import itself executes the top-level code again, causing infinite recursion.

**Q: What should I set `max_workers` to?**

A: We recommend `os.cpu_count()` or `None` (auto). On Windows, `spawn` process overhead is relatively high, so it's not recommended to exceed the number of CPU cores.

**Q: Does this issue exist in 4.0.x as well?**

A: Yes. All `batch_*` functions in all versions have the same issue on Windows. This is an inherent limitation of Python's multiprocessing on Windows, not a bug in ap_ds.

**Q: Can ap_ds fix this automatically for me?**

A: No. This is behavior at the Python runtime level that ap_ds cannot intervene in. We can only remind you in documentation and try to provide clear error messages in the code.

---



> **When using `batch_get_metadata()` or any batch parsing API on Windows, you MUST protect your entry point with `if __name__ == "__main__":`. Otherwise, your program will crash when child processes start.**


## 🌿 AFS Branch: ap_ds's "Dual-Track" Future

### Why "Split the Family"?

With Opus joining, the mainline version size grew from 2.5MB to 3.87MB. This sparked a deeper consideration:

**In the future, we'll add more formats. Each new format increases size. If we cram all formats into mainline, ap_ds will eventually become a bloated monster,背离 the original "lightweight" vision.**

So we decided: **split the family.**

### Mainline 4.1.0's Special Status

> **4.1.0 is the ONLY mainline version that includes Opus support.**

This is an intentional design decision:

- **4.1.0 RC**: Opus's "swan song" in mainline — collect feedback, validate solutions
- **4.2.0+ (mainline)**: Remove Opus, return to lightweight positioning
- **ap-ds-afs 1.0.0+**: Opus's permanent home, independent development

**This gives users a clear choice:**

| Your Need | Recommended Version |
|-----------|---------------------|
| Need Opus and willing to test | **4.1.0 RC (mainline)** |
| Need Opus and追求 long-term stability | **ap-ds-afs 1.0.0 (AFS branch)** |
| Don't need Opus,追求 extreme lightweight | **4.2.0+ (mainline)** |

### AFS Branch (All-Format Support)

**AFS (All-Format Support)** is a brand-new independent branch that will carry all new format support "beyond the 2.5MB limit."

| Dimension | Mainline (ap-ds) | AFS Branch (ap-ds-afs) |
|-----------|------------------|----------------------|
| **PyPI package name** | `ap-ds` | `ap-ds-afs` |
| **Import name** | `ap_ds` | `ap_ds` (**identical!**) |
| **Version number** | 4.x (continuing) | 1.0.0 (fresh start) |
| **Opus support** | ❌ (except 4.1.0) | ✅ **Permanent** |
| **Core formats** | MP3 / WAV / FLAC / OGG / AAC | **Above + Opus + all future new formats** |
| **Size** | ~2.5MB | ~2.8-3.5MB |
| **Update strategy** | Security fixes only | **Syncs all mainline updates + own new formats** |
| **Target users** | Minimalist developers | Developers needing special formats |

**Key Design: Import Name Identical**

```python
# Regardless of whether user installed ap-ds or ap-ds-afs
# The import method is exactly the same!
from ap_ds import AudioLibrary
```

This means users can switch seamlessly between the two packages by simply changing the package name in `requirements.txt` or `pip install`.

### Which Should Users Choose?

| Your Need | Recommendation |
|-----------|----------------|
| Only need MP3/WAV/FLAC/OGG/AAC | **Mainline (ap-ds 4.2.0+)** — lightweight, stable |
| Need Opus and willing to test RC | **Mainline (ap-ds 4.1.0 RC)** — early adopter, feedback |
| Need Opus and追求 long-term stability | **AFS (ap-ds-afs 1.0.0)** — full-featured, long-term support |
| Unsure if you'll need new formats later | Install mainline first, switch to AFS when needed (import name identical, painless switching) |

### Foolproof Design: Conflict Detection

If a user installs both `ap-ds` and `ap-ds-afs` simultaneously, ap_ds's `__init__.py` will detect the conflict and hard-exit:

```python
# When both packages are installed, import triggers hard exit
>>> import ap_ds
Checking for package conflicts...
WARNING: Package conflict detected!
The following packages exist simultaneously:
  - ap-ds and ap-ds-afs
Please uninstall one of them.
# Python process exits directly 💀
```

This is not a bug — it's "foolproof design" — ensuring users don't encounter hard-to-debug import issues from package conflicts.


## 📖 Technical Manual Update

`show_tech_manual()` has been updated to 4.1.0, adding the following sections:

### 2.1 OPUS Support (New Section)

- Opus format introduction (IETF standard, WebRTC, low-bitrate high quality)
- Opus playback backend architecture (Windows waveOut / Linux ALSA / macOS Core Audio)
- Opus auto-DLL download (Windows) and system library detection (Linux/macOS)
- OpusAudio class complete API reference
- AID 1:1 mapping mechanism explanation
- Cross-platform Opus library loading deep dive (including macOS special notes)
- **4.1.0 version positioning notes (RC prerelease + Opus uniqueness statement)**

### 10.4 Opus Error Codes (New Section)

- Complete 10 Opus error codes list (2001-2010)
- Meaning and suggested action for each error code
- Error trigger scenario examples

### Version History

- Added 4.1.0 RC version entry
- Recorded Opus support, cross-platform playback backends, AFS branch establishment
- **Marked 4.1.0 as RC prerelease**
- **Clearly stated Opus uniqueness in mainline**


## 🚀 Getting Started

### Installation

```bash
# Mainline 4.1.0 RC (includes Opus, for testing feedback)
pip install ap-ds==4.1.0rc1

# Mainline 4.2.0+ (no Opus, extreme lightweight)
pip install ap-ds>=4.2.0

# AFS branch (includes Opus + all future new formats, permanent support)
pip install ap-ds-afs==1.0.0
```

### 💡 Why Python 3.15t / 3.14t?

Python's **Free-Threading versions** (filenames with `t`) **remove the GIL (Global Interpreter Lock)**, enabling true multi-core parallelism. Combined with ap_ds's batch parsing, **120 MP3 files can be parsed in just 0.33 seconds**.

> ⚠️ **Version Selection Note**: Python 3.15t is currently beta (b4) with potential unknown issues. For a more stable environment, we recommend Python 3.14t (stable). Both support GIL-free free-threading mode.

#### Windows Users

**Python 3.14t (Stable) Downloads:**

| Architecture | Download Link |
|--------------|---------------|
| Windows 64-bit | [python-3.14.4t-amd64.zip](https://mirrors.huaweicloud.com/python/3.14.4/python-3.14.4t-amd64.zip) |
| Windows 32-bit | [python-3.14.4t-win32.zip](https://mirrors.huaweicloud.com/python/3.14.4/python-3.14.4t-win32.zip) |
| ARM64 | [python-3.14.4t-arm64.zip](https://mirrors.huaweicloud.com/python/3.14.4/python-3.14.4t-arm64.zip) |

**Python 3.15t (Beta) Downloads:**

| Architecture | Download Link |
|--------------|---------------|
| Windows 64-bit | [python-3.15.0b4t-amd64.zip](https://mirrors.huaweicloud.com/python/3.15.0/python-3.15.0b4t-amd64.zip) |
| Windows 32-bit | [python-3.15.0b4t-win32.zip](https://mirrors.huaweicloud.com/python/3.15.0/python-3.15.0b4t-win32.zip) |
| ARM64 | [python-3.15.0b4t-arm64.zip](https://mirrors.huaweicloud.com/python/3.15.0/python-3.15.0b4t-arm64.zip) |

> 📦 **ZIP — Extract and Use**: Download and extract to any directory, add `python.exe` path to system PATH, and you're ready. No EXE installer needed — deployment in seconds.

#### Linux Users

**Option 1: Use Package Manager**

**Fedora:**

```bash
sudo dnf install python3.14-freethreading
```

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

**Ubuntu/Debian (using deadsnakes PPA):**

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

> This PPA provides the `-nogil` version, also a GIL-disabled build.

**Option 2: Use Conda (Cross-Platform)**

Install from `conda-forge` channel:

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

**Option 3: Build from Source (Universal)**

```bash
# 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 (GUI)**

1. Download the macOS installer from [python.org](https://www.python.org/downloads/)
2. Run the installer, click the **"Customize"** button in the "Installation Type" screen
3. In the component list that appears, **check the "Free-threaded Python"** option, continue installation

**Option 2: Using Homebrew**

```bash
brew install python-freethreading
```

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

#### Verify Installation

Run the following commands to verify free-threading is working properly:

```bash
# Check version info (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 Virtual Environment

```bash
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-free isolated environment.

### Quick Start

```python
from ap_ds import AudioLibrary

# Initialize library
lib = AudioLibrary()

# Play 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 elapsed time
duration = lib.stop_audio(aid)
print(f"Played {duration:.2f} seconds")
```

### Playing Opus Files (via AudioLibrary, Recommended)

```python
from ap_ds import AudioLibrary

lib = AudioLibrary()
aid = lib.play_from_file("song.opus")  # Auto-routed to Opus engine

# All control methods are identical
lib.pause_audio(aid)
lib.seek_audio(aid, 30.0)
lib.set_volume(aid, 80)
lib.stop_audio(aid)
```

### Playing Opus Files (Directly Using OpusAudio)

```python
from ap_ds import OpusAudio

opus = OpusAudio()
aid = opus.play_from_file("song.opus")
opus.pause_audio(aid)
opus.seek_audio(aid, 30.0)
opus.stop_audio(aid)
```

### Getting Opus Metadata

```python
from ap_ds import get_audio_metadata

meta = get_audio_metadata("song.opus")
print(meta["duration"])       # 265
print(meta["sample_rate"])    # 48000
print(meta["channels"])       # 2
print(meta["bitrate"])        # 105351
```

### Batch Parsing

```python
from ap_ds import batch_get_metadata

# Batch parse 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 API Overview:**

| 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()` | Batch parse filtered by format |

### ⚠️ CRITICAL: Windows Batch Parsing and BrokenProcessPool

If you're using **batch parsing APIs** on **Windows**, you **MUST** protect your entry point with `if __name__ == "__main__"`.

**Why?**

On Windows, `ProcessPoolExecutor` uses `spawn` to create new processes. This means each subprocess will re-import your main module. Without entry point protection, this creates an **infinite recursion loop**, crashing your program with a `BrokenProcessPool` error.

**This is not optional. This is mandatory.**

#### ❌ Wrong (Will Crash on Windows):

```python
from ap_ds import batch_get_metadata

# This will crash with BrokenProcessPool on Windows!
results = batch_get_metadata("/music/", max_workers=4)
```

#### ✅ Correct (Always Works):

```python
from ap_ds import batch_get_metadata

def main():
    results = batch_get_metadata("/music/", max_workers=4)
    print(f"Parsed {len(results)} files")

if __name__ == "__main__":
    main()
```

#### ✅ Correct (Script with Configuration):

```python
from ap_ds import batch_get_metadata

def get_config():
    # ... config logic ...
    return config

def main():
    config = get_config()
    results = batch_get_metadata(config["audio_dir"], max_workers=4)
    print(f"Parsed {len(results)} files")

if __name__ == "__main__":
    main()
```

**This applies to:**

- ✅ Any script that imports ap_ds and uses batch parsing on Windows
- ✅ Jupyter notebooks (if running on Windows, wrap batch calls in a function with `if __name__ == "__main__"`)
- ✅ Any test scripts (e.g., `CI-CD-TEST.py`)

**What about Linux/macOS?**

Not required. Linux and macOS use `fork` by default and don't have this issue. However, for cross-platform compatibility, using entry point protection is still **good practice**.

### DAP Playlist System

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

```python
# Files are recorded automatically
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 takes approximately 150 bytes of memory.

### Platform Support

#### Windows

- Automatically downloads SDL2.dll and SDL2_mixer.dll with hash verification
- Opus support auto-downloads four DLLs (libopusfile-0.dll, libopus-0.dll, libogg-0.dll, libopusurl-0.dll)
- No manual configuration needed
- Supports Windows 7 and above

#### macOS

- Automatically downloads SDL2.framework and SDL2_mixer.framework with hash verification
- Opus support via Homebrew or MacPorts system library installation (auto-detection + auto-install attempt)
- 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. **Auto-installation**: Detects package manager and installs required packages
4. **Interactive guidance**: Provides manual options if all above fail

**Opus Package Manager Support:**

```bash
# Ubuntu/Debian
sudo apt-get install libopusfile-dev libopus-dev libogg-dev

# Fedora
sudo dnf install opusfile-devel opus-devel libogg-devel

# Arch
sudo pacman -S opusfile opus libogg
```

**SDL2 Package Manager Support:**

```bash
# 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 the following platforms:

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

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


## 🧪 CI/CD Testing — Comprehensive Coverage, All Passed

### Opus Test Suite (OPUS_TEST.py)

4.1.0 adds a complete Opus test suite covering 16 test categories:

| Test Category | Test Items | Description |
|---------------|------------|-------------|
| OP-1 | Module import/constants/error codes | Verify all Opus modules, constants, error codes correctly exported |
| OP-2 | DLL loading and auto-download | Verify `_opusdll.py` loading, DLL file existence, hash verification |
| OP-3 | Metadata parsing | Verify Opus duration, sample rate, channels, bitrate, extended tags |
| OP-4 | Playback functionality | Verify `play_from_file`, `play_from_memory`, `new_aid` |
| OP-5 | Playback control | Verify `pause_audio`, `play_audio`, `stop_audio` |
| OP-6 | Volume control | Verify `set_volume` (0-128 boundary values) and `get_volume` |
| OP-7 | Seeking functionality | Verify `seek_audio` for various positions and boundary values |
| OP-8 | Fade in/out | Verify `fadein_music`, `fadein_music_pos`, `fadeout_music` |
| OP-9 | Opus vs native format distinction | Verify `_is_opus_file` and AudioLibrary auto-routing |
| OP-10 | Batch parsing | Verify `batch_get_metadata`, `batch_get_duration`, `batch_by_type` |
| OP-11 | Error code triggering | Verify all 10 Opus error codes trigger correctly |
| OP-12 | AID 1:1 mapping | Verify main AID ↔ Opus sub-AID full lifecycle |
| OP-13 | Resource management | Verify `cleanup_function` correctly releases resources |
| OP-14 | Boundary values and error testing | Verify invalid parameter types, out-of-range values handled correctly |
| OP-15 | DLL-specific testing | Verify DLL file sizes, hashes, load idempotency |
| OP-16 | Error code trigger specific testing | Verify each Opus error code triggers in real scenarios |

### Test Results

```
==================================================================
 Opus Test Summary
==================================================================
   Passed : 229
   Failed : 0
   Skipped: 0
==================================================================
```

**All 229 tests passed, 0 failed, 0 skipped.**

### CI/CD Integration

`OPUS_TEST.py` is integrated into the CI/CD pipeline, automatically running before each release. Test coverage includes:

- **Automated tests** (no human intervention): OP-1 ~ OP-16
- **Listening tests** (requires human confirmation): OP-L (5 listening tests)

Listening tests include:
1. Normal playback is audible
2. Fade-in effect is smooth
3. Fade-out effect is smooth
4. Volume 0 is silent
5. Seek position is correct

### Running Tests

```bash
# Full test (including listening tests)
python OPUS_TEST.py

# Automated tests only
python OPUS_TEST.py --auto

# Listening tests only
python OPUS_TEST.py --listen
```


## 🧪 CI/CD Comprehensive Testing — All Passed (Green)

Below is the complete output summary of the **full CICD test suite** (`CI,CD_TEST.py`) run on a Windows machine with Python 3.13.4. It covers **all public APIs, error handling, edge cases, boundary values, and interactive listening tests** — **650+ tests passed, 0 failed, 0 skipped**. The same tests have been verified on **macOS** and **Ubuntu** with identical results.

```
==================================================================
 Opus Test Summary
==================================================================
   Passed : 229
   Failed : 0
   Skipped: 0
==================================================================

==================================================================
 CICD Test Summary
==================================================================
   Passed : 650+
   Failed : 0
   Skipped: 0
==================================================================
```

**You can run the test suite yourself** by executing `CI,CD_TEST.py` (included in the package):

```bash
python CI,CD_TEST.py --auto      # Automated tests only
python CI,CD_TEST.py --listen    # Interactive listening tests only
python CI,CD_TEST.py --full      # Everything (default)
```


## 🧪 API Import Verification Test

We provide a comprehensive test script `IMPORT_TEST.py` to verify that all documented APIs exist and are correctly exported.

### Test Results

```
============================================================
🧪 Testing All ap_ds API Exports
============================================================

📦 Testing top-level imports:
----------------------------------------
  ✅ AudioLibrary
  ✅ OpusAudio
  ✅ batch_get_metadata
  ✅ batch_get_duration
  ✅ batch_get_metadata_by_type
  ✅ get_audio_duration
  ✅ get_audio_metadata
  ✅ auto_check_runtime
  ✅ check_runtime_mode
  ✅ show_tech_manual

----------------------------------------
🎯 Testing AudioLibrary methods:
----------------------------------------
  ✅ AudioLibrary.__init__
  ✅ AudioLibrary.play_from_file
  ✅ AudioLibrary.play_from_memory
  ✅ AudioLibrary.new_aid
  ✅ AudioLibrary.play_audio
  ✅ AudioLibrary.pause_audio
  ✅ AudioLibrary.stop_audio
  ✅ AudioLibrary.seek_audio
  ✅ AudioLibrary.set_volume
  ✅ AudioLibrary.get_volume
  ✅ AudioLibrary.fadein_music
  ✅ AudioLibrary.fadein_music_pos
  ✅ AudioLibrary.fadeout_music
  ✅ AudioLibrary.is_music_playing
  ✅ AudioLibrary.is_music_paused
  ✅ AudioLibrary.get_music_fading
  ✅ AudioLibrary.get_audio_duration
  ✅ AudioLibrary.get_audio_metadata
  ✅ AudioLibrary.get_audio_metadata_by_path
  ✅ AudioLibrary.get_audio_metadata_by_aid
  ✅ AudioLibrary.batch_get_metadata
  ✅ AudioLibrary.batch_get_duration
  ✅ AudioLibrary.batch_get_metadata_by_type
  ✅ AudioLibrary.save_dap_to_json
  ✅ AudioLibrary.get_dap_recordings
  ✅ AudioLibrary.clear_dap_recordings
  ✅ AudioLibrary.clear_memory_cache
  ✅ AudioLibrary.cleanup_function
  ✅ AudioLibrary._find_channel_by_aid
  ✅ AudioLibrary._get_file_path_by_aid
  ✅ AudioLibrary._is_music_file
  ✅ AudioLibrary._seek_audio
  ✅ AudioLibrary._get_duration_by_filepath
  ✅ AudioLibrary._get_file_duration

----------------------------------------
🔍 Checking extra APIs:
----------------------------------------
  ✅ is_full_performance
  ✅ get_runtime_info

============================================================
📊 FINAL SUMMARY
============================================================
🎉 ALL APIs EXIST! Documentation is accurate.
============================================================
✅ Passed: 47
❌ Failed: 0
```

**All 47 API tests passed!**

| Category | Count | Status |
|----------|-------|--------|
| Top-level imports | 10 | ✅ All passed |
| AudioLibrary methods | 34 | ✅ All passed |
| Extra APIs | 3 | ✅ All passed |
| **Total** | **47** | **✅ 47/47 passed** |


## 📊 Version Comparison Overview

| Dimension | 4.0.1 | 4.1.0 RC | 4.2.0+ (planned) | ap-ds-afs 1.0.0 |
|-----------|-------|----------|------------------|-----------------|
| **Opus support** | ❌ | ✅ | ❌ | ✅ |
| **Playback formats** | MP3/WAV/FLAC/OGG/AAC | +Opus | MP3/WAV/FLAC/OGG/AAC | +Opus + future formats |
| **Cross-platform playback engine** | SDL2 only | SDL2 + Opus native | SDL2 only | SDL2 + Opus native |
| **Opus error codes** | ❌ | ✅ 2001-2010 | ❌ | ✅ 2001-2010 |
| **Auto-DLL download** | SDL2 | SDL2 + Opus DLL | SDL2 | SDL2 + Opus DLL |
| **AFS branch** | ❌ | ✅ | ❌ | ✅ (AFS itself) |
| **Test coverage** | 421 tests | 650+ tests | 421 tests | 650+ tests |
| **Library size** | ~2.5MB | **3.87MB** | **~2.5MB** | ~2.8-3.5MB |
| **Version status** | Stable | **RC prerelease** | Planned | Planned |


## 📦 Version Relationships

| Version | Type | Support Period | Use Case |
|---------|------|----------------|----------|
| **v3.0.0 LTS** | Long-Term Support | Until March 2031 | Production environments |
| **v3.1.x** | LFV (Latest Feature Version) | ~6 months | Early adopters (superseded) |
| **v4.0.x** | LFV (Latest Feature Version) | ~6 months | Early adopters, new features |
| **v4.1.0 RC** | **RC prerelease** | ~2 months | **Opus testing and feedback collection** |
| **v4.2.0+ (mainline)** | LFV (Latest Feature Version) | ~6 months | **Remove Opus, return to lightweight** |
| **ap-ds-afs 1.0.0+** | Long-Term Support | TBD | **Permanent home for Opus and new formats** |


## 📌 Version Upgrade Recommendations

| User Type | Recommendation |
|-----------|----------------|
| Production environments | Continue using **v3.0.0 LTS**, wait for v4.0.0 LTS |
| Development/testing | Upgrade to **v4.1.0 RC** to try Opus, help test feedback |
| Need Opus and willing to test | **Use 4.1.0 RC**, report bugs |
| Need Opus and追求 long-term stability | Wait for **ap-ds-afs 1.0.0** |
| Don't need Opus,追求 extreme lightweight | Wait for **4.2.0+** or continue with 4.0.x |
| Previously affected by v3.1.x metadata bug | **Must upgrade** to v4.0.0+ |

```bash
# 4.1.0 RC installation
pip install ap-ds==4.1.0rc1

# AFS branch (after release)
pip install ap-ds-afs==1.0.0
```


## 🌐 apds.top Is Now Live!

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

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

### Website Features

- 📄 **Full documentation**: API reference, user guide, FAQ
- 📦 **Version 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 via HTTPS


## ⚠️ Repository Migration Notice

### GitHub Deprecated, GitLab Abandoned, Migrated to GitCode — apds.top Is the Permanent Home

#### 1. Why GitHub Was Deprecated

The developer's GitHub account was locked due to loss of two-factor authentication (2FA) device. After multiple attempts to contact GitHub support, only automated bot responses 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 no longer receives any updates.

#### 2. Why GitLab (JiHu) Was Abandoned

After the GitHub issues, the project migrated the primary repository to Gitee and GitLab (JiHu). However, due to platform policy changes where basic account login became a paid feature, GitLab was recently abandoned. 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 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, particularly for Chinese developers, offering fast and stable access. It remains a strongly recommended choice. However, its role has been adjusted to a backup or China-facing mirror for two main reasons:

- **International accessibility**: Gitee's servers are primarily located in 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 large portion of the user base.

- **UI and workflow**: While feature-rich, Gitee's UI and workflows are often considered outdated and not as aligned with modern Git workflows that 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 globally-oriented project.

#### 4. Solution: GitCode Becomes the New Primary Repository

After surveying the landscape of free Git hosting platforms, **GitCode** emerged as the ideal solution. GitCode offers a modern interface, strong feature set, and, most importantly, excellent accessibility for developers both within China and abroad. It has become the new official primary repository for the ap_ds project.

**New primary repository located at:** [https://gitcode.com/dvsxt/ap_ds](https://gitcode.com/dvsxt/ap_ds)

#### 5. 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 solves 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](https://apds.top)


## 🔄 NEW: GitHub Repository (Compatibility Mirror)

### A Word from the Developer

Recently, US-based developer **Clint Shepherd** raised a legitimate concern:

> *"Why isn't there a GitHub repository? GitCode is in China, I'm not used to it. apds.top doesn't have issues, pull requests, forks, or other collaboration features..."*

After careful consideration, we realized he was absolutely right.

While we have no intention of making GitHub the **primary** platform — their customer support was terrible when our account was locked, and we're still bitter about it — the reality is undeniable:

- **GitHub has a massive user base**
- **The ecosystem is very mature**
- **Many developers are simply more comfortable with it**
- **Collaboration features (issues, PRs, forks) are standard expectations**

So we made a pragmatic decision: **We created a new GitHub account** (`dvs-dvsxt`) and set up a compatibility mirror at:

🔗 **[https://github.com/dvs-dvsxt/ap_ds](https://github.com/dvs-dvsxt/ap_ds)**

### Important: This Is a Compatibility Mirror, Not the Primary Repository

| Aspect | Details |
|--------|---------|
| **Purpose** | Compatibility mirror for GitHub developers |
| **Permanent Home** | **[apds.top](https://apds.top)** — permanent official source |
| **Primary Mirror** | **[GitCode](https://gitcode.com/dvsxt/ap_ds)** — global access |
| **China Mirror** | **[Gitee](https://gitee.com/dssxt/ap_ds)** — for Chinese users |
| **GitHub Status** | ⚠️ **Compatibility mirror** — updated periodically, may lag |
| **Best For** | ✅ GitHub users who prefer familiar workflows to star, watch, or clone |

### What This Means for You

- **If you're a GitHub user**: You can now clone, fork, and file issues on GitHub. We will respond to GitHub issues, but **please be patient** — response times may be slower than on GitCode or apds.top.

- **If you're a developer in China**: **Gitee** remains your fastest and most stable choice. Use it with confidence.

- **If you want the latest updates**: Always check **[apds.top](https://apds.top)** first. It has the latest releases, changelogs, and announcements.

- **If you want to contribute**: We welcome contributions on **any** platform — GitCode, Gitee, or GitHub. PRs are reviewed regardless of origin.

### Final Repository Strategy (Updated)

| Platform | Status | Use |
|----------|--------|-----|
| **apds.top** | ✅ **Permanent home** | Official source, documentation, downloads |
| **GitCode** | ✅ Primary mirror | Code hosting for global users |
| **GitHub** | ✅ Compatibility mirror | For GitHub developers **(NEW!)** |
| **Gitee** | ✅ China mirror | Fast access for Chinese users |
| **GitLab (JiHu)** | ❌ Abandoned | No longer maintained |

> **Special thanks to Clint Shepherd** for asking the hard questions and pushing us to make ap_ds more accessible to the global Python community. Your feedback made this better. 🙏


## 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 no external Python dependencies, uses only the Python standard library, and provides non-blocking playback suitable for GUI applications.

**Core Features:**

- **Extremely lightweight:** 2.5MB on Windows / 3.36MB full solution on macOS
- **Zero Python dependencies:** Standard library only
- **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:** Ideal for GUI applications
- **Cross-platform:** Windows, macOS, Linux, embedded ARM64
- **DAP recording system:** Metadata-only automatic playback history
- **Python 3.15t support:** GIL-free true parallelism, full multi-core performance
- **LTS support:** First long-term support version with 5-year maintenance commitment


## Contact and Support

### 📧 Licensing Inquiries

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

### 🛠️ Technical Support

apds.top Issues · GitCode Issues · GitHub Issues · Gitee Issues · Email (completely free)


## Official Repositories and Project Sources

**✅ Official Primary Repository (Preferred):** [https://apds.top](https://apds.top) — ap_ds's permanent official home, fully independent control, TLS encrypted,不受 third-party platform policy changes.

**✅ Primary Mirror (Global Access):** [GitCode](https://gitcode.com/dvsxt/ap_ds) — Globally accessible, modern UI, actively maintained as a public mirror.

**✅ Compatibility Mirror (GitHub Users):** [GitHub](https://github.com/dvs-dvsxt/ap_ds) — For GitHub developers. May lag behind primary sources. Issues and PRs welcome, but response times may be slower.

**✅ China Mirror (Fast and Stable):** [Gitee](https://gitee.com/dssxt/ap_ds) — Full mirror for Chinese developers, fast access, classic stable UI.

**❌ Deprecated and Abandoned:**

- **[GitHub (dvs-web/ap_ds)](https://github.com/dvs-web/ap_ds)** — Deprecated due to permanent account lockout, no longer maintained.
- **[GitLab (JiHu)](https://jihulab.com/dvs/ap_ds)** — 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 Version**
> This release 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](https://apds.top)** or the public mirrors **GitCode**, **GitHub (dvs-dvsxt/ap_ds)**, and **Gitee**.
>
> **Developer Personal Homepage and Blog:** [https://dvsx.top](https://dvsx.top) — Blog is currently under maintenance and upgrades. Stay tuned.
> **ap_ds Project Homepage:** [https://apds.top](https://apds.top) (Official documentation, releases, and licensing center).

**🔗 Canonical URL:** [https://apds.top/](https://apds.top/)
**📖 Blog and Author:** [dvsx.top](https://dvsx.top) — Blog is currently under maintenance and upgrades. Stay tuned.


## About the Author

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


## ap_ds Official Portal

**🎵 ap_ds Official Website (Primary):** [https://apds.top](https://apds.top) — Permanent official home, full documentation, version releases, licensing center
**📦 PyPI Project Page:** [https://pypi.org/project/ap_ds/](https://pypi.org/project/ap_ds/) — Installable via pip
**🌐 Mirror Documentation Site:** [https://www.dvsyun.top/ap_ds](https://www.dvsyun.top/ap_ds) — Backup documentation access

> 👉 The ap_ds project homepage ([apds.top](https://apds.top)) is the **preferred recommendation** for official sources, hosting full documentation, license details, version changelogs, and official releases. The author's personal blog ([dvsx.top](https://dvsx.top)) is currently under maintenance and upgrades. Stay tuned.


# ap_ds Audio Library — Complete API Reference

**Version: v4.1.0 RC**
**Document Date: August 2026**
**Project Homepage: https://apds.top**

## Table of Contents

1. AudioLibrary Class — Complete API
   - Initialization
   - Playback Methods
   - Control Methods
   - Volume Methods
   - Fade and Transition Methods
   - Metadata Methods
   - Batch Parsing Methods
   - DAP System Methods
   - Resource Management
   - Internal Helper Methods

2. Top-Level Convenience Functions

3. AudioParser Module — Metadata API

4. SDL2 Integration Layer

5. Constants Reference

6. Unified Error Handling and Error Code Reference

7. Environment Variables Reference

8. Technical Manual (show_tech_manual())

## AudioLibrary Class — Complete API

The `AudioLibrary` class is the main interface for audio playback, control, and metadata management. Every method that can fail returns a **unified error tuple**:

```
(code: int, message: str, suggestion: str)
```

On success, methods return their documented success value. On failure, they **never raise exceptions** (`__init__` excepted, as it is a constructor) — they return the error tuple above. See the *Unified Error Handling and Error Code Reference* section for details.

### 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. Must be called before any playback operations. Sets up the audio device with the specified parameters and registers an exit handler for automatic resource cleanup.

> **Note:** `__init__` is a **constructor** — it cannot return error tuples. If SDL2 or mixer initialization fails, it raises `RuntimeError` (the only intentional exception in the library).

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `frequency` | `int` | `44100` | Audio sample rate (Hz). Common values: 44100 (CD quality), 48000 (DVD/video), 22050 (voice). |
| `format` | `int` | `MIX_DEFAULT_FORMAT` | Audio sample format. Typically `AUDIO_S16SYS` (16-bit signed, system endianness). |
| `channels` | `int` | `2` | Number of audio channels. `1` = mono, `2` = stereo. |
| `chunksize` | `int` | `2048` | Buffer size (in samples). Larger values reduce CPU but increase latency. |

**Raises (constructor only):**

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

**Examples:**

```python
from ap_ds import AudioLibrary

lib = AudioLibrary()                              # Default (CD quality, stereo)
lib_voice = AudioLibrary(frequency=22050, channels=1, chunksize=1024)
```

---

### Playback Methods

#### `play_from_file(file_path: str, loops: int = 0, start_pos: float = 0.0) -> Union[int, Tuple[int, str, str]]`

**Description:**

Loads and plays an audio file directly from disk. `.ap-ds-dap` files are DAP **export records** (output format) and are **not** supported as playback input — passing one returns `(1003, msg, suggestion)`.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `file_path` | `str` / `bytes` / `os.PathLike` | Required | Full path to the audio file. Supported formats: MP3, WAV, FLAC, OGG, **Opus (4.1.0 RC)**. |
| `loops` | `int` | `0` | Number of loops after the first playback. `0` = once, `-1` = infinite, `>0` = count. |
| `start_pos` | `float` | `0.0` | Start position in seconds. Not supported for sound effects. |

**Returns:**

- **Success:** `int` — Unique Audio ID (AID) identifying this playback instance.
- **Failure:** `Tuple[int, str, str]` — `(error_code, error_message, suggestion)`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1001` | `file_path` does not exist, or `file_path` type is invalid (`None`, `int`, `list`, `dict`, `tuple`, etc.). |
| `1999` | `loops` type is invalid (must be `int`). |
| `1003` | Audio loading failed (e.g., `.ap-ds-dap` file, corrupted file, or unsupported format). |
| `1004` | Playback failed (e.g., no available channel or audio device issue). |
| `2003` | Opus file open failed (Opus-specific). |

**Behavior by File Type:**

| File Type | Mode | Seek Support | Fade Support |
|-----------|------|--------------|--------------|
| MP3, OGG, FLAC | Music (Mix_PlayMusic) | Yes | Yes |
| **Opus (4.1.0 RC)** | **OpusAudio (libopusfile + native API)** | **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 |

**Examples:**

```python
aid = lib.play_from_file("song.mp3")                  # Play once
aid = lib.play_from_file("beep.wav", loops=5)         # Loop 5 times
aid = lib.play_from_file("podcast.mp3", start_pos=30.0)
aid = lib.play_from_file("ambient.ogg", loops=-1)     # Infinite loop
aid = lib.play_from_file("song.opus")                 # Opus auto-routed (4.1.0 RC)

# Invalid input returns error tuple (no exception):
result = lib.play_from_file(None)          # (1001, 'Invalid file path type: NoneType', ...)
result = lib.play_from_file("x.mp3", loops="2")  # (1999, 'Invalid loops type: str...', ...)
```

---

#### `play_from_memory(file_path: str, loops: int = 0, start_pos: float = 0.0) -> Union[int, Tuple[int, str, str]]`

**Description:**

Plays an audio file that has been preloaded into memory via `new_aid()`. Faster than `play_from_file()` because the file is already cached.

**Parameters:** Same as `play_from_file()`.

**Returns:**

- **Success:** `int` (AID).
- **Failure:** `Tuple[int, str, str]` — `(error_code, error_message, suggestion)`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1013` | `file_path` not loaded into memory, or `file_path` type invalid (`None`, `list`, `dict`, etc.). |
| `1999` | `loops` type invalid (must be `int`). |
| `1004` | Playback from cache failed. |

**Example:**

```python
lib.new_aid("gunshot.wav")
lib.new_aid("explosion.wav")
aid = lib.play_from_memory("gunshot.wav")   # Instant, no disk I/O
```

---

#### `new_aid(file_path: str) -> Union[int, Tuple[int, str, str]]`

**Description:**

Preloads an audio file into memory without playing it. Useful for caching sounds or tracks that will be played multiple times.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `file_path` | `str` / `bytes` / `os.PathLike` | Path to the audio file to cache. |

**Returns:**

- **Success:** `int` (AID).
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1001` | `file_path` does not exist or type is invalid. |
| `1003` | Audio loading failed. |

**Example:**

```python
sounds = {
    'hit':  lib.new_aid("hit.wav"),
    'jump': lib.new_aid("jump.wav"),
    'coin': lib.new_aid("coin.wav"),
}
lib.play_from_memory(sounds['hit'])
```

---

### Control Methods

#### `play_audio(aid: int) -> Tuple[int, str, str]`

**Description:**

Resumes a paused audio instance.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid. |

**Example:**

```python
lib.pause_audio(aid)
lib.play_audio(aid)      # Resume
result = lib.play_audio(99999)   # (1002, 'Invalid AID: 99999', ...)
```

---

#### `pause_audio(aid: int) -> Tuple[int, str, str]`

**Description:**

Pauses an audio instance. Can be resumed with `play_audio()`.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid. |

---

#### `stop_audio(aid: int) -> Union[float, Tuple[int, str, str]]`

**Description:**

Stops playback and returns the elapsed playback time in seconds.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |

**Returns:**

- **Success:** `float` — Elapsed playback time in seconds.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid. |

**Example:**

```python
played = lib.stop_audio(aid)          # e.g., 3.42
result = lib.stop_audio(99999)        # (1002, 'Invalid AID: 99999', ...)
```

---

#### `seek_audio(aid: int, position: float) -> Tuple[int, str, str]`

**Description:**

Seeks to the specified position in seconds. Only supported for music-mode files (MP3, OGG, FLAC, long WAV, **Opus**).

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |
| `position` | `int` / `float` | Position in seconds. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid. |
| `1999` | `position` type is invalid. |
| `2007` | Opus seek failed (Opus-specific). |
| `2009` | Opus stream not seekable (Opus-specific). |

**Example:**

```python
lib.seek_audio(aid, 30.5)
result = lib.seek_audio(aid, None)    # (1999, 'Invalid position type: NoneType...', ...)
```

---

### Volume Methods

#### `set_volume(aid: int, volume: int) -> Tuple[int, str, str]`

**Description:**

Sets the volume. Range **0–128**.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |
| `volume` | `int` | Volume value 0–128. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid. |
| `1015` | `volume` is not `int`, or out of 0–128 range. |
| `1004` | Applying volume failed. |

**Example:**

```python
lib.set_volume(aid, 64)
result = lib.set_volume(aid, "loud")   # (1015, 'Invalid volume type: str...', ...)
result = lib.set_volume(aid, 200)      # (1015, 'Invalid volume: 200 (must be 0-128)', ...)
```

---

#### `get_volume(aid: int) -> Union[int, Tuple[int, str, str]]`

**Description:**

Returns the current volume (0–128).

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |

**Returns:**

- **Success:** `int` — Current volume (0–128).
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid. |

---

### Fade and Transition Methods

#### `fadein_music(aid: int, loops: int = -1, ms: int = 0) -> Tuple[int, str, str]`

**Description:**

Fades music in from silence to full volume over `ms` milliseconds.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `aid` | `int` | — | AID of the music file. |
| `loops` | `int` | `-1` | `-1` = infinite, `0` = once, `>0` = count. |
| `ms` | `int` | `0` | Fade duration in milliseconds. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid or not a music file. |
| `1999` | `ms` / `loops` type is invalid. |
| `1003` | Music loading failed. |
| `1004` | SDL_mixer fade failed. |

---

#### `fadein_music_pos(aid: int, loops: int = -1, ms: int = 0, position: float = 0.0) -> Tuple[int, str, str]`

**Description:**

Fades music in from the specified position.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `aid` | `int` | — | AID of the music file. |
| `loops` | `int` | `-1` | Loop count. |
| `ms` | `int` | `0` | Fade duration in milliseconds. |
| `position` | `int` / `float` | `0.0` | Start position in seconds. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1002` | `aid` is invalid or not a music file. |
| `1999` | `ms` / `loops` / `position` type is invalid. |
| `1012` | `Mix_FadeInMusicPos` not supported by current SDL_mixer. |
| `1003` | Music loading failed. |
| `1004` | SDL_mixer fade failed. |

---

#### `fadeout_music(ms: int = 0) -> Tuple[int, str, str]`

**Description:**

Fades out the currently playing music over `ms` milliseconds.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `ms` | `int` | `0` | Fade duration in milliseconds. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1004` | No music is playing, or fade failed. |

---

#### `is_music_playing() -> bool`

**Description:**

Returns whether music is currently playing.

**Returns:** `bool` — `True` if playing, `False` otherwise.

---

#### `is_music_paused() -> bool`

**Description:**

Returns whether music is currently paused.

**Returns:** `bool` — `True` if paused, `False` otherwise.

---

#### `get_music_fading() -> int`

**Description:**

Returns the current fade status.

**Returns:** `int`:

- `0` (`MUS_NO_FADING`): No fade in progress
- `1` (`MUS_FADING_IN`): Fading in
- `2` (`MUS_FADING_OUT`): Fading out

---

### Metadata Methods

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

**Description:**

Returns the duration of an audio file in seconds. Accepts a file path (`str`) or an AID (`int`).

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `source` | `str` or `int` | — | File path or AID. |
| `is_file` | `bool` | `False` | If `True`, treats `source` as a file path. |

**Returns:**

- **Success:** `int` — Duration in seconds (floored).
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1001` | File does not exist. |
| `1002` | Invalid AID. |
| `1011` | Metadata parsing failed. |
| `1999` | Unknown error. |

---

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

**Description:**

Returns full metadata for an audio file. Accepts a file path (`str`) or an AID (`int`).

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `source` | `str` or `int` | — | File path or AID. |
| `is_file` | `bool` | `False` | If `True`, treats `source` as a file path. |

**Returns:**

- **Success:** `Dict` — Contains `path`, `format`, `duration`, `length`, `sample_rate`, `channels`, `bitrate`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1001` | File does not exist. |
| `1002` | Invalid AID. |
| `1011` | Metadata parsing failed. |
| `1014` | `source` type is invalid. |
| `2003` | Opus file open failed (Opus-specific). |
| `2004` | OpusHead corrupted (Opus-specific). |
| `2008` | Opus bitrate unavailable (Opus-specific). |

---

#### `get_audio_metadata_by_path(file_path: str) -> Union[Dict, Tuple[int, str, str]]`

**Description:**

Returns full metadata for an audio file by file path.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `file_path` | `str` | Audio file path. |

**Returns:**

- **Success:** `Dict`.
- **Failure:** `Tuple[int, str, str]`.

---

#### `get_audio_metadata_by_aid(aid: int) -> Union[Dict, Tuple[int, str, str]]`

**Description:**

Returns full metadata for an audio file by AID.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |

**Returns:**

- **Success:** `Dict`.
- **Failure:** `Tuple[int, str, str]`.

---

### Batch Parsing Methods

> **Windows Note:** When using batch APIs, protect your entry point with `if __name__ == "__main__":`.

#### `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 `ProcessPoolExecutor`. Accepts a list of file paths or a directory path (recursively scans supported formats).

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `file_paths` | `List[str]` or `str` | — | List of files or directory path. |
| `max_workers` | `int` | `None` | Number of worker processes. Defaults to CPU core count. Must be a positive integer. |
| `show_progress` | `bool` | `False` | Prints progress to stdout. |

**Returns:**

- **Success:** `List[Dict]` — Metadata dicts for successfully parsed files (failed files are omitted).
- **Failure:** `Tuple[int, str, str]` — When `max_workers` is invalid.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1999` | `max_workers` is invalid (0, negative, non-integer, or exceeds platform limits). |

---

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

**Description:**

Returns durations for multiple files in parallel.

**Returns:**

- **Success:** `Dict[str, int]` — `{path: duration_seconds}`.
- **Failure:** `Tuple[int, str, str]` — Invalid `max_workers`.

---

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

**Description:**

Parses multiple files but only returns results matching a specific format (e.g., `"mp3"`, `"opus"`).

**Returns:**

- **Success:** `List[Dict]` — Metadata for matching files.
- **Failure:** `Tuple[int, str, str]` — Invalid `max_workers`.

---

### DAP System Methods

#### `save_dap_to_json(save_path: str) -> Tuple[int, str, str]`

**Description:**

Saves DAP records to a `.ap-ds-dap` JSON file.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `save_path` | `str` | Output path. Must end with `.ap-ds-dap`. |

**Returns:**

- **Success:** `(0, "", "")`.
- **Failure:** `Tuple[int, str, str]`.

**Error Codes:**

| Code | Condition |
|------|-----------|
| `1009` | Extension is not `.ap-ds-dap`. |
| `1010` | Write failed (bad path, permission denied, disk full). |

---

#### `get_dap_recordings() -> List[Dict]`

**Description:**

Returns the current DAP records (a copy).

**Returns:** `List[Dict]` — Each record contains `path`, `duration`, `bitrate`, `channels`.

---

#### `clear_dap_recordings() -> None`

**Description:**

Clears all DAP records from memory.

**Returns:** `None`.

---

### Resource Management

#### `clear_memory_cache() -> None`

**Description:**

Releases all cached `Mix_Chunk` and `Mix_Music` objects and clears the cache.

**Returns:** `None`.

---

#### `cleanup_function() -> None`

**Description:**

Cleans up all resources: clears cache, closes mixer, quits SDL. Automatically registered with `atexit`.

**Returns:** `None`.

---

## Top-Level Convenience Functions

The following functions are exported at the package top level (`from ap_ds import ...`).

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

**Description:** Parses multiple audio files in parallel.

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

**Description:** Returns durations for multiple files in parallel.

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

**Description:** Parses in parallel and filters by format.

#### `get_audio_duration(file_path: str) -> int`

**Description:** Returns the duration of a single audio file in seconds.

#### `get_audio_metadata(file_path: str) -> Optional[Dict]`

**Description:** Returns full metadata for a single audio file.

#### `auto_check_runtime() -> Optional[Dict]`

**Description:** Runs a runtime self-check.

#### `check_runtime_mode() -> bool`

**Description:** Checks whether the GIL is enabled.

#### `show_tech_manual() -> None`

**Description:** Prints the complete built-in technical manual to stdout.

#### `is_full_performance() -> bool`

**Description:** Returns whether running in full-performance mode.

#### `get_runtime_info() -> Dict`

**Description:** Returns a runtime information dictionary.


## AudioParser Module — Metadata API

The `audio_parser` module provides pure-Python metadata parsers with zero external dependencies.

### Format-Specific Parser Classes

| Class | Format | Accuracy | Parsing Method |
|-------|--------|----------|----------------|
| `WAVFile` | WAV | 100% | RIFF chunk structure |
| `FLACFile` | FLAC | 100% | STREAMINFO metadata block |
| `MP3File` | MP3 | >98% | Per-frame sync word scanning |
| `AACFile` | AAC (ADTS) | >99% | ADTS frame parsing |
| `OGGFile` | OGG Vorbis | 99.99% | Granule position + Vorbis headers |

All parser classes inherit from `FileType`, exposing properties `length`, `sample_rate`, `channels`, and `bitrate`.

#### `open_audio(filename: str) -> FileType`

**Description:**

Factory function that returns the appropriate parser instance for the file.

**Raises:**

- `ValueError`: If the file format is unsupported.


## SDL2 Integration Layer

The `_sdl2` module provides the cross-platform SDL2 loader, constants, structures, and ctypes bindings. This is an internal module; users interact with it indirectly through `AudioLibrary`.

### Global SDL2 Functions

| Function | Description |
|----------|-------------|
| `SDL_Init(flags)` | Initializes SDL subsystems. Returns 0 on success. |
| `SDL_Quit()` | Shuts down SDL. |
| `SDL_GetError()` | Returns the last SDL error message. |
| `SDL_RWFromFile(file, mode)` | Opens a file as an SDL RWops handle. |
| `SDL_Delay(ms)` | Suspends the calling thread for `ms` milliseconds. |

### Global SDL2_mixer Functions

| Function | Description |
|----------|-------------|
| `Mix_OpenAudio(freq, format, channels, chunksize)` | Opens the audio mixer. Returns 0 on success. |
| `Mix_CloseAudio()` | Closes the mixer. |
| `Mix_LoadWAV(file)` | Loads a WAV sound effect into a `Mix_Chunk`. |
| `Mix_LoadMUS(file)` | Loads a music file into a `Mix_Music`. |
| `Mix_FreeChunk(chunk)` | Frees a sound effect chunk. |
| `Mix_FreeMusic(music)` | Frees a music object. |
| `Mix_PlayChannel(channel, chunk, loops)` | Plays a chunk on a channel. |
| `Mix_PlayMusic(music, loops)` | Plays music. |
| `Mix_Pause(channel)` / `Mix_PauseMusic()` | Pauses a channel / music. |
| `Mix_Resume(channel)` / `Mix_ResumeMusic()` | Resumes a channel / music. |
| `Mix_HaltChannel(channel)` / `Mix_HaltMusic()` | Stops a channel / music. |
| `Mix_SetMusicPosition(position)` | Seeks music to position (seconds). |
| `Mix_Volume(channel, volume)` | Sets/gets channel volume (0–128, `-1` = get). |
| `Mix_VolumeMusic(volume)` | Sets/gets music volume (0–128, `-1` = get). |
| `Mix_FadeInMusic(music, loops, ms)` | Fades music in. |
| `Mix_FadeOutMusic(ms)` | Fades music out. |
| `Mix_FadeInMusicPos(music, loops, ms, position)` | Fades music in from position. |


## 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 subsystem. |
| `SDL_INIT_GAMECONTROLLER` | `0x00002000` | Game controller subsystem. |
| `SDL_INIT_EVENTS` | `0x00004000` | Events subsystem. |
| `SDL_INIT_EVERYTHING` | `0x00007231` | All subsystems combined. |

### Audio Formats

| Constant | Value | Description |
|----------|-------|-------------|
| `AUDIO_U8` | `0x0008` | Unsigned 8-bit. |
| `AUDIO_S8` | `0x8008` | Signed 8-bit. |
| `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_S32LSB` | `0x8020` | Signed 32-bit little-endian. |
| `AUDIO_S32MSB` | `0x9020` | Signed 32-bit big-endian. |
| `AUDIO_F32LSB` | `0x8120` | Float 32-bit little-endian. |
| `AUDIO_F32MSB` | `0x9120` | Float 32-bit big-endian. |
| `MIX_DEFAULT_FORMAT` | `AUDIO_S16SYS` | Default mixer format. |

### Mixer Initialization Flags

| Constant | Value | Description |
|----------|-------|-------------|
| `MIX_INIT_FLAC` | `0x00000001` | FLAC support. |
| `MIX_INIT_MOD` | `0x00000002` | MOD support. |
| `MIX_INIT_MP3` | `0x00000008` | MP3 support. |
| `MIX_INIT_OGG` | `0x00000010` | OGG support. |
| `MIX_INIT_MID` | `0x00000020` | MIDI support. |
| `MIX_INIT_OPUS` | `0x00000040` | Opus support (unused in SDL2_mixer, replaced by OpusAudio). |

### Music Type Constants

| Constant | Value | Description |
|----------|-------|-------------|
| `MUS_NONE` | `0` | No music. |
| `MUS_CMD` | `1` | Command-based. |
| `MUS_WAV` | `2` | WAV. |
| `MUS_MOD` | `3` | MOD. |
| `MUS_MID` | `4` | MIDI. |
| `MUS_OGG` | `5` | OGG. |
| `MUS_MP3` | `6` | MP3. |
| `MUS_FLAC` | `7` | FLAC. |
| `MUS_OPUS` | `8` | Opus (unused in SDL2_mixer). |

### Fade Status Constants

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


## Unified Error Handling and Error Code Reference

### Overview

Starting from v4.0.0, ap_ds uses a **unified error handling system**. All methods that can fail return a consistent tuple format:

```
(error_code: int, error_message: str, suggestion: str)
```

**Success:** `(0, "", "")`

**Failure:** `(error_code, error_message, suggestion)`

### Complete Error Code Reference

| Code | Constant | Meaning | Suggestion |
|------|----------|---------|------------|
| 0 | `AP_DS_SUCCESS` | Operation completed successfully | No action needed |
| 1001 | `AP_DS_ERR_FILE_NOT_FOUND` | File does not exist | Verify file path exists and is accessible |
| 1002 | `AP_DS_ERR_INVALID_AID` | Invalid or expired Audio ID | Check AID is valid and audio is loaded |
| 1003 | `AP_DS_ERR_AUDIO_LOAD_FAILED` | Audio file loading failed | Check file format and integrity |
| 1004 | `AP_DS_ERR_PLAYBACK_FAILED` | Audio playback failed | Check audio device and file format |
| 1005 | `AP_DS_ERR_SDL_INIT_FAILED` | SDL2 initialization failed | Check SDL2 installation and audio driver |
| 1006 | `AP_DS_ERR_MIXER_INIT_FAILED` | SDL2_mixer initialization failed | Check audio device and available formats |
| 1007 | `AP_DS_ERR_UNSUPPORTED_FORMAT` | Audio format not supported | Use a supported format |
| 1008 | `AP_DS_ERR_NOT_MUSIC_FILE` | Operation only supports music files | Sound effects do not support this operation |
| 1009 | `AP_DS_ERR_DAP_INVALID_EXT` | Invalid DAP file extension | Use `.ap-ds-dap` extension when saving DAP records |
| 1010 | `AP_DS_ERR_DAP_SAVE_FAILED` | DAP record saving failed | Check write permissions and disk space |
| 1011 | `AP_DS_ERR_METADATA_PARSE_FAILED` | Metadata parsing failed | File may be corrupted or uses unsupported variant |
| 1012 | `AP_DS_ERR_FADE_NOT_SUPPORTED` | Fade operation not supported by SDL_mixer | Update SDL_mixer or use alternative method |
| 1013 | `AP_DS_ERR_AUDIO_NOT_LOADED` | Audio file not loaded into memory | Call `new_aid()` first to load file into cache |
| 1014 | `AP_DS_ERR_INVALID_SOURCE` | Invalid source type for metadata query | Use file path (str) or AID (int) |
| 1015 | `AP_DS_ERR_INVALID_VOLUME` | Volume value out of range | Volume must be between 0 and 128 |
| 1016 | `AP_DS_ERR_SEEK_NOT_SUPPORTED` | Seeking not supported for this audio | Sound effects (short WAVs) do not support seeking |
| **2001** | `AP_DS_ERR_OPUS_LIB_LOAD_FAILED` | **libopusfile loading failed** | **Check if DLL exists** |
| **2002** | `AP_DS_ERR_OPUS_DLL_DEPENDENCY` | **DLL dependency missing** | **Ensure libopus-0.dll and libogg-0.dll exist** |
| **2003** | `AP_DS_ERR_OPUS_OPEN_FAILED` | **Opus file open failed** | **File may be corrupted or not a valid Opus stream** |
| **2004** | `AP_DS_ERR_OPUS_HEADER_CORRUPT` | **OpusHead header corrupted** | **Header info invalid or corrupted** |
| **2005** | `AP_DS_ERR_OPUS_TAGS_PARSE_FAILED` | **Tags parsing failed** | **Tag data corrupted or invalid format** |
| **2006** | `AP_DS_ERR_OPUS_DECODE_FAILED` | **Opus decoding failed** | **Audio data corrupted** |
| **2007** | `AP_DS_ERR_OPUS_SEEK_FAILED` | **Opus seek failed** | **Stream may not support seeking to that position** |
| **2008** | `AP_DS_ERR_OPUS_BITRATE_UNAVAILABLE` | **Bitrate unavailable** | **Unable to determine bitrate for this Opus stream** |
| **2009** | `AP_DS_ERR_OPUS_NOT_SEEKABLE` | **Stream not seekable** | **This Opus stream does not support seeking** |
| **2010** | `AP_DS_ERR_OPUS_CHANNEL_INVALID` | **Invalid channel count** | **Opus stream has invalid channel count** |
| 1999 | `AP_DS_ERR_UNKNOWN` | An unexpected error occurred | Check file integrity and retry |


## Environment Variables Reference

| Variable | Default | Description |
|----------|---------|-------------|
| `AP_DS_HIDE_SUPPORT_PROMPT` | Not set | Set to `1` to hide startup banner |
| `AP_DS_WAV_THRESHOLD` | `6` | WAV mode switch threshold (seconds) |
| `AP_DS_SUPPRESS_WARNINGS` | Not set | Set to `1` to suppress deprecation warnings |
| `AP_DS_SHOW_CONGRATS` | Not set | Set to `0` to hide full-performance congratulatory message |
| `AP_DS_SKIP_AUTO_CHECK` | `1` | Set to `0` to enable runtime self-check on import |


## Technical Manual (show_tech_manual())

ap_ds 4.1.0 RC includes a built-in technical manual that can be displayed by calling `show_tech_manual()`. This manual contains:

- Library overview
- Supported audio formats (including Opus chapter)
- Core components (AudioLibrary, metadata parser, SDL2 loader, Opus player)
- DAP system documentation
- WAV smart mode documentation
- Environment variables reference
- Performance optimization tips
- Cross-platform notes (including macOS Opus special notes)
- Troubleshooting guide
- API reference
- Version history
- Contribution and support information

View the manual:

```python
from ap_ds import show_tech_manual
show_tech_manual()
```

# Version History

### v4.1.0 RC (August 19, 2026) — Opus Support Preview

**⚠️ This is an RC (Release Candidate) prerelease version for collecting feedback and bug reports.**

**🚨 Important: Opus support exists ONLY in version 4.1.0 of the mainline. Mainline 4.2.0+ will REMOVE Opus support, migrating to the AFS branch (ap-ds-afs).**

**🎯 New: Opus Support**

- Added `opusplayer.py`: `OpusAudio` class, complete Opus playback engine
- Added `_opusdll.py`: Cross-platform Opus library loader (Windows auto-DLL download, Linux/macOS system library detection + installation)
- Added 10 Opus-specific error codes (2001-2010)
- Windows: Auto-download libopusfile-0.dll, libopus-0.dll, libogg-0.dll, libopusurl-0.dll with SHA256 hash verification
- Linux: apt/dnf/pacman auto-installation + interactive guidance
- macOS: Homebrew/MacPorts auto-detection + auto-install attempt + manual guidance
- Cross-platform playback backends: Windows (winmm waveOut) / Linux (ALSA) / macOS (Core Audio AudioQueue)

**🎯 New: AFS Branch**

- Announced AFS (All-Format Support) branch (ap-ds-afs)
- Opus will migrate to AFS branch after 4.1.0 RC
- Mainline 4.2.0+ will remove Opus, returning to 2.5MB lightweight positioning
- Added package conflict detection (foolproof design)

**🧪 Testing**

- Added OPUS_TEST.py: 229 Opus-specific tests, all passed
- CI/CD integration: 650+ comprehensive tests, all passed
- 5 listening tests (normal playback, fade-in, fade-out, volume 0, Seek)

**📚 Documentation**

- `show_tech_manual()` added 2.1 OPUS Support section
- Added 10.4 Opus Error Codes section
- Version history added 4.1.0 RC entry
- Updated README complete version with API reference, error codes, environment variables, and all other content

**📦 Size**

- Current 3.87 MB (4,059,251 bytes) — 4.1.0 RC only
- 4.2.0+ will remove Opus, returning to ~2.5MB


### v4.0.0 (August 2026) — Architecture Refactor and Performance Edition

**This is a complete rewrite of the core architecture.** This is the largest refactor in ap_ds history, addressing all technical debt accumulated in the v3.1.x series.

**🔧 Architecture Changes**

- Complete module refactoring:
  - Merged `audio_parser.py` (wrapper) with `audio_info.py` (actual parsers) into a single `audio_parser.py`, eliminating circular dependencies
  - Split `player.py` into `player.py` (AudioLibrary only) and `_sdl2.py` (loader + constants + bindings)
  - Each file now has a single responsibility, easy to maintain and debug

**🐛 Bug Fixes**

- Fixed v3.1.x circular import bug: `get_audio_duration()` and `get_audio_metadata()` returning 0 or None in some environments is now completely resolved
- Fixed inconsistent import behavior across Python versions
- Eliminated redundant wrapper layers that caused confusion

**🚀 New Features**

- **Unified error handling system**: All methods return `(error_code, error_message, suggestion)` tuples,告别 the era of exception chaos and inconsistent return values
- **Batch parsing API**: `batch_get_metadata()`, `batch_get_duration()`, `batch_get_metadata_by_type()` using `ProcessPoolExecutor` to process hundreds of files in parallel
- **Python 3.15t free-threading support**: GIL-free true parallelism, batch parsing scales linearly on multi-core CPUs
- **O(1) DAP deduplication**: `_add_to_dap_recordings()` uses set-based deduplication, near-instantaneous duplicate checking
- **Lazy imports** (Python 3.15+): Heavy modules loaded on demand, speeding up `import ap_ds`
- **Runtime self-check**: Automatic environment diagnosis on import (disable with `AP_DS_SKIP_AUTO_CHECK=1`)
- **Smart WAV mode**: WAV files shorter than `AP_DS_WAV_THRESHOLD` (default 6 seconds) play as sound effects; longer files stream as music, supporting seeking and fade effects
- **Fade control**: `fadein_music()`, `fadein_music_pos()`, `fadeout_music()` and status checks
- **Cross-platform SDL2 loader**: Auto-downloads and hash-verifies SDL2 binaries on Windows and macOS; intelligent fallback on Linux
- **Parameter type validation**: Every public entry point performs strict type checking, programming errors return clear error tuples instead of obscure `ctypes.ArgumentError`

**📚 Documentation**

- Complete API reference: covers all AudioLibrary methods, parameters, return values, error codes
- Unified error code reference: list of all error codes from 1001 to 1999
- Environment variables reference: `AP_DS_WAV_THRESHOLD`, `AP_DS_SUPPRESS_WARNINGS`, etc.
- `show_tech_manual()` built-in technical manual

**🔗 Repository**

- Added GitHub compatibility mirror: `dvs-dvsxt/ap_ds`, providing convenience for GitHub users
- Primary repositories remain apds.top and GitCode

**⚡ Performance**

- 120 MP3 files batch parsing: 0.331 seconds (8 processes), 2.94x faster than Mutagen (0.973 seconds)
- 3.88x faster than v3.0.0 multi-threaded solution (1.285 seconds)


### v3.1.2 (July 2026) — Performance and Batch Parsing Edition (LFV)

**🚨 Known Issue: Circular import bug causing `get_audio_duration()` and `get_audio_metadata()` to return 0 or None in some environments. This is fixed in v4.0.0. Users are advised to skip this version and upgrade directly to v4.0.0.**

**🚀 New Features**

- **Batch parsing API**:
  - `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()`: Batch parse filtered by format
- **Python 3.15t free-threading support**: Full adaptation to GIL-free environment, runtime auto-detection of GIL status
- **DAP deduplication optimization**: Upgraded from O(n) linear scan to O(1) set-based deduplication, with O(n) fallback retained
- **Lazy imports** (Python 3.15+): Heavy modules loaded on demand
- **Runtime diagnostic functions**: `is_full_performance()` and `get_runtime_info()`
- **Runtime self-check**: Auto-executed on import (skip with `AP_DS_SKIP_AUTO_CHECK=1`)

**⚡ Performance**

- 120 MP3 files: Serial 1.367s → 8-process parallel 0.331s (4.13x speedup)
- 2.94x faster than Mutagen, 3.88x faster than v3.0.0

**📦 Version Relationship**

- This version is an LFV (Latest Feature Version), not LTS
- Next LTS is v4.0.0 LTS (after Python 3.15 stabilizes)


### v3.0.0 LTS (March 22, 2026) — First Long-Term Support Version

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

**🎯 New Features**

- **Deterministic resource cleanup**: Replaced unreliable `__del__` finalizers with explicit exit handlers
- **Hash-verified downloads**: Each downloaded SDL2 library is verified against hardcoded SHA-256 hashes before use
- **Complete test coverage**: Tested on all platforms with zero memory leaks
- **5-year support period**: Until March 22, 2031, with free technical support

**🔄 Compatibility**

- No breaking changes, fully backward compatible with v2.x


### v2.4.2 (March 22, 2026) — Development Mishap Version

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

This version is for curious exploration only — do not use in production environments.


### v2.4.1 (March 1, 2026) — Documentation Update

Updated PyPI documentation to fully reflect v2.4.0's new features.

**📚 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:** No code changes in this release — documentation only.


### v2.4.0 (March 1, 2026) — Audio Effects and Engineering Improvements

Introduced professional audio transitions and important internal engineering upgrades.

**🎵 New Audio Control Functions**

| Function | Description |
|----------|-------------|
| `fadein_music(aid, loops=-1, ms=0)` | Fade music in over specified milliseconds |
| `fadein_music_pos(aid, loops=-1, ms=0, position=0.0)` | Fade music in from specified position |
| `fadeout_music(ms=0)` | Fade out currently playing music |
| `is_music_playing()` | Check if music is playing |
| `is_music_paused()` | Check if music is paused |
| `get_music_fading()` | Get 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

**🔄 Compatibility**

- 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 and Embedded Verification

**Six-dimensional test coverage:**

1. Library loading and initialization
2. Playback testing (MP3, FLAC, OGG, WAV)
3. Seek testing
4. Memory pressure and 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 improvement 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 config file (`~/.config/ap_ds/sdl_paths.conf`)


### v2.3.3 (February 9, 2026) — Critical Bug Fix and Platform Stabilization

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

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

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


### 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 paths
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:**

- **Smart automatic recording**: Auto-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 recordings of 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**

- Automatically downloads and installs SDL2.framework, SDL2_mixer.framework
- Intelligent .dmg file extraction and framework loading
- Maintains extreme lightweight: only 3.36MB (vs Windows 2.5MB)

**2. Enhanced Automatic Dependency Management**

- Cross-platform intelligent download strategy
- Complete error handling and retry mechanisms
- Local caching of dependency files


### v2.1.4 (January 18, 2026) — Stable Release

**Production-ready stable release.**

- Core stability: Extensively tested, no known critical bugs
- Extreme 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 precision improvements: Better time control and seeking


### v2.0.0 (November 5, 2025) — Architecture Refactor

**Introducing modern audio management system.**

**Major Improvements:**

- **AID system**: Unified audio instance management
- **Architecture refactor**: Modular design, 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, basic functionality.**

**Core Features:**

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

## License

This project is licensed under the **DVS Audio Library (ap_ds) Open Source License v2.0**. The full license text is below. Use, copying, modification, or distribution of this software constitutes acceptance of all terms and conditions of this license.

# DVS Audio Library (ap_ds) Open Source License v2.0

**Version: 2.0**
**Effective Date: March 22, 2026**
**Applies to: ap_ds 2.4.1 and above (except for subsequent license updates)**
**Project Homepage: 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.

1.2. **"Source Code"** means the human-readable form of the Software.

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

1.4. **"Distribution"** 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 rights granted by this License.

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


## 2. License Grant

Subject to the terms and conditions of this License, the author 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 Source Code of the Software and make any modifications to suit Your needs.

2.4. **Integrate and Use Commercially**: Integrate the Software into Your products or projects and use it in any commercial environment.


## 3. Obligations and Restrictions

### 3.1. Attribution and Source Identification

Whenever using, distributing, or integrating the Software or a Modified Version, You must:

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

b) **Provide Prominent Source Attribution**: Clearly and prominently state in the Software's 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://apds.top
   ```

c) **Add Notice for Modified Versions**: If You distribute a Modified Version, in addition to the above attribution, You must add the following notice:
   ```
   This is a modified version maintained by [Your name/organization].
   Support: [Your contact information].
   This version is not official 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) **No Use of Original Brand Names**: You may not name a Modified Version "ap_ds", "AP_DS", "Audio Library By DVS", "DVS Audio Player", or any confusingly similar variation, combination, or derivative name.

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

c) **Maintainer Responsibility Statement**: Distributors of Modified Versions must state on their project homepage or in a prominent location:
   ```
   This project is based on DVS Audio Library (ap_ds) but has independently evolved and is entirely maintained by [Your name].
   For the original version, please visit: https://apds.top.
   The maintainer is solely responsible for any issues related to this project.
   ```

### 3.3. Modified Version Quality Commitment

If You distribute a Modified Version, You must:

a) **Clearly State Modifications**: Clearly indicate that this is a Modified Version and list key modifications and compatibility notes compared to the original version.

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

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

### 3.4. Prohibited Uses

You may not use the Software for any illegal activities, malicious purposes, or in violation of local laws and regulations.


## 4. Patent Grant

4.1. **Patent License**: The author 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. **Security Review Right**: Any user has the right to conduct security audits of the Software's Source Code.

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

5.3. **No Backdoors Commitment**: Official releases commit to containing no malicious code, backdoors, or functionality that collects user data without explicit user consent.


## 6. Disclaimer of Warranties and Limitation of Liability

6.1. **Disclaimer of Warranties**: The Software is provided "AS IS", without warranty of any kind, express or implied.

6.2. **Limitation of Liability**: To the maximum extent permitted by applicable law, the author or copyright holder shall not be liable for any direct, indirect, incidental, special, consequential, or punitive damages.


## 7. License Management and Termination

7.1. **Version Control**: This License is version 2.0. Subsequent versions will be published on the project homepage.

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

7.3. **Automatic Termination**: If You fail to comply with the terms of this License, Your rights will automatically terminate.


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

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 in the place of the project author's domicile.


## 9. Contact Information

9.1. **Licensing Inquiries**:
  - Email: me@dvsyun.top or dvs6666@163.com
  - Project Homepage: https://apds.top
  - Response Time: Within 7 business days

9.2. **Technical Support**:
  - Priority: File issues via GitCode Issues
  - Urgent matters: Send to the above email addresses


**Use, copying, modification, or distribution of this software constitutes acceptance of all terms and conditions of this license.**


## SDL2 Acknowledgements

### Sincere Gratitude

ap_ds would not exist without the extraordinary work of the **SDL2** development team. We owe them a debt of gratitude that words cannot adequately express.

**To Sam Lantinga and the entire SDL development community:**

Thank you. From the bottom of our hearts, thank you.

You have built something truly remarkable. For over twenty years, SDL has been the backbone of countless games, multimedia applications, and creative projects worldwide. It runs on everything — Windows, macOS, Linux, Android, iOS, game consoles, and embedded devices. It is stable, efficient, and beautifully designed. It is one of the most important open-source projects of our time.

We are just one small library among thousands that depend on your work. But we are deeply grateful. Every time a user plays an audio file through ap_ds, it's SDL2 doing the heavy lifting — decoding, mixing, and streaming audio with low latency and rock-solid reliability. We just provide the Python wrapper. You provide the magic.

### Legal Compliance

This library uses the **Simple DirectMedia Layer (SDL2)** and **SDL2_mixer** libraries.

- **SDL2 Website:** https://www.libsdl.org/
- **SDL2 License:** zlib/libpng license
- **SDL2_mixer Website:** https://www.libsdl.org/projects/SDL_mixer/
- **SDL2_mixer License:** zlib/libpng license

The zlib/libpng license is a permissive free software license that allows free use, modification, and distribution of the software in commercial products with minimal attribution requirements.


## Closing

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!**
