backend-infra-engineer: Release v0.3.9-hotfix7 snapshot

This commit is contained in:
scawful
2025-11-23 13:37:10 -05:00
parent c8289bffda
commit 2934c82b75
202 changed files with 34914 additions and 845 deletions

View File

@@ -0,0 +1,222 @@
# SDL3 Audio Backend Implementation
**Date**: 2025-11-23
**Author**: snes-emulator-expert agent
**Status**: Implementation Complete
## Overview
This document describes the SDL3 audio backend implementation for the YAZE SNES emulator. The SDL3 backend provides a modern, stream-based audio interface that replaces the SDL2 queue-based approach.
## Architecture
### Key Components
1. **SDL3AudioBackend Class** (`src/app/emu/audio/sdl3_audio_backend.h/.cc`)
- Implements the `IAudioBackend` interface
- Uses SDL3's stream-based audio API
- Provides volume control, resampling, and playback management
2. **SDL Compatibility Layer** (`src/app/platform/sdl_compat.h`)
- Provides cross-version compatibility macros
- Abstracts differences between SDL2 and SDL3 APIs
- Enables conditional compilation based on `YAZE_USE_SDL3`
3. **Factory Integration** (`src/app/emu/audio/audio_backend.cc`)
- Updated `AudioBackendFactory::Create()` to support SDL3
- Conditional compilation ensures SDL3 backend only available when built with SDL3
## SDL3 Audio API Changes
### Major Differences from SDL2
| SDL2 API | SDL3 API | Purpose |
|----------|----------|---------|
| `SDL_OpenAudioDevice()` | `SDL_OpenAudioDeviceStream()` | Device initialization |
| `SDL_QueueAudio()` | `SDL_PutAudioStreamData()` | Queue audio samples |
| `SDL_GetQueuedAudioSize()` | `SDL_GetAudioStreamQueued()` | Get queued data size |
| `SDL_ClearQueuedAudio()` | `SDL_ClearAudioStream()` | Clear audio buffer |
| `SDL_PauseAudioDevice(id, 0/1)` | `SDL_ResumeAudioDevice()` / `SDL_PauseAudioDevice()` | Control playback |
| `SDL_GetAudioDeviceStatus()` | `SDL_IsAudioDevicePaused()` | Check playback state |
### Stream-Based Architecture
SDL3 introduces `SDL_AudioStream` as the primary interface for audio:
```cpp
// Create stream with device
SDL_AudioStream* stream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, // Use default device
&spec, // Desired format
nullptr, // No callback
nullptr // No user data
);
// Queue audio data
SDL_PutAudioStreamData(stream, samples, size_in_bytes);
// Get device from stream
SDL_AudioDeviceID device = SDL_GetAudioStreamDevice(stream);
// Control playback through device
SDL_ResumeAudioDevice(device);
SDL_PauseAudioDevice(device);
```
## Implementation Details
### Initialization
The `Initialize()` method:
1. Creates an audio stream using `SDL_OpenAudioDeviceStream()`
2. Extracts the device ID from the stream
3. Queries actual device format (may differ from requested)
4. Starts playback immediately with `SDL_ResumeAudioDevice()`
### Audio Data Flow
```
Application → QueueSamples() → Volume Scaling → SDL_PutAudioStreamData() → SDL3 → Audio Device
```
### Volume Control
Volume is applied during sample queueing:
- Fast path: When volume = 1.0, samples pass through unchanged
- Slow path: Samples are scaled by volume factor with clamping
### Resampling Support
The backend supports native rate resampling for SPC700 emulation:
1. **Setup**: Create separate resampling stream with `SDL_CreateAudioStream()`
2. **Input**: Native rate samples (e.g., 32kHz from SPC700)
3. **Process**: SDL3 handles resampling internally
4. **Output**: Resampled data at device rate (e.g., 48kHz)
### Thread Safety
- Volume control uses `std::atomic<float>` for thread-safe access
- Initialization state tracked with `std::atomic<bool>`
- SDL3 handles internal thread safety for audio streams
## Build Configuration
### CMake Integration
The SDL3 backend is conditionally compiled based on the `YAZE_USE_SDL3` flag:
```cmake
# In src/CMakeLists.txt
if(YAZE_USE_SDL3)
list(APPEND YAZE_APP_EMU_SRC app/emu/audio/sdl3_audio_backend.cc)
endif()
```
### Compilation Flags
- Define `YAZE_USE_SDL3` to enable SDL3 support
- Include paths must contain SDL3 headers
- Link against SDL3 library (not SDL2)
## Testing
### Unit Tests
Located in `test/unit/sdl3_audio_backend_test.cc`:
- Basic initialization and shutdown
- Volume control
- Sample queueing (int16 and float)
- Playback control (play/pause/stop)
- Queue clearing
- Resampling support
- Double initialization handling
### Integration Testing
To test the SDL3 audio backend in the emulator:
1. Build with SDL3 support:
```bash
cmake -DYAZE_USE_SDL3=ON ..
make
```
2. Run the emulator with a ROM:
```bash
./yaze --rom_file=zelda3.sfc
```
3. Verify audio playback in the emulator
## Performance Considerations
### Optimizations
1. **Volume Scaling Fast Path**
- Skip processing when volume = 1.0 (common case)
- Use thread-local buffers to avoid allocations
2. **Buffer Management**
- Reuse buffers for resampling operations
- Pre-allocate based on expected sizes
3. **Minimal Locking**
- Rely on SDL3's internal thread safety
- Use lock-free atomics for shared state
### Latency
SDL3's stream-based approach can provide lower latency than SDL2's queue:
- Smaller buffer sizes possible
- More direct path to audio hardware
- Better synchronization with video
## Known Issues and Limitations
1. **Platform Support**
- SDL3 is newer and may not be available on all platforms
- Fallback to SDL2 backend when SDL3 unavailable
2. **API Stability**
- SDL3 API may still evolve
- Monitor SDL3 releases for breaking changes
3. **Device Enumeration**
- Current implementation uses default device only
- Could be extended to support device selection
## Future Enhancements
1. **Device Selection**
- Add support for choosing specific audio devices
- Implement device change notifications
2. **Advanced Resampling**
- Expose resampling quality settings
- Support for multiple resampling streams
3. **Spatial Audio**
- Leverage SDL3's potential spatial audio capabilities
- Support for surround sound configurations
4. **Performance Monitoring**
- Add metrics for buffer underruns
- Track actual vs requested latency
## Migration from SDL2
To migrate from SDL2 to SDL3 backend:
1. Install SDL3 development libraries
2. Set `YAZE_USE_SDL3=ON` in CMake
3. Rebuild the project
4. Audio backend factory automatically selects SDL3
No code changes required in the emulator - the `IAudioBackend` interface abstracts the differences.
## References
- [SDL3 Migration Guide](https://wiki.libsdl.org/SDL3/README-migration)
- [SDL3 Audio API Documentation](https://wiki.libsdl.org/SDL3/CategoryAudio)
- [SDL_AudioStream Documentation](https://wiki.libsdl.org/SDL3/SDL_AudioStream)