# ap_ds: A Lightweight Python Audio Library

**Current Stable Version: v3.0.0 LTS (Long‑Term Support) – Released March 22, 2026**

------

### v3.0.0 LTS – The First Long‑Term Support Release
This is the **first Long-Term Support (LTS) release** in ap_ds history. After years of refinement, extensive real-world testing, and a complete overhaul of internal resource management, we are proud to present a version that is ready for mission-critical applications, enterprise deployment, and personal projects alike. **It is strongly recommended for all users.**

**Why LTS?**  
We understand that stability and long-term predictability are essential for developers building products, systems, and services. This release marks a commitment to **five years of maintenance**, including security updates, critical bug fixes, and free technical support. You can confidently build upon ap_ds without worrying about unexpected API changes or deprecations.

**What makes v3.0.0 LTS special?**

1.  **Deterministic Resource Cleanup** – The old `__del__` finalizer, which could be unreliable in certain interpreter shutdown scenarios, has been replaced with an explicit at-exit handler. All SDL2 resources – audio devices, channels, caches – are now guaranteed to be freed, even when exceptions or signals interrupt normal execution. This eliminates a long-standing source of memory leaks and resource warnings.

2.  **Hash-Verified Downloads (Security Hardening)** – In previous versions, if SSL certificate verification failed, the library would fall back to an unverified connection without any checks. Starting with v3.0.0 LTS, **every downloaded SDL2 library is validated against a hardcoded SHA-256 hash** before it is used. If the hash does not match the expected value, the download is rejected and retried. This cryptographic guarantee ensures that the binary you run is exactly the one we released – no tampering, no man-in-the-middle attacks, and no accidental corruption.

3.  **Complete Test Coverage** – The library has been tested on:
    - Windows 7/10/11 (x86_64)
    - macOS 10.15 – 14 (Intel and Apple Silicon)
    - Linux (Ubuntu 20.04/22.04, Debian 11, Fedora 38, Arch Linux)
    - ARM64 embedded devices (Orange Pi 4 Pro, Raspberry Pi 5)
    All test suites (playback, seeking, volume, DAP, metadata, resource cleanup) pass with **zero memory leaks** and no regressions.

4.  **5-Year Support Period** – Free technical support is available through Gitee, GitLab, and email until **March 22, 2031**. You will receive security patches and critical bug fixes without needing to upgrade to a newer major version unless you wish to.

5.  **100% Backward Compatibility** – The public API remains unchanged from v2.x. Your existing code will run exactly as before. Upgrading is as simple as `pip install --upgrade ap_ds`.

**Version Maintenance and Support Policy**

To further clarify the maintenance boundaries of the Long-Term Support (LTS) release, the ap_ds project is introducing a unified version management and support identification system.

This release, **v3.0.0 LTS**, along with its subsequent **v3.0.x** series (i.e., 3.0.0, 3.0.1, 3.0.2, …), is officially designated as the sole version range covered by the **APDSLTS001** support plan. **All version updates within this series, regardless of the specific patch number, uniformly use APDSLTS001 as their long-term support identifier.** This means that as long as you remain on the v3.0.x release line, you are entitled to the full five-year maintenance commitment.

**Support Period**: Official technical support, security updates, and critical bug fixes for APDSLTS001 will terminate on **March 22, 2031 at 23:59:59 UTC**. The corresponding Beijing time (UTC+8) is **March 23, 2031 at 07:59:59**. After this date, the v3.0.x series will no longer receive updates or maintenance of any kind.

**Version Branch Description**:
- **v3.0.x (APDSLTS001)**: The Long-Term Support version, which includes only bug fixes, security patches, and stability improvements. **No new features are introduced**, ensuring complete API and behavioral stability.
- **v3.1.x and above (e.g., v3.1.0, v3.2.0, …)**: These are regular release versions that **do not enjoy long-term technical support**. Such versions will include new features, performance optimizations, and possible API extensions. If you need the latest functionality, you may choose to upgrade to these versions; however, for production environments that prioritize long-term stability and wish to avoid frequent changes, it is recommended to remain on the v3.0.x LTS series.

Through this policy, we aim to provide clear choices for users in different scenarios: **production projects should stay on the LTS release line, while exploratory projects are free to upgrade to new feature versions.** All update patches for the LTS version will continue to be released through Gitee, GitLab, and official channels until the support period ends. We believe that this clear version differentiation and support boundary will help developers better plan their product lifecycles.

**All users are encouraged to upgrade immediately.** This is the definitive version for production use.
---

## ⚠️ Important Version Information

### Repository Changes: GitHub Deprecation, GitLab Abandonment, and the Move to GitCode

This section provides a detailed explanation of the recent changes to ap_ds's official code repositories, which are crucial for all users to understand.

#### 1. Why GitHub is Deprecated

The developer’s GitHub account was locked due to a lost two-factor authentication (2FA) device. After multiple attempts to contact GitHub support, only automated bot replies were received. Frustrated by the complete lack of human assistance, the developer decided to abandon the GitHub account entirely and will not create a new one for the foreseeable future. The old `dvs-web/ap_ds` repository is now officially deprecated and will not receive any further updates.

#### 2. Why GitLab (JihuLab) is Abandoned

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

#### 3. Why Gitee is Now a Backup (Not the Primary)

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

- **International Accessibility**: Gitee’s servers are primarily located within China. For developers outside of mainland China, access can be slow, unstable, and in some cases, entirely blocked due to international network policies. This created a poor experience for a significant portion of the user base.
- **User Interface and Workflow**: The Gitee UI and workflow, while functional, are often perceived as dated and less aligned with the modern Git workflows that many international developers are accustomed to.

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

#### 4. The Solution: GitCode as the New Primary Repository

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

**The new primary repository is:** [https://gitcode.com/dvsxt/ap_ds](https://gitcode.com/dvsxt/ap_ds)

We believe this move provides the best of both worlds: a modern, globally accessible primary home for the project, while maintaining Gitee as a fast, stable backup for our users in China.

#### 5. The Future: A Dedicated Official ap_ds Repository

This is just the beginning. We have a major announcement: **We are planning to launch a dedicated, official code repository for ap_ds within the next three days!**

The new repository will be hosted under our official project domain, **[apds.top](https://apds.top)** , creating a permanent, independent home for the project's source code. This will serve as the ultimate, authoritative source, further insulating the project from the whims of third-party platforms. Stay tuned for this exciting development!

#### 6. The Permanent Home Strategy

Our goal is to establish a permanent, stable foundation for ap_ds. While GitCode serves as our primary platform today, we are mindful of the fact that even the best platforms can change their policies (as we saw with GitLab). Therefore, the upcoming dedicated repository on `apds.top` is planned as the **ultimate, permanent code repository**.

- **apds.top (Planned)**: This will be the **eternal, official source** of ap_ds, controlled entirely by the project.
- **GitCode**: Will remain the **primary, actively maintained public mirror**.
- **Gitee**: Will continue as the **dedicated mirror for users in China**.
- **GitHub**: There are no plans to return to GitHub in the short or medium term. In the very long term, if a new account is ever created, it would only serve as an additional backup mirror, not as a primary development hub.
- **GitLab (JihuLab)**: The repository there is abandoned. If the platform ever reintroduces a functional free tier for collaboration, or if the project's funding allows for a paid plan, we may reconsider, but there are no such plans at this time.

**Important Note for PyPI Users**: The documentation on PyPI currently still references GitLab as the primary source. This is because the PyPI project page requires a new release to update its long description. Since v3.0.0 LTS was just released, the next update will correct this. For the most current and accurate information, please always refer to the official project homepage at **[https://apds.top](https://apds.top)** or this latest documentation.

---

### About v2.4.2

Version 2.4.2 was accidentally uploaded with a development‑stage `player.py` file. While it technically works, it may contain minor quirks and is **not recommended for any real project**. It was meant to be a test release. Because the developer’s PyPI account is also locked due to the same 2FA issue, it cannot be yanked right now. Once the account is restored, it will be marked as deprecated, but the release will remain on PyPI for those curious to see what a “development slip” looks like. Consider it a curiosity – **do not use it in production.**

| Version | Release Date | Status | Recommendation |
|---------|--------------|--------|----------------|
| **v3.0.0 LTS** | March 22, 2026 | ✅ **LTS – Current Stable** | **Recommended for all users** |
| v2.4.2 | March 22, 2026 | ⚠️ Development mishap | Not recommended – for curiosity only |
| v2.4.1 | March 1, 2026 | ✅ Stable | Safe but superseded by v3.0.0 LTS |
| v2.4.0 | March 1, 2026 | ✅ Stable | Safe but superseded |
| v2.3.6 | February 27, 2026 | ✅ Stable | Safe but superseded |
| v2.3.5 | February 26, 2026 | ✅ Stable | Safe but superseded |
| v2.3.4 | February 10, 2026 | ✅ Stable | Safe but superseded |
| v2.3.3 | February 9, 2026 | ✅ Stable | Safe but superseded |
| v2.3.2 | February 9, 2026 | ❌ YANKED | Do not use |
| v2.3.1 | February 9, 2026 | ❌ YANKED | Do not use |
| v2.3.0 | January 31, 2026 | ❌ YANKED | Do not use |
| v2.2.0 | January 19, 2026 | ❌ YANKED | Do not use |

**Action Required:** Run `pip install --upgrade ap_ds` immediately to update to the **v3.0.0 LTS** release.

---

## Overview

ap_ds is a lightweight (2.5MB) Python audio library for playback and accurate metadata parsing of MP3, FLAC, OGG, and WAV files. It has zero external Python dependencies, utilizing only the Python standard library, and provides non‑blocking playback for smooth GUI applications.

**Key Features:**
- **Extreme Lightweight:** Only 2.5MB (Windows) / 3.36MB (macOS) complete solution
- **Zero Python Dependencies:** Uses only standard library
- **Accurate Metadata:** 100% for WAV/FLAC, 99.99% for OGG, >98% for MP3
- **Non‑blocking Playback:** Perfect for GUI applications
- **Cross‑Platform:** Windows, macOS, Linux, and embedded ARM64
- **DAP Recording System:** Automatic playback history with metadata only
- **LTS Support:** First Long‑Term Support release with 5‑year commitment

---

---
Contact & support

### 📧 Licensing inquiries
me@dvsyun.top or dvs6666@163.com · response within 7 business days

### 🛠️ Technical support
Gitee Issues · GitCode Issues · email (free during LTS period)

---

### Official Repositories & Project Sources

**✅ Primary (Recommended for all users):** [GitCode](https://gitcode.com/dvsxt/ap_ds) – Globally accessible, modern UI, actively maintained as the primary development hub.

**✅ Backup for China (Fast & Stable):** [Gitee](https://gitee.com/dssxt/ap_ds) – Fully maintained mirror for developers within China who require fast access. The UI and workflow are classic and stable.

**❌ Deprecated & Abandoned:**
- **[GitHub (dvs-web/ap_ds)](https://github.com/dvs-web/ap_ds)** – Deprecated due to a permanent account lockout. No longer maintained.
- **[GitLab (JihuLab)](https://jihulab.com/dvs/ap_ds)** – Abandoned due to platform policy changes that introduced paid login requirements for collaboration.

> **ℹ️ ap_ds v3.0.0 LTS – The first Long‑Term Support release**  
> This version is the culmination of all previous improvements and adds deterministic cleanup, hash‑verified downloads, and a 5‑year support commitment. The old GitHub repository (`dvs-web/ap_ds`) is deprecated and will not receive updates. The GitLab repository is abandoned. For future updates and contributions, please use the new official repositories: **GitCode (primary)** and **Gitee (China mirror)**.
>
> **For the developer’s personal page & blog:** [https://dvsx.top](https://dvsx.top) – a space where Dvs shares technology insights, life notes, and project philosophy.  
> For the dedicated **ap_ds project homepage**, please visit: [https://apds.top](https://apds.top) (official documentation, releases, and license center).

**🔗 Canonical URL:** [https://apds.top/](https://apds.top/)  
**📖 Blog & about author:** [dvsx.top](https://dvsx.top) – "DVS · Blog: recording tech & life, sharing knowledge and insights"
---

### About the author

**Developer:** Dvs (DvsXT)  
**Personal homepage & blog:** [https://dvsx.top](https://dvsx.top) – *DVS · Blog: recording technology & life, sharing knowledge and insights*  
**Author profile:** [https://dvsyun.top/me/dvs](https://dvsyun.top/me/dvs)  
**Email:** me@dvsyun.top · dvs6666@163.com

### ap_ds official portals

**🎵 ap_ds official website 1 (primary):** [https://apds.top](https://apds.top)  
**🌐 Official website 2 (mirror / docs):** [https://www.dvsyun.top/ap_ds](https://www.dvsyun.top/ap_ds)  
**📦 PyPI project page:** [https://pypi.org/project/ap_ds/](https://pypi.org/project/ap_ds/)

> 👉 The dedicated ap_ds project homepage ([apds.top](https://apds.top)) hosts the full documentation, license details, version changelog, and official releases. The author's personal blog ([dvsx.top](https://dvsx.top)) contains technical essays, life stories, and project background.

---
---
### Let's start using it!
## Installation

```bash
pip install ap_ds
```

To upgrade from an older version:

```bash
pip install --upgrade ap_ds
```

---

## Quick Start

```python
from ap_ds import AudioLibrary

# Initialize the library
lib = AudioLibrary()

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

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

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

---

## DAP Playlist System

Audio files are automatically recorded to DAP (Dvs Audio Playlist) upon playback:

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

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

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

DAP stores only metadata (path, duration, bitrate, channels), not audio data. Memory usage is approximately 150 bytes per recording.

---

## Platform Support

### Windows
- Automatic download of SDL2.dll and SDL2_mixer.dll with hash verification
- No manual configuration required
- Supports Windows 7 and later

### macOS
- Automatic download of SDL2.framework and SDL2_mixer.framework with hash verification
- No manual configuration required
- Supports macOS 10.9 and later

### Linux
**Intelligent Multi-Layer Import System:**
1. **System Library Check:** Uses system-installed SDL2 libraries
2. **User Configuration:** Checks saved paths from previous runs
3. **Automatic Installation:** Detects package manager and installs required packages
4. **Interactive Guidance:** Manual options if all else fails

**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:
- **Orange Pi 4 Pro** (Allwinner A733, 2xA76 + 6xA55 @ 2.0GHz)
- **Raspberry Pi 5** (BCM2712, 4xA76 @ 2.4GHz)

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

---
# ap_ds Audio Library – Complete API Reference

**Version: 3.0.0 LTS**  
**Document Date: March 22, 2026**  
**Project Homepage: https://apds.top**

---

## Table of Contents

1. [AudioLibrary Class – Complete API](#audiolibrary-class--complete-api)
   - [Initialization](#initialization)
   - [Playback Methods](#playback-methods)
   - [Control Methods](#control-methods)
   - [Volume Methods](#volume-methods)
   - [Fade & Transition Methods](#fade--transition-methods)
   - [Metadata Methods](#metadata-methods)
   - [DAP System Methods](#dap-system-methods)
   - [Resource Management](#resource-management)
   - [Internal Helper Methods](#internal-helper-methods)

2. [AudioParser Class – Metadata API](#audioparser-class--metadata-api)

3. [AudioInfo Module – Format-Specific Parsers](#audioinfo-module--format-specific-parsers)

4. [SDL2 Integration Layer](#sdl2-integration-layer)

5. [Constants Reference](#constants-reference)

6. [Exception Handling](#exception-handling)

---

## AudioLibrary Class – Complete API

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

### Initialization

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

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

**Parameters:**

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

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

**Example:**
```python
from ap_ds import AudioLibrary

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

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

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

---

### Playback Methods

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

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

**Parameters:**

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

Note: AAC files support metadata parsing, but due to SDL2 limitations, we are unable to play or perform other operations on them.

**Returns:**  
`int` – Audio ID (AID) that uniquely identifies this playback instance. This ID can be used with all control methods.

**Raises:**
- `FileNotFoundError`: If the file does not exist.
- `RuntimeError`: If the file format is unsupported or the audio cannot be loaded.
- `RuntimeError`: If playback fails (e.g., no available channels).

**Behavior by File Type:**

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

**Example:**
```python
# Play a song once from the beginning
aid = lib.play_from_file("song.mp3")

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

# Start playback from 30 seconds into the track
aid = lib.play_from_file("podcast.mp3", start_pos=30.0)

# Infinite loop (background music)
aid = lib.play_from_file("ambient.ogg", loops=-1)
```

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

---

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

**Description:**  
Plays an audio file that has already been loaded into memory via `new_aid()`. This method is faster than `play_from_file()` for repeated playback of the same file because the file is already cached.

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

**Returns:** `int` – Audio ID (AID).

**Raises:**
- `ValueError`: If the file has not been loaded with `new_aid()`.
- `RuntimeError`: If playback fails.

**Example:**
```python
# Pre-load sound effects
lib.new_aid("gunshot.wav")
lib.new_aid("explosion.wav")

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

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

---

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

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

**Parameters:**

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

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

**Raises:**
- `FileNotFoundError`: If the file does not exist.
- `RuntimeError`: If the file format is unsupported or loading fails.

**Example:**
```python
# Cache frequently used sounds
sounds = {
    'hit': lib.new_aid("hit.wav"),
    'jump': lib.new_aid("jump.wav"),
    'coin': lib.new_aid("coin.wav")
}

# Later, play from memory
lib.play_from_memory(sounds['hit'])
```

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

---

### Control Methods

#### `play_audio(aid: int) -> None`

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

**Parameters:**

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

**Raises:**
- `ValueError`: If the AID is invalid or the audio is not paused.

**Example:**
```python
aid = lib.play_from_file("song.mp3")
time.sleep(2)
lib.pause_audio(aid)      # Pause after 2 seconds
time.sleep(1)
lib.play_audio(aid)       # Resume after 1 second pause
```

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

---

#### `pause_audio(aid: int) -> None`

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

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

**Raises:**
- `ValueError`: If the AID is invalid or the audio is already paused.

**Example:**
```python
aid = lib.play_from_file("song.mp3")
time.sleep(5)
lib.pause_audio(aid)
```

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

---

#### `stop_audio(aid: int) -> float`

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

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

**Returns:** `float` – The total duration played (in seconds) before stopping.

**Raises:**
- `ValueError`: If the AID is invalid (returns 0.0 silently? Actually raises ValueError per code).

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

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

---

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

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

**Parameters:**

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

**Raises:**
- `ValueError`: If the AID is invalid.
- `RuntimeError`: If the audio is not seekable (sound effect mode).

**Example:**
```python
# Seek to 30 seconds into the track
lib.seek_audio(aid, 30.0)
```

**Internal Behavior (Music):**
1. Stops current playback with `Mix_HaltMusic()`.
2. Reloads the music file with `Mix_LoadMUS()`.
3. Plays from the beginning with `Mix_PlayMusic()`.
4. If `Mix_SetMusicPosition` is available, seeks to the target position.
5. Updates `_channel_info` with new start time.

**Internal Behavior (Sound Effect):**
1. Stops current playback with `Mix_HaltChannel()`.
2. Re‑plays the cached chunk with `Mix_PlayChannel()`.
3. This effectively starts from the beginning; seeking is not supported.

---

### Volume Methods

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

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

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `aid` | `int` | Audio ID. |
| `volume` | `int` | Volume value (0–128). Values outside this range are clamped. |

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

**Example:**
```python
# Set volume to 50% (64 out of 128)
lib.set_volume(aid, 64)

# Mute
lib.set_volume(aid, 0)

# Maximum
lib.set_volume(aid, 128)
```

**Internal Behavior:**
1. Finds the channel for the AID.
2. Clamps volume to [0, 128].
3. If music: calls `Mix_VolumeMusic(volume)`.
4. If sound effect: calls `Mix_Volume(channel, volume)`.

---

#### `get_volume(aid: int) -> int`

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

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

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

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

**Internal Behavior:**
1. Finds the channel for the AID.
2. If music: calls `Mix_VolumeMusic(-1)` (‑1 means get without setting).
3. If sound effect: calls `Mix_Volume(channel, -1)`.

---

### Fade & Transition Methods

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

**Description:**  
Fades in music over the specified duration. This method stops any currently playing music and starts the specified track with a fade‑in effect. Works only for music‑mode files.

**Parameters:**

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

**Returns:** `bool` – `True` if fade‑in started successfully, `False` otherwise.

**Raises:**
- Prints error message if AID not found or not a music file.

**Example:**
```python
# Fade in over 2 seconds
lib.fadein_music(aid, loops=-1, ms=2000)

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

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

---

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

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

**Parameters:**

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

**Returns:** `bool` – `True` if successful, `False` otherwise.

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

**Internal Behavior:**
1. Checks if `Mix_FadeInMusicPos` is available in the loaded SDL2_mixer.
2. Same as `fadein_music()`, but calls `Mix_FadeInMusicPos()` with the position parameter.

---

#### `fadeout_music(ms: int = 0) -> bool`

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

**Parameters:**

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

**Returns:** `bool` – `True` if fade‑out started, `False` if no music was playing.

**Example:**
```python
# Smooth fade out over 3 seconds
lib.fadeout_music(ms=3000)

# Stop immediately
lib.fadeout_music(ms=0)
```

**Internal Behavior:**
1. Calls `Mix_FadeOutMusic(ms)`.
2. Returns `True` if the return value is `1` (success), `False` otherwise.

---

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

**Description:**  
Checks whether any music is currently playing.

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

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

**Internal Behavior:** Calls `Mix_PlayingMusic()`.

---

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

**Description:**  
Checks whether the music is paused.

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

**Internal Behavior:** Calls `Mix_PausedMusic()`.

---

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

**Description:**  
Returns the current fade state of the music.

**Returns:** `int` – One of:
- `0` (`MUS_NO_FADING`): No fade in progress.
- `1` (`MUS_FADING_OUT`): Fading out.
- `2` (`MUS_FADING_IN`): Fading in.

**Example:**
```python
state = lib.get_music_fading()
if state == 1:
    print("Fading out...")
```

**Internal Behavior:** Calls `Mix_FadingMusic()`.

---

### Metadata Methods

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

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

**Parameters:**

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

**Returns:**
- On success: `int` – duration in seconds (rounded down).
- On failure: `Tuple[int, str]` – `(0, error_message)`.

**Example:**
```python
# By file path
duration = lib.get_audio_duration("song.mp3", is_file=True)

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

**Internal Behavior:**
1. If `is_file` is `True`, calls `_get_duration_by_filepath(str(source))`.
2. If `is_file` is `False`, looks up the file path from `_aid_to_filepath` and then calls `_get_duration_by_filepath()`.

---

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

**Description:**  
Returns complete metadata for the audio file: duration, sample rate, channels, bitrate, and format.

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

**Returns:** `Dict` or `None` (if parsing fails).

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

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

---

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

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

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `file_path` | `str` | Path to the audio file. |

**Returns:** `Dict` or `None`.

---

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

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

**Parameters:**

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

**Returns:** `Dict` or `None`.

**Raises:**
- `ValueError`: If the AID is invalid (returns `None`).

---

### DAP System Methods

#### `save_dap_to_json(save_path: str) -> bool`

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

**Parameters:**

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

**Returns:** `bool` – `True` if successful, `False` otherwise.

**Raises:**
- `ValueError`: If the file extension is not `.ap-ds-dap`.

**Example:**
```python
if lib.save_dap_to_json("my_playlist.ap-ds-dap"):
    print("Playlist saved!")
else:
    print("Failed to save")
```

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

---

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

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

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

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

---

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

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

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

---

#### `_add_to_dap_recordings(file_path: str) -> None`

**Description:**  
Internal method that adds a file to the DAP recordings list. Called automatically by `play_from_file()`, `play_from_memory()`, and `new_aid()`.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `file_path` | `str` | Path to the audio file. |

**Internal Behavior:**
1. Calls `get_audio_metadata_by_path(file_path)` to extract metadata.
2. If metadata is available, creates a record with `path`, `duration`, `bitrate`, and `channels`.
3. Checks for duplicates (by path) before appending.

---

### Resource Management

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

**Description:**  
Frees all cached audio data (both sound effects and music) from memory. After calling this, any subsequent playback will require reloading files from disk.

**Example:**
```python
lib.clear_memory_cache()
print("All cached audio cleared")
```

**Internal Behavior:**
1. Iterates through `_audio_cache` and calls `Mix_FreeChunk()` on each.
2. Iterates through `_music_cache` and calls `Mix_FreeMusic()` on each.
3. Clears both dictionaries.

---

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

**Description:**  
Registered with `atexit` to clean up all resources when the Python interpreter exits. This method:
- Clears memory cache.
- Closes the audio device with `Mix_CloseAudio()`.
- Shuts down SDL with `SDL_Quit()`.

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

---

### Internal Helper Methods

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

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

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

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

---

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

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

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

---

#### `_is_music_file(file_path: str) -> bool`

**Description:**  
Determines whether a file should be treated as music (supports seek) or as a sound effect (no seek). The decision is based on:
- File extension (`.mp3`, `.ogg`, `.flac` → music).
- For WAV files: compares duration against `WAV_THRESHOLD`.

**Returns:** `bool` – `True` if music mode, `False` if sound effect mode.

---

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

**Description:**  
Internal method that performs the actual seek operation for a given channel.

---

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

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

---

#### `_get_file_duration(file_path: str) -> float`

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

---

## AudioParser Class – Metadata API

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

### `get_audio_parser() -> AudioParser`

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

**Example:**
```python
from ap_ds import get_audio_parser

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

---

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

**Description:**  
Returns metadata for the given audio file. Delegates to the format‑specific parser in `audio_info.py`.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `file_path` | `str` | Path to the audio file. |

**Returns:** `Dict` or `None` (if parsing fails).

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

---

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

**Description:**  
Returns the duration of the audio file in seconds (rounded down).

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

---

### Legacy Methods (for backward compatibility)

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

- `get_ogg_duration(file_path)`
- `get_flac_duration(file_path)`
- `get_mp3_duration(file_path)`
- `get_wav_duration(file_path)`
- `get_duration_by_extension(file_path)`

---

## AudioInfo Module – Format-Specific Parsers

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

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

### `StreamInfo` Class

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

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

---

### `WAVFile` Class

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

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

**Method:**
1. Reads `RIFF` header and `WAVE` identifier.
2. Scans chunks for `fmt ` (format) and `data`.
3. From `fmt `: extracts `channels`, `sample_rate`, `block_align`.
4. From `data`: extracts `data_size`.
5. Computes `total_frames = data_size // block_align`.
6. Computes `length = total_frames / sample_rate`.
7. Computes `bitrate = sample_rate * block_align * 8 // channels`.

---

### `FLACFile` Class

**Parser:** Reads the STREAMINFO block (must be present in all FLAC files).

**Accuracy:** 100% (from metadata).

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

---

### `MP3File` Class

**Parser:** Frame‑by‑frame scanner that counts frames and accumulates samples.

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

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

---

### `OGGFile` Class

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

**Accuracy:** 99.99% (granule position is exact, but may be slightly off if the file is truncated).

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

---

### `AACFile` Class

**Parser:** Reads ADTS (Audio Data Transport Stream) frames, accumulates samples.

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

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

---

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

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

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `filename` | `str` | Path to the audio file. |

**Returns:** Instance of `WAVFile`, `FLACFile`, `MP3File`, `OGGFile`, or `AACFile`.

**Raises:** `ValueError` if the format is unsupported.

---

## SDL2 Integration Layer

The `player.py` module contains the low‑level bindings to SDL2 and SDL2_mixer using `ctypes`. All functions are bound with proper `argtypes` and `restype` to ensure cross‑platform stability.

### Global SDL2 Functions

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

### Global SDL2_mixer Functions

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

---

## Constants Reference

### SDL Initialization Flags

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

### Audio Formats

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

### Mixer Initialization Flags

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

### Music Type Constants (Returned by `Mix_GetMusicType`)

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

### Fade State Constants

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

---

## Exception Handling

All methods in `AudioLibrary` raise exceptions in the following circumstances:

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

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

---

## End of API Reference

This document covers all public and internal APIs of the ap_ds library version 3.0.0 LTS. For additional examples and usage patterns, refer to the main README.md.

---
## Environment Variables

### 1. AP_DS_HIDE_SUPPORT_PROMPT

**Purpose:** Controls the display of the startup banner when the library is imported.

**Default:** Not set (banner is shown)

**Behavior:**
- When set to `1` (any non‑empty string that evaluates to `1`), the startup message is completely suppressed.
- This is useful for GUI applications, daemons, or any environment where console output should be minimal.

**Usage:**
```bash
# Linux/macOS
export AP_DS_HIDE_SUPPORT_PROMPT=1

# Windows Command Prompt
set AP_DS_HIDE_SUPPORT_PROMPT=1

# Windows PowerShell
$env:AP_DS_HIDE_SUPPORT_PROMPT=1
```

**Code Example:**
```python
import os
os.environ['AP_DS_HIDE_SUPPORT_PROMPT'] = '1'
import ap_ds  # No banner output
```

### 2. AP_DS_WAV_THRESHOLD

**Purpose:** Determines whether a WAV file is played as a sound effect (no seek capability) or as a music file (full seek and fade support).

**Default:** `6` seconds

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

**Why this exists:**  
SDL2_mixer has two distinct playback mechanisms: sound effects (channels) and music. Sound effects are lightweight and ideal for short clips, but they cannot be seeked or faded. Music tracks support these advanced features but consume slightly more resources. This threshold allows you to automatically choose the appropriate mechanism based on file duration.

**Usage:**
```bash
# Set threshold to 10 seconds
export AP_DS_WAV_THRESHOLD=10

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

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

**Validation Rules:**
```python
# Internal validation logic
if WAV_THRESHOLD >= 30:
    WAV_THRESHOLD = 6  # Prevent memory issues
elif WAV_THRESHOLD < 0:
    WAV_THRESHOLD = 6
```

### 3. AP_DS_SDL2_PATH and AP_DS_SDL2_MIXER_PATH (Linux only)

**Purpose:** Specify custom paths to SDL2 and SDL2_mixer shared libraries on Linux systems. These variables are used when the system‑installed libraries are not found or when you have compiled your own versions.

**Default:** Not set; the library searches standard system paths (`/usr/lib`, `/usr/local/lib`, etc.) and package‑manager locations.

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

**Usage:**
```bash
export AP_DS_SDL2_PATH=/usr/local/lib/libSDL2.so
export AP_DS_SDL2_MIXER_PATH=/usr/local/lib/libSDL2_mixer.so
```

**Why this exists:**  
Linux distributions have diverse package managers and library locations. Some users may compile SDL2 from source for performance or compatibility reasons. These environment variables provide a direct way to point the library to exactly the files you want to use.

**Configuration File:**  
After first successful manual configuration, the paths are saved to:
```
~/.config/ap_ds/sdl_paths.conf
```
The file contains:
```
SDL2_PATH=/path/to/libSDL2.so
SDL2_MIXER_PATH=/path/to/libSDL2_mixer.so
```

**Security Note:** The configuration file is stored in the user's home directory and is only readable by the user. It does not contain any sensitive information beyond library paths.

---

## v3.0.0 LTS – Detailed Release Overview

### What is an LTS Release?

Long‑Term Support (LTS) releases are a common practice in the software industry, particularly for libraries and frameworks that are used as foundations for other products. An LTS release is a version that will receive **security updates, critical bug fixes, and maintenance patches for a defined period**, without introducing new features or breaking changes.

For ap_ds, the LTS designation means:

- **Stability guarantee:** The API will not change. Your code written for v3.0.0 LTS will continue to work for the entire support period.
- **Security updates:** Any discovered vulnerabilities will be patched and backported to v3.0.0 LTS.
- **Critical bug fixes:** Major bugs (crashes, memory leaks, severe functionality issues) will be fixed.
- **No new features:** Feature development will continue in newer versions, but those features will not be added to the LTS branch. This ensures that upgrading is a conscious decision, not a forced migration.

### Why v3.0.0 LTS Exists

The decision to create an LTS release was driven by user demand. Over the years, we have seen ap_ds used in:

- **Commercial software:** Integrated into paid applications where stability is paramount.
- **Embedded systems:** Deployed on devices that may not be updated frequently.
- **Educational environments:** Used in curriculum where consistency is important.
- **Personal projects:** Developers who want a reliable baseline.

For these users, the ability to pin a version and receive only critical updates is essential. v3.0.0 LTS fulfills that need.

### Detailed Technical Changes in v3.0.0 LTS

#### 1. Deterministic Resource Cleanup

**Problem in earlier versions:**  
The `AudioLibrary` class relied on Python's `__del__` finalizer to release SDL2 resources when the object was garbage‑collected. However, `__del__` is not guaranteed to run in all circumstances, especially during interpreter shutdown or when exceptions occur. This could lead to:

- Audio device handles remaining open.
- SDL2 mixer channels not being freed.
- Warning messages about unclosed resources.

**Solution:**  
v3.0.0 LTS uses `atexit.register()` to install a cleanup function that is **guaranteed** to run when the Python interpreter exits normally. Additionally, the cleanup logic is now **explicit** and **deterministic**:

- All cached `Mix_Chunk` and `Mix_Music` objects are freed.
- `Mix_CloseAudio()` is called to shut down the audio device.
- `SDL_Quit()` is called to clean up SDL2 subsystems.

This ensures that even if your program terminates unexpectedly (e.g., via `sys.exit()` or unhandled exception), the at‑exit handler will still run and release all resources.

**Implementation snippet:**
```python
def cleanup_function():
    self.clear_memory_cache()
    Mix_CloseAudio()
    SDL_Quit()

atexit.register(self.cleanup_function)
```

**Impact:** No more resource leaks, no more warnings, and cleaner integration with long‑running processes.

#### 2. Hash‑Verified Downloads (Security Hardening)

**Problem in earlier versions:**  
When SSL certificate verification failed (common on older Windows systems or behind corporate proxies), the library would fall back to an unverified connection. This was necessary for usability but introduced a theoretical risk: if a man‑in‑the‑middle attack occurred during that fallback, the downloaded DLL could be replaced with a malicious version.

**Solution:**  
v3.0.0 LTS adds a **cryptographic hash check** to every downloaded file. The expected SHA‑256 hashes are hardcoded into the source:

```python
FILE_HASHES = {
    "SDL2.dll": "520d0459b91efa32fbccf9027a9ca1fc5aae657e679ce8e90f179f9cf5afd279",
    "SDL2_mixer.dll": "2a0fc5e9f72c2eaec3240cb82b7594a58ccda609485981f256b94d0a4dd8d6f8",
    "SDL2.dmg": "2bf2cb8f6b44d584b14e8d4ca7437080d1d968fe3962303be27217b336b82249",
    "SDL2_mixer.dmg": "d74052391ee4d91836bf1072a060f1d821710f3498a54996c66b9a17c79a72d1",
}
```

When a file is downloaded (whether with SSL verification or without), the library:

1. Calculates the SHA‑256 hash of the downloaded content.
2. Compares it to the expected hash.
3. If they match, the file is saved and used.
4. If they do not match, the download is rejected and retried (up to 3 times).

**Security implications:**  
Even if a malicious actor manages to intercept the download and replace the file, they cannot produce a file with the same SHA‑256 hash without breaking the cryptographic algorithm (computationally infeasible). The hash is stored in the source code, which is itself distributed via PyPI and verified by `pip`'s own integrity mechanisms.

**Performance impact:**  
Hash calculation adds a trivial overhead (a few milliseconds for files up to 3MB). This is negligible compared to network latency.

#### 3. Complete Test Coverage

**Tested environments:**

| Platform | Architecture | Python Versions | SDL2 Source |
|----------|--------------|-----------------|-------------|
| Windows 7 | x86_64 | 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 | Auto‑downloaded |
| Windows 10 | x86_64 | 3.7 – 3.13 | Auto‑downloaded |
| Windows 11 | x86_64 | 3.7 – 3.13 | Auto‑downloaded |
| macOS 10.15 | Intel | 3.7 – 3.13 | Auto‑downloaded |
| macOS 11 | Apple Silicon (Rosetta) | 3.7 – 3.13 | Auto‑downloaded |
| macOS 14 | Apple Silicon (native) | 3.7 – 3.13 | Auto‑downloaded |
| Ubuntu 20.04 | x86_64 | 3.7 – 3.13 | System libraries |
| Ubuntu 22.04 | ARM64 | 3.7 – 3.13 | System libraries |
| Debian 11 | x86_64 | 3.7 – 3.13 | System libraries |
| Fedora 38 | x86_64 | 3.7 – 3.13 | System libraries |
| Arch Linux | x86_64 | 3.7 – 3.13 | System libraries |
| Orange Pi 4 Pro | ARM64 | 3.10 | System libraries |
| Raspberry Pi 5 | ARM64 | 3.11 | System libraries |

**Test categories:**
- **Library loading:** Successful import on all platforms, fallback behavior on network failure.
- **Playback:** MP3, FLAC, OGG, WAV formats; variable bitrate; concurrent playback.
- **Seeking:** Accuracy within ±0.1 seconds; boundary conditions; seek during pause/stop.
- **Volume control:** Range 0‑128; persistence across playbacks.
- **DAP system:** Recording deduplication; JSON save/load; memory cleanup.
- **Resource cleanup:** No memory leaks after 10,000 play/stop cycles; at‑exit handler verification.
- **Error handling:** Invalid file paths, corrupted files, missing SDL2 libraries.

**Results:** All tests pass with zero failures. Memory growth after 10,000 cycles is under 4MB.

### LTS Support Commitment and Additional License Terms

#### Support Period

- **Start Date:** March 22, 2026
- **End Date:** March 22, 2031

During this period, the following services are provided **free of charge**:

- **Security updates:** Any vulnerability discovered in the LTS branch will be patched and released as a minor version update (e.g., v3.0.1, v3.0.2, etc.). Users can upgrade to the latest LTS patch without changing any code.
- **Critical bug fixes:** Bugs that cause crashes, memory leaks, or severe functionality loss will be fixed and backported.
- **Technical support:** Questions and issues can be submitted via Gitee Issues, GitLab Issues, or email. Response time is within 7 business days for all inquiries, and within 48 hours for critical issues.

#### What is NOT Covered

- **New features:** Feature development will continue in the main branch, but those features will not be added to the LTS branch.
- **Non‑critical bugs:** Minor bugs that do not affect functionality may not be fixed in the LTS branch.
- **Platform‑specific issues:** If a new operating system version introduces incompatibilities, support will be evaluated on a case‑by‑case basis.

#### LTS Additional License Terms

The DVS Audio Library Open Source License Version 2.0 applies to v3.0.0 LTS, with the following clarifications:

1. **Commercial Use:** The LTS version may be used in commercial products, cloud services, and SaaS platforms without any additional licensing fees. Attribution requirements remain as specified in Section 3.1 of the license.

2. **Redistribution:** You may redistribute v3.0.0 LTS as part of your own applications. You must include the original copyright notices and provide attribution.

3. **Modified Versions:** If you modify the source code and distribute your modified version, you must use an independent brand name and clearly indicate that it is not the official LTS version. You are responsible for supporting your modified version.

4. **Support for Modified Versions:** The official LTS support (security updates, bug fixes) applies only to the unmodified source code as distributed by the author. If you modify the code, you assume all responsibility for maintaining it.

5. **LTS Mark:** The “LTS” designation is a trademark of the project. You may not call your modified version “LTS” or imply that it has the same support commitment as the official LTS release.

---

## Technical Architecture

### Core Components

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

### 1. Player Module (`player.py`)

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

**Key Classes and Functions:**

- `AudioLibrary`: Main class that users interact with.
  - **Initialization:** `__init__(frequency, format, channels, chunksize)` – sets up SDL audio and mixer.
  - **Playback:** `play_from_file()`, `play_from_memory()`, `new_aid()`
  - **Control:** `pause_audio()`, `play_audio()`, `stop_audio()`, `seek_audio()`
  - **Volume:** `set_volume()`, `get_volume()`
  - **Fade Effects:** `fadein_music()`, `fadein_music_pos()`, `fadeout_music()`, `is_music_playing()`, `is_music_paused()`, `get_music_fading()`
  - **Metadata:** `get_audio_duration()`, `get_audio_metadata()`
  - **DAP:** `save_dap_to_json()`, `get_dap_recordings()`, `clear_dap_recordings()`

- **Internal Data Structures:**
  - `self._audio_cache`: `dict[str, Mix_Chunk]` – cached sound effects (short files).
  - `self._music_cache`: `dict[str, Mix_Music]` – cached music tracks.
  - `self._channel_info`: `dict[int, dict]` – active playback sessions, keyed by SDL channel ID.
  - `self._aid_to_filepath`: `dict[int, str]` – AID (Audio ID) to file path mapping.
  - `self._dap_recordings`: `list[dict]` – DAP history records.

- **Platform Abstraction:**
  - `import_sdl2()`: Intelligently loads SDL2 libraries on Windows, macOS, and Linux.
  - `download_sdl_libraries()`: Downloads platform‑specific binaries with hash verification.
  - `check_sdl_libraries_exist()`: Verifies presence of required files.
  - `load_sdl2_from_directory()`: Loads libraries from the package directory or system paths.

- **SDL2 Binding:**
  - All SDL2 and SDL2_mixer functions are bound via `ctypes`.
  - Function prototypes (`argtypes`, `restype`) are set for all platforms to prevent segmentation faults.
  - The binding code is unconditional – it runs on every platform, ensuring stability.

### 2. Metadata Parser Module (`audio_parser.py`)

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

**Key Functions:**
- `get_audio_parser()`: Singleton factory that returns an instance of `AudioParser`.
- `AudioParser.get_audio_metadata(file_path)`: Returns a dictionary with `path`, `format`, `duration`, `length`, `sample_rate`, `channels`, `bitrate`.

**Fallback Behavior:** If a format‑specific parser fails, the library returns `None`. No exceptions are raised during metadata parsing; errors are logged.

### 3. Format‑Specific Parsers (`audio_info.py`)

**Purpose:** Low‑level parsers for each supported format. These modules are written in pure Python and do not depend on external libraries.

**Supported Formats and Parsing Methods:**

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

**Why not 100% for MP3?**  
MP3 frames can have variable bitrates, and the Xing/LAME header (which contains accurate frame counts) is optional and not always present. The frame‑by‑frame scanner is the most reliable method, but due to the way MP3 frames are packed, the last frame may be incomplete, causing a small error (<1%). For most use cases, this is acceptable.

### 4. DAP (Dvs Audio Playlist) System

**Purpose:** Automatically records playback history with metadata only, enabling applications to build listening histories without storing audio data.

**Workflow:**
1. User calls `play_from_file()` or `play_from_memory()`.
2. The method calls `_add_to_dap_recordings(file_path)`.
3. `_add_to_dap_recordings()` retrieves metadata via `get_audio_metadata_by_path()`.
4. A record is created with `path`, `duration`, `bitrate`, and `channels`.
5. The record is added to `self._dap_recordings` if not already present (deduplication).
6. User can call `save_dap_to_json()` at any time to persist the list to a `.ap_ds-dap` JSON file.

**Memory Characteristics:**
- Each record: approximately 150 bytes (path string + three integers).
- 10,000 records: ~1.5 MB RAM, ~2‑3 MB JSON file.
- No audio data is stored.

**File Format:**
```json
[
  {
    "path": "/Music/song.mp3",
    "duration": 240,
    "bitrate": 320000,
    "channels": 2
  }
]
```

### 5. SDL2 Integration and ctypes Binding

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

**Function Prototypes:**  
For every SDL2 and SDL2_mixer function used, we set `argtypes` and `restype`. This is critical for cross‑platform stability:

- Without `argtypes`, Python may pass arguments incorrectly, leading to segmentation faults.
- Without `restype`, Python may misinterpret the return value (especially for pointer types).

In early versions (v2.2.x and earlier), these prototypes were only set on Windows, causing crashes on macOS and Linux. v2.3.3 fixed this by making the prototype definitions unconditional.

**Memory Management:**  
SDL2 allocates memory for `Mix_Chunk` and `Mix_Music` objects. The library stores them in caches and frees them with `Mix_FreeChunk()` and `Mix_FreeMusic()` when no longer needed. The at‑exit handler ensures they are freed even if the user forgets to call `clear_memory_cache()`.

### 6. Cross‑Platform Download and Extraction

**Windows:**  
- Downloads `SDL2.dll` and `SDL2_mixer.dll` from `https://dvsyun.top/ap_ds/download/SDL2` and `.../SDL2_M`.
- After download, calculates SHA‑256 hash and compares to expected values.
- Saves DLLs to the package directory (`site-packages/ap_ds/`).

**macOS:**  
- Downloads `SDL2.dmg` and `SDL2_mixer.dmg`.
- Mounts the DMG using `hdiutil`.
- Searches for `SDL2.framework` and `SDL2_mixer.framework` in the mounted volume.
- Copies the frameworks to the package directory.
- Unmounts the DMG and deletes the temporary file.

**Linux:**  
- Does not download libraries automatically. Instead, relies on system package manager or user‑provided paths.
- Four‑layer fallback: system libraries → user config → automatic package manager installation → interactive guidance.

**Hash Verification:**  
For all downloaded files, SHA‑256 is calculated and compared to a hardcoded dictionary. If the hash does not match, the download is rejected and retried. After three failures, an exception is raised.

### 7. Error Handling Philosophy

ap_ds follows a **fail‑fast, fail‑clearly** philosophy:

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

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

**Why not complex error messages with suggestions?**  
Developers are expected to read error messages and use common sense. Adding suggestions (e.g., “try reinstalling SDL2”) adds maintenance burden and may be incorrect in edge cases. The library focuses on being correct, not prescriptive.

---

## Frequently Asked Questions

### 1. Which version should I use for production?

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

### 2. Can I use v3.0.0 LTS in commercial products?

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

### 3. Why is the library so small?

ap_ds focuses precisely on playback and parsing for the four most common formats, avoiding the bloat of editing/transcoding features. It is built on the efficient SDL2 C library and uses only the Python standard library. The total size on Windows is 2.5MB, and on macOS it is 3.36MB.

### 4. How accurate is MP3 duration parsing?

MP3 duration parsing is >98% accurate. This is due to the format's variable header complexities. For WAV and FLAC we guarantee 100% accuracy, and for OGG 99.99%.

### 5. Does it work on embedded devices?

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

### 6. What are the system requirements?

- **Windows:** Windows 7+, Python 3.7+
- **macOS:** macOS 10.9+, Python 3.7+
- **Linux:** Modern distributions, Python 3.7+, SDL2 libraries (automatically installed via package manager if possible)
- **Embedded:** ARM64 devices with Ubuntu 22.04 or similar

### 7. How do I suppress the startup banner?

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

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

### 8. How do I configure the WAV threshold?

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

```bash
export AP_DS_WAV_THRESHOLD=10  # Linux/macOS
set AP_DS_WAV_THRESHOLD=10     # Windows Command Prompt
$env:AP_DS_WAV_THRESHOLD=10    # Windows PowerShell
```

### 9. How do I provide custom SDL2 paths on Linux?

Set `AP_DS_SDL2_PATH` and `AP_DS_SDL2_MIXER_PATH` to the full paths of the shared libraries. These paths are automatically saved for future runs.

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

### 10. What if SSL verification fails on Windows?

The library will fall back to downloading without certificate verification, but **then validates the SHA‑256 hash** of the downloaded file. If the hash matches, the file is safe. If the hash does not match, the download is rejected. This is more secure than the previous behavior (which simply accepted any file).

### 11. Is the hash‑verified download safe?

Yes. The hash is hardcoded in the source code. Even if the download is intercepted, the attacker would need to produce a file with the same SHA‑256 hash, which is computationally infeasible. The hash itself is distributed via PyPI, which uses its own integrity checks (package signing).

### 12. Can I use ap_ds in a multithreaded application?

Yes, but you must ensure that calls to the `AudioLibrary` instance are serialized. SDL2 and SDL2_mixer are not thread‑safe in all respects. For best results, create one `AudioLibrary` instance and call its methods from a single thread, or use a lock to serialize access. The library itself does not create additional threads except during download operations (which are isolated).

### 13. How do I get support for the LTS version?

Free support is available via:
- **GitCode Issues (Primary)**: [https://gitcode.com/dvsxt/ap_ds/issues](https://gitcode.com/dvsxt/ap_ds/issues) – Recommended for all users, globally accessible
- **Gitee Issues (China Mirror)**: [https://gitee.com/dssxt/ap_ds/issues](https://gitee.com/dssxt/ap_ds/issues) – Fast and stable for users within China
- **Email**: me@dvsyun.top or dvs6666@163.com

Response time: within 7 business days for standard inquiries, within 48 hours for critical issues.

> **Note**: The GitLab (JihuLab) repository has been abandoned due to platform policy changes. Please use GitCode or Gitee for all support requests.
### 14. What happens after the LTS support period ends (March 22, 2031)?

After the LTS period, the v3.0.0 branch will enter **end‑of‑life**. No further updates will be provided. Users are encouraged to migrate to a newer LTS release (if available) or the latest stable version at that time. The source code will remain available, but security issues will not be patched.

### 15. Can I modify the source code and still call it “ap_ds”?

No. Modified versions must use an independent brand name (see Section 3.2 of the license). You can still use the code, but you cannot use the original project name, logo, or imply that your version is the official release.

### 16. How do I save DAP records to a file?

Call `save_dap_to_json()` with a filename ending in `.ap_ds-dap`:

```python
success = lib.save_dap_to_json("my_history.ap_ds-dap")
if success:
    print("Saved!")
```

The file is in JSON format and can be loaded with any JSON parser.

### 17. What is the maximum number of concurrent audio streams?

This depends on the SDL2_mixer configuration. The default is 8 channels (mixer channels) for sound effects, plus one music channel. You can increase the number of channels with `Mix_AllocateChannels()` (exposed as `lib._mix_lib.Mix_AllocateChannels()`), but note that each channel consumes additional resources. For most applications, the default is sufficient.

### 18. Can I play audio from memory (bytes) instead of a file?

Yes, but it requires pre‑loading the file with `new_aid()` and then using `play_from_memory()`. There is no direct API for playing from a `bytes` object because SDL2_mixer expects a file path. However, you can write the bytes to a temporary file and then play it.

### 19. Why does the library print “AP_DS © - Audio Library By DVS v3.0.0 | https://apds.top” on import?

This is the welcome banner. It can be suppressed by setting `AP_DS_HIDE_SUPPORT_PROMPT=1` before importing the library. The banner serves to:
- Inform users of the version they are using.
- Provide a direct link to the official website for documentation and support.
- Acknowledge the project’s authorship.

### 20. How do I report a security vulnerability?

Please email the author directly at me@dvsyun.top. Do not create a public issue for security vulnerabilities. The author will respond within 7 business days with an acknowledgment and a timeline for a fix.

---

## Version History

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

**This is the first LTS release.** It introduces:
- **Deterministic resource cleanup** (replaced `__del__` with at‑exit handlers).
- **Hash‑verified downloads** – every downloaded SDL2 library is validated against a hardcoded SHA‑256 hash.
- **Complete test coverage** on all platforms with zero memory leaks.
- **5‑year support period** with free technical support.

**No breaking changes** – fully backward‑compatible with v2.x.

---

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

**This version was accidentally uploaded with a development‑stage `player.py`.** While it works for basic playback, it may contain minor quirks and is **not recommended for production**. It remains on PyPI because the developer’s PyPI account is temporarily locked due to a 2FA issue; it will be deprecated once the account is restored. **Do not use this version in real projects.**

---

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

This version updates the PyPI documentation to fully reflect the new features introduced in v2.4.0, including detailed API descriptions, usage examples, and environment variable documentation.

**Changes:**
- Updated PyPI project description with complete 2.4.0 feature documentation
- Added detailed examples for all new fade functions
- Documented `AP_DS_HIDE_SUPPORT_PROMPT` environment variable
- Improved quick start guide with common usage patterns

**Note:** This release contains no code changes from v2.4.0—only documentation improvements.

---

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

**✅ This version introduces professional audio transitions and significant internal engineering upgrades.**

---

#### 🎵 New Audio Control Features

**Fade In/Out Functions:**

| Function | Description |
|----------|-------------|
| `fadein_music(aid, loops=-1, ms=0)` | Fade in music over specified milliseconds |
| `fadein_music_pos(aid, loops=-1, ms=0, position=0.0)` | Fade in from specific position |
| `fadeout_music(ms=0)` | Fade out currently playing music |
| `is_music_playing()` | Check if music is currently playing |
| `is_music_paused()` | Check if music is paused |
| `get_music_fading()` | Get current fade state (fading in/out/none) |

**Implementation Details:**
```python
# Fade in over 2 seconds
aid = lib.play_from_file("song.mp3")
lib.fadein_music(aid, loops=-1, ms=2000)

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

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

# Check states
if lib.is_music_playing():
    print("Music is playing")
    
fade_state = lib.get_music_fading()  # Returns MIX_NO_FADING, MIX_FADING_OUT, MIX_FADING_IN
```

**Underlying SDL2 Functions:**
- `Mix_FadeInMusic()` - Basic fade in
- `Mix_FadeInMusicPos()` - Positioned fade in  
- `Mix_FadeOutMusic()` - Fade out
- `Mix_PlayingMusic()` - Play state check
- `Mix_PausedMusic()` - Pause state check
- `Mix_FadingMusic()` - Fade state query

---

#### 🧠 Engineering Improvements

**1. Smart Welcome Message**

The startup banner is now cleaner and user-controllable:

```python
# Default behavior (shows once)
import ap_ds  # Prints: AP_DS © - Audio Library By DVS v2.4.0 | https://dvsx.top

# Silence it with environment variable
import os
os.environ['AP_DS_HIDE_SUPPORT_PROMPT'] = '1'
import ap_ds  # No output
```

**2. Centralized Version Management**

Version is now defined once in `setup.py` and automatically generates `_version.py`:

```python
# setup.py
VERSION = "2.4.0"

def write_version_file():
    version_file_path = os.path.join("ap_ds", "_version.py")
    with open(version_file_path, "w", encoding="utf-8") as f:
        f.write(f'__version__ = "{VERSION}"\n')
```

**3. Robust Import System**

Two-layer fallback ensures compatibility across all Python environments:

```python
try:
    from ._version import __version__
except ImportError:
    try:
        from _version import __version__
    except ImportError:
        __version__ = "unknown"

# Same pattern for core modules
try:
    from .player import *
except ImportError:
    from player import *
```

**4. Unified Project URL**

All project references now point to the central hub: **https://dvsx.top**

---

#### 📋 Full API Changes

| Function | Parameters | Return | Description |
|----------|------------|--------|-------------|
| `fadein_music` | `aid: int, loops: int = -1, ms: int = 0` | `None` | Fade in audio instance |
| `fadein_music_pos` | `aid: int, loops: int = -1, ms: int = 0, position: float = 0.0` | `None` | Fade in from position |
| `fadeout_music` | `ms: int = 0` | `None` | Fade out current music |
| `is_music_playing` | `()` | `bool` | Check playing state |
| `is_music_paused` | `()` | `bool` | Check paused state |
| `get_music_fading` | `()` | `int` | Get fade state (0=no fade, 1=fade out, 2=fade in) |

---

#### 🔧 Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `AP_DS_WAV_THRESHOLD` | `6` | WAV threshold in seconds (from v2.3.5) |
| `AP_DS_HIDE_SUPPORT_PROMPT` | not set | Set to `1` to hide startup message |

---

#### ⚡ Performance & Compatibility

- **Zero API breakage** – All existing code continues to work
- **No size increase** – Still under 4MB across all platforms
- **Full backward compatibility** – v2.3.x applications run unchanged
- **Python 3.7+ support** – Maintained

---

#### 📦 Migration Guide

No migration needed. Simply upgrade and enjoy the new features:

```bash
pip install --upgrade ap_ds
```

To use the new fade functions in existing code:

```python
# Before (v2.3.x)
aid = lib.play_from_file("song.mp3")
time.sleep(5)
lib.stop_audio(aid)

# After (v2.4.0) - Professional transitions!
aid = lib.play_from_file("song.mp3")
lib.fadeout_music(ms=3000)  # Smooth exit
```

---

#### 🎯 Summary

v2.4.0 transforms ap_ds from a "playback library" into a **professional audio tool** with:
- Smooth audio transitions (fade in/out)
- Complete state querying
- Cleaner, more professional startup
- Industrial-strength version management
- Bulletproof import system

**All while maintaining the core promise: lightweight, zero external Python dependencies, and cross-platform compatibility.**

---

*Next: Planning v2.5.0 was superseded by v3.0.0 LTS.*

### v2.3.6 (February 27, 2026)

This version updates the PyPi documentation, adding detailed license information and version history, and adding more examples.

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

#### Stability & Testing

**Six-Dimension Test Coverage:**

1. **Library Loading & Initialization**
   - Cross-platform SDL2 loading validation
   - Network failure, file corruption, permission error recovery
   - AudioLibrary parameter verification
   - Default settings validation across devices

2. **Playback Testing**
   - MP3, FLAC, OGG, WAV format validation
   - Variable bitrate and non-standard file handling
   - Non-blocking playback verification
   - Concurrent audio playback testing

3. **Seek Testing**
   - Precision validation across formats
   - Boundary condition testing (start, end, beyond duration)
   - Seek during paused/stopped states

4. **Memory Pressure & Leak Detection**
   - Long-duration playback monitoring
   - Repeated load/unload cycles
   - Cache behavior validation
   - **Result:** ~4MB growth after extensive testing

5. **Metadata Parsing Accuracy**
   - Sample collection from diverse sources
   - Edge case analysis
   - Format-specific parser validation

6. **DAP System Validation**
   - Automatic recording verification
   - Save/load reliability
   - Memory cleanup on clear

#### Embedded Platform Support

**Tested Environments:**

| Platform | SoC | Cores | RAM | OS | Audio Output |
|----------|-----|-------|-----|-----|--------------|
| Orange Pi 4 Pro | Allwinner A733 | 2xA76 + 6xA55 @ 2.0GHz | 8GB LPDDR5 | Ubuntu 22.04 ARM64 | 3.5mm → TPA3116D2 @ 2×8Ω 5W |
| Raspberry Pi 5 | BCM2712 | 4xA76 @ 2.4GHz | 8GB LPDDR4X | Ubuntu 22.04 ARM64 | 3.5mm → TPA3116D2 @ 2×8Ω 5W |

**Results:**
- Intelligent package management: Working normally
- Audio output: Normal via 3.5mm
- AID management system: Working normally
- Memory growth: ~4MB after comprehensive testing

**Important Note:** ap_ds is a wrapper library. Low-level concerns (power optimization, I2S/HDMI/ALSA output, fine-grained memory management) are handled by SDL2 and the operating system. Python's language nature limits fine resource control. We have tested and confirmed functionality; no embedded-specific optimizations are planned as they fall outside our scope.

#### Bug Fixes

**WAV Playback Mode Selection**

Fixed an issue where WAV files were incorrectly treated as sound effects, preventing seek operations.

**New Logic:**
- Files < threshold (default 6s) → Sound effect mode (no seek)
- Files >= threshold → Music mode (full seek support)
- Configurable via `AP_DS_WAV_THRESHOLD` environment variable

**Validation:**
```python
WAV_THRESHOLD = int(os.environ.get('AP_DS_WAV_THRESHOLD', '6'))
if WAV_THRESHOLD >= 30:  # Reset to default to prevent memory issues
    WAV_THRESHOLD = 6
elif WAV_THRESHOLD < 0:
    WAV_THRESHOLD = 6
```

#### Error Message Decision

**Design Decision: Keep Existing Simple Error Format**

**Proposed Change Rejected:** Structured error messages with causes and suggestions were considered but rejected.

**Reasons:**

1. **Limited Help for Complex Errors**
   - Simple errors are easy to debug
   - Complex errors cannot be accurately diagnosed by the library
   - LLM integration for error analysis was considered but rejected due to cost and security concerns

2. **Target Users Are Developers**
   - Developers can read error messages
   - Beginners can ask ChatGPT or search engines
   - Pre-written suggestions add little value

3. **Maintenance Burden**
   - Modifying all error paths is time-consuming
   - High risk of introducing new bugs
   - Existing format works: `raise RuntimeError(f"Failed to load: {file_path}")`

**Final Decision:** Maintain existing simple exception format with clear error messages.

#### SSL Certificate Issue: R12 Certificate Compatibility

**Problem:** On some Windows systems, R12 certificates cause verification failures during SDL2 download.

**Solution Implemented:**
```python
def download_with_ssl(url):
    try:
        # First attempt: Standard SSL verification
        return urllib.request.urlopen(url)
    except (URLError, SSLError) as e:
        # Fallback: Unverified context
        ssl_context = ssl.create_default_context()
        ssl_context.check_hostname = False
        ssl_context.verify_mode = ssl.CERT_NONE
        opener = urllib.request.build_opener(
            urllib.request.HTTPSHandler(context=ssl_context)
        )
        urllib.request.install_opener(opener)
        return urllib.request.urlopen(url)
```

**Note:** In v3.0.0 LTS, the fallback is still present, but **hash verification** makes it safe: even if the download is intercepted, the file will be rejected unless its hash matches the official one.

---

v1.0.0 (July 8, 2025) - Initial Release
Milestone: Project birth, foundational functionality

Core Features

Basic audio playback: MP3, WAV, FLAC, OGG formats
Playback control: Play, Pause, Stop, Seek basic APIs
Lightweight design: Initial version ~2MB
Technical Characteristics

SDL2-based audio processing
Pure Python encapsulation, no complex dependencies
Clean API design
v2.0.0 (November 5, 2025) - Architecture Refactoring
Milestone: Introduction of modern audio management system

Major Improvements

AID System: Audio ID for unified audio instance management
Architecture Refactoring: Modular design for improved maintainability
Smart Memory Management: Automatic cleanup of unused audio resources
State Management: Unified playback state tracking
Technical Upgrades

Introduction of AudioLibrary class as core manager
Audio instance lifecycle management
Error handling and recovery mechanisms
v2.1.0 (December 26, 2025) - Feature Enhancement
Milestone: Professional functionality expansion

New Features

Volume Control: Real-time volume adjustment (0-100%)
Metadata Enhancement: More precise audio information parsing
Playback Accuracy Improvement: Better time control and seeking
Optimization Improvements

Enhanced audio format compatibility
Optimized memory usage efficiency
More user-friendly API interface
v2.1.4 (January 18, 2026) - Stable Version
Milestone: Production environment stable release

Version Highlights

Core Stability: Thoroughly tested with no known critical bugs
Extreme Lightweight: Complete solution at only 2.5MB
Pain Point Resolution: Fills Python audio development gap
Complete Documentation: Detailed technical manual and examples
Market Positioning "2.5MB Windows Python Audio Solution"

Technical Specifications Size Analysis:

├── SDL2.dll: 2.1MB
├── SDL2_mixer.dll: 400KB
└── Python Code: 42KB
Comparison Advantages:

├── FFmpeg Solution: At least 160MB (64x larger!)
├── Pygame Solution: Bloated with incomplete features
└── ap_ds: 2.5MB perfect solution ✓
v2.2.0 (January 19, 2026) - Cross-Platform Revolution
Milestone: From single-platform to cross-platform

Major New Features

1. Complete macOS Support

Automatic download and installation of SDL2.framework, SDL2_mixer.framework
Smart .dmg file extraction and framework loading
Maintains extreme lightweight: Only 3.36MB (vs 2.5MB Windows version)
Cross-platform unified API, code requires no modification
2. Enhanced Automatic Dependency Management

Cross-platform intelligent download strategy:
Windows: Automatic .dll download
macOS: Automatic .framework download and extraction
Complete error handling and retry mechanisms
Dependency file local caching to avoid repeated downloads
3. Strengthened Platform Position Statement

Explicit Linux non-support with detailed technical reasoning
Professional rejection strategy and alternative solution suggestions
Focus on serving real user needs (Windows/macOS developers)
Performance & Optimization Size Control Breakthrough:

Windows Version: 2.5MB

├── SDL2.dll: 2.1MB
├── SDL2_mixer.dll: 400KB
└── Python Code: 42KB
macOS Version: 3.36MB

├── SDL2.framework: 1.82MB
├── SDL2_mixer.framework: 1.54MB
└── Python Code: 42KB
Comparison with Traditional Solutions:

├── FFmpeg Solution: At least 160MB (47x larger than ap_ds!)
├── Pygame + Parser Libraries: Bloated with incomplete features
└── ap_ds: 3.36MB complete solution ✓
Loading Performance Optimization

First load: Automatic dependency download
Subsequent loads: Use local cache
Cross-platform loading logic unified and efficient
Technical Architecture Improvements Cross-Platform Loading System:

def import_sdl2():
    """Intelligent SDL2 library loading (Windows/macOS)"""
    if platform == "win32":
        # Load .dll
    elif platform == "darwin":
        # Load .framework
    else:
        # Explicitly reject unsupported platforms
Enhanced Error Handling

More user-friendly error messages
Detailed troubleshooting suggestions
Intelligent environment detection and problem diagnosis
Documentation & Examples Update

Windows Installation: pip install ap_ds (fully automatic)
macOS Installation: pip install ap_ds (fully automatic, requires network download)
Linux: Clear explanation of non-support reasons and alternatives
Design Philosophy Reiteration

Audio playback only, no editing/transcoding
Serves real needs: Python desktop application developers
Willingness to say no: No Linux support, no AAC support
Market Positioning Upgrade From: "2.5MB Windows Python Audio Solution"

To: "3.36MB Cross-Platform Python Audio Solution"

v2.3.0 (January 31, 2026) - DAP Recording System
Milestone: From playback to memory, introducing audio playback history recording system

DAP (Dvs Audio Playlist) is a major functional extension of the ap_ds library, providing developers with a complete solution for audio playback history recording. This is not a traditional playlist manager, but a lightweight system focused on recording and memory.

Core Characteristics

1. Intelligent Automatic Recording

Seamless integration: Automatically triggered in play_from_file(), play_from_memory()
Precise timing: Records only during actual playback, avoiding misoperations
Complete metadata: Records path, duration, bitrate, channel count, and other key information
2. Lightweight Design Philosophy

Metadata only: No audio data storage, maintaining extremely low memory usage
Intelligent deduplication: Automatically avoids duplicate records of the same file
On-demand persistence: Runtime memory storage, explicit call required for file saving
3. Standardized File Format

Dedicated extension: .ap_ds-dap ensures format recognition
Standard JSON: Easy parsing, editing, and exchange
Format validation: Enforced extension, ensuring data integrity
New API Details

DAP Recording Function List

1. _add_to_dap_recordings(file_path: str) -> None Function: Add audio file metadata to memory record list

Characteristics:

Internal use only (automatically triggered via playback methods)
Automatic audio metadata extraction (duration, bitrate, channel count)
Intelligent deduplication mechanism
Standardized recording format
Usage Example:

# Automatically triggered via playback
lib.play_from_file("song.mp3")  # Automatically recorded to DAP
# Log output: Recorded DAP file: song.mp3
2. save_dap_to_json(save_path: str) -> bool Function: Save DAP records from memory to JSON file

Mandatory Requirements:

File extension must be .ap_ds-dap
UTF-8 encoding for multilingual compatibility
Pretty JSON format (2-space indentation)
Return Value:

True: Save successful
False: Save failed (error logged)
Error Handling:

try:
    success = lib.save_dap_to_json("history.ap_ds-dap")
except ValueError as e:
    print(f"Format error: {e}")  # Extension doesn't meet requirements
3. get_dap_recordings() -> List[Dict] Function: Get deep copy of all current DAP records

Data Format:

[
  {
    "path": "/music/song1.mp3",
    "duration": 240,
    "bitrate": 320000,
    "channels": 2
  }
]
Usage Scenarios:

Display playback history statistics
Export to other formats
Custom interface display
4. clear_dap_recordings() -> None Function: Clear all DAP records from memory

Characteristics:

Irreversible operation (unless already saved)
Clear operation logging
Immediate memory release
Technical Architecture Design

Workflow:

User plays audio
Calls play_from_file()
Automatically calls _add_to_dap_recordings()
Extracts metadata via get_audio_metadata_by_path()
Creates standardized record
Adds to _dap_recordings list
Intelligent deduplication check
Stores in memory
User optionally calls save_dap_to_json()
Extension validation
JSON serialization
File saving
Memory Management Design:

class AudioLibrary:
    def __init__(self):
        # DAP memory storage
        self._dap_recordings = []  # List[Dict]
        
        # Approximate memory usage per record:
        # path: ~100 bytes
        # metadata: ~50 bytes
        # total: ~150 bytes/record
Performance Characteristics:

Record operation: O(1) complexity (adding new records)
Deduplication check: O(n) complexity (linear check)
Memory usage: ~1.5MB per 10,000 records
File size: ~2-3MB per 10,000 records (JSON format)
v2.3.1(February 9, 2026) - Documentation Update
Milestone: Improved documentation and minor fixes

Changes:

Updated README.md with better examples and explanations
Minor bug fixes in documentation examples
Improved error messages for common usage scenarios
Enhanced installation instructions for different platforms
v2.3.2 (February 9, 2026) - Linux Support Enhancement
Milestone: Extended Linux support with interactive setup

Major New Features:

1. Interactive Linux Support

Three options for Linux users:

Use system-installed libraries (via package manager)
Specify paths to compiled .so files
Get detailed compilation instructions
Smart platform detection and guidance

Unified API across all platforms

2. Enhanced Cross-Platform Compatibility

Windows: Full auto-download support for SDL2 libraries
macOS: Full auto-download support for SDL2 frameworks
Linux: Interactive setup with user guidance
3. Improved User Experience

Clear platform-specific installation guidance
Better error handling for library loading failures
Enhanced troubleshooting documentation
Technical Implementation:

def import_sdl2():
    """Main function: Import SDL2 libraries with cross-platform support"""
    platform = sys.platform
    
    if platform == "win32":
        # Windows - auto-download and load .dll
    elif platform == "darwin":
        # macOS - auto-download and load .framework
    elif platform.startswith("linux"):
        # Linux - interactive setup with options:
        print("Linux SDL2 Library Loading")
        print("Options:")
        print("1. Use system-installed libraries (if available)")
        print("2. Specify path to your compiled .so files")
        print("3. Get compilation instructions")
        # User interaction and library loading
Market Positioning Upgrade From: "3.36MB Cross-Platform Python Audio Solution"

To: "Complete Cross-Platform Python Audio Solution (Windows, macOS, Linux)"

Performance & Optimization:

Maintains lightweight design across all platforms
Intelligent dependency management for each platform
Consistent API experience regardless of platform
Backward Compatibility:

All existing code continues to work unchanged
New Linux support doesn't affect Windows/macOS users
DAP system fully functional on all platforms
v2.3.3 (February 9, 2026) - Critical Bug Fix & Platform Stabilization
🚨 This is a critical update that fixes a major bug. Previous versions (2.2.0, 2.3.0, 2.3.1, 2.3.2) have been yanked from PyPI due to this issue. Please upgrade immediately.

Critical Fix
Resolved Cross-Platform Crash: Fixed a critical segmentation fault bug that prevented the library from functioning on macOS and Linux. The issue was caused by missing C function prototypes (ctypes argtypes/restype) on non-Windows platforms.
Root Cause: In earlier versions, the platform-specific code only defined essential C function bindings (prototypes) for Windows (win32), causing memory access violations on other operating systems.
Solution: The library now unconditionally defines all necessary C function bindings immediately after loading the SDL2 libraries, regardless of the operating system. This ensures stable and safe calls across Windows, macOS, and Linux.
Enhanced Stability & Integrity
Guaranteed Functionality: The core promise—pip install ap_ds followed by functional playback and parsing—is now reliably fulfilled on all three major platforms.
Validated Workflow: The existing, user-friendly platform-handling logic (auto-download for Windows/macOS, interactive guidance for Linux) has been fully stabilized and verified end-to-end.
What This Means for You
For All Users: If you are using any version prior to 2.3.4, you are affected. Please run pip install --upgrade ap_ds now.
For Linux/macOS Users: The library will now work correctly. The interactive Linux setup (choosing system libraries, specifying paths, or getting compilation help) functions as intended.
For Windows Users: While you may not have experienced crashes, upgrading is essential to stay on the supported, stable release.
Technical Summary
This patch (2.3.2 → 2.3.4) is a PATCH-level version change under Semantic Versioning, signifying a backward-compatible bug fix that resolves incorrect behavior. The core API, features, and user experience remain unchanged and now operate reliably everywhere.

Maintainer's Note: We sincerely apologize for the disruption. Upholding stability and a professional standard for all users is our top priority. Version 2.3.4 represents that commitment.

v2.3.4 (February 10, 2026) - Linux Intelligent Import System
🚀 This update revolutionizes Linux support with an intelligent, multi-layer import system that dramatically improves user experience.

Intelligent Linux Import System
Four-Layer Fallback Strategy: Implements a sophisticated, progressive library discovery system:

System Library Check: First attempts to use system-installed SDL2 libraries via standard library paths
User Configuration: Checks for user-saved library paths from previous successful configurations
Automatic Installation: Intelligently detects package manager and attempts non-interactive installation
Interactive Guidance: Only presents manual options when all automatic methods fail
Package Manager Detection & Auto-Install:

Ubuntu/Debian: Auto-detects apt-get and installs libsdl2-dev libsdl2-mixer-dev
Fedora: Auto-detects dnf and installs SDL2-devel SDL2_mixer-devel
Arch: Auto-detects pacman and installs sdl2 sdl2_mixer
All installations use non-interactive mode (-y/--noconfirm) for seamless setup
Configuration Persistence & Smart Memory
Automatic Path Saving: User-specified library paths are automatically saved to both:

Environment variables (AP_DS_SDL2_PATH, AP_DS_SDL2_MIXER_PATH)
Persistent config file (~/.config/ap_ds/sdl_paths.conf)
One-Time Setup: Linux users only need to configure paths once; subsequent runs use saved configurations automatically

Configuration Validation: Saved paths are validated before use to ensure libraries are still accessible

Enhanced User Experience
Reduced User Interaction: Most users will experience automatic setup without any manual input
Clear Progress Feedback: Real-time status updates during each layer of the discovery process
Intelligent Fallback: System gracefully degrades through each layer, always providing the next-best option
Technical Implementation Highlights
def import_sdl2():
    """Intelligent multi-layer SDL2 import system"""
    if platform.startswith("linux"):
        # Layer 1: System library check
        if find_system_libraries():
            return load_from_system()
        
        # Layer 2: User-configured paths
        if check_user_config():
            return load_from_user_config()
        
        # Layer 3: Automatic package manager installation
        if try_auto_install():
            return load_from_newly_installed()
        
        # Layer 4: Interactive guidance (only if all else fails)
        return interactive_setup_and_save()
Platform-Specific Improvements
Windows/macOS: Unchanged - continue with seamless auto-download functionality
Linux: Transformed from manual setup to intelligent automatic configuration
All Platforms: Consistent API experience with zero changes to user code
Performance & Stability
Faster First-Time Setup: Linux users experience dramatically reduced setup time
Reduced Error Rates: Intelligent validation prevents common configuration mistakes
Enhanced Reliability: Multi-layer approach ensures maximum compatibility across diverse Linux distributions
What This Means for You
For New Linux Users: Installation is now as simple as Windows/macOS - pip install ap_ds followed by automatic setup in most cases
For Existing Linux Users: Your saved configurations continue to work; new users benefit from the intelligent system
For All Users: The library maintains its lightweight design (still under 4MB) while gaining sophisticated platform intelligence

---

## License

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

---

# DVS Audio Library (ap_ds) Open Source License Version 2.0

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

---

## 1. Definitions

1.1. **“Software”** means the DVS Audio Library (ap_ds) project and all its components, source code, object code, and related documentation. The official name of this project is “ap_ds”, and the following names are also granted as officially recognized brand identifiers:
   - AP_DS
   - Audio Library By DVS
   - DVS Audio Player
   (All of the above names are case-insensitive and are considered officially recognized brand names.)

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

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

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

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

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

---

## 2. Grant of License

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

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

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

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

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

---

## 3. Obligations and Restrictions

### 3.1. Attribution and Source Identification
Any time the Software or a Modified Version is used, Distributed, or integrated, You must:

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

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

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

### 3.2. Brand Protection
To prevent brand confusion and project fragmentation, Modified Versions must comply with the following strict rules:

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

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

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

### 3.3. Quality Commitment for Modified Versions
If You Distribute a Modified Version, You must:

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

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

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

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

---

## 4. Patent Grant

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

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

---

## 5. Technical Transparency and Security

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

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

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

---

## 6. Disclaimer of Warranty and Limitation of Liability

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

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

---

## 7. License Management and Termination

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

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

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

---

## 8. Governing Law and Dispute Resolution

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

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

---

## 9. Contact Information

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

9.2. **Technical Support**:
   - Priority should be given to submitting issues via GitCode Issues.
   - Urgent matters can be directed to the emails above.

---

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

---

## Final Notes

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. For questions or issues, please reach out through the official channels.

**Thank you for using ap_ds!**
