docs: audio system and build optimizations

- Introduced a production-quality audio backend abstraction layer, enabling seamless integration with SDL2, SDL3, and custom platforms.
- Implemented APU handshake debugging features for improved monitoring of CPU-APU communication during audio processing.
- Upgraded gRPC to version 1.67.1, resolving MSVC template issues and enhancing compatibility with modern compilers.
- Added MSVC-specific compiler flags to optimize Windows build performance and reduce build times significantly.
- Updated documentation to reflect new audio system architecture and build instructions, ensuring clarity for developers.
This commit is contained in:
scawful
2025-10-09 01:16:53 -04:00
parent 58960d59e7
commit 35dcdbb2fc
3 changed files with 393 additions and 8 deletions

View File

@@ -164,31 +164,62 @@ open build/yaze.xcodeproj
## 7. Windows Build Optimization ## 7. Windows Build Optimization
### The Problem: Slow gRPC Builds ### gRPC v1.67.1 and MSVC Compatibility
Building with gRPC on Windows (`-DYAZE_WITH_GRPC=ON`) can take **15-20 minutes** the first time, as it compiles gRPC and its dependencies from source.
### Solution: Use vcpkg for Pre-compiled Binaries **Recent Update (October 2025):** The project has been upgraded to gRPC v1.67.1 which includes critical MSVC template fixes. This version resolves previous template instantiation errors that occurred with v1.62.0.
**MSVC-Specific Compiler Flags:**
The build system now automatically applies these flags for Windows builds:
- `/bigobj` - Allows large object files (gRPC generates many symbols)
- `/permissive-` - Enables standards conformance mode
- `/wd4267 /wd4244` - Suppresses harmless conversion warnings
- `/constexpr:depth2048` - Handles deep template instantiations (MSVC 2019+)
### The Problem: Slow gRPC Builds
Building with gRPC on Windows (`-DYAZE_WITH_GRPC=ON`) can take **15-20 minutes** the first time, as it compiles gRPC v1.67.1 and its dependencies from source.
### Solution A: Use vcpkg for Pre-compiled Binaries (Recommended - FAST)
Using `vcpkg` to manage gRPC is the recommended approach for Windows developers who need GUI automation features. Using `vcpkg` to manage gRPC is the recommended approach for Windows developers who need GUI automation features.
**Step 1: Install vcpkg and Dependencies** **Step 1: Install vcpkg and Dependencies**
```powershell ```powershell
# This only needs to be done once # This only needs to be done once
# Use the setup script for convenience:
.\scripts\setup-vcpkg-windows.ps1
# Or manually:
vcpkg install grpc:x64-windows protobuf:x64-windows abseil:x64-windows vcpkg install grpc:x64-windows protobuf:x64-windows abseil:x64-windows
``` ```
**Step 2: Configure CMake to Use vcpkg** **Step 2: Configure CMake to Use vcpkg**
Pass the `vcpkg.cmake` toolchain file to your configure command. Pass the `vcpkg.cmake` toolchain file to your configure command.
```bash ```powershell
# Configure a build that uses vcpkg for gRPC # Configure a build that uses vcpkg for gRPC
cmake -B build -DYAZE_WITH_GRPC=ON ` cmake -B build -G "Visual Studio 17 2022" -A x64 `
-DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DCMAKE_TOOLCHAIN_FILE="vcpkg/scripts/buildsystems/vcpkg.cmake"
# Build (will now be much faster) # Build (will now be much faster: 5-10 minutes)
cmake --build build cmake --build build --config RelWithDebInfo --parallel
``` ```
**Build Time:** ~5-10 minutes (uses pre-compiled gRPC)
### Solution B: FetchContent Build (Slow but Automatic)
If you don't want to use vcpkg, CMake will automatically download and build gRPC from source.
```powershell
# Configure (will download and build gRPC v1.67.1 from source)
cmake -B build -G "Visual Studio 17 2022" -A x64
# Build (first time: ~45-60 minutes, subsequent: ~2-5 minutes)
cmake --build build --config RelWithDebInfo --parallel
```
**Build Time:** ~45-60 minutes first time, ~2-5 minutes subsequent builds (gRPC cached)
## 8. Troubleshooting ## 8. Troubleshooting
Build issues, especially on Windows, often stem from environment misconfiguration. Before anything else, run the verification script. Build issues, especially on Windows, often stem from environment misconfiguration. Before anything else, run the verification script.

View File

@@ -913,6 +913,237 @@ cmake --build build --target yaze -j12
--- ---
## 11.5 Audio System Architecture (October 2025)
### Overview
The emulator now features a **production-quality audio abstraction layer** that decouples the audio implementation from the emulation core. This architecture enables easy migration between SDL2, SDL3, and custom platform-native backends.
### Audio Backend Abstraction
**Architecture:**
```
┌─────────────────────────────────────┐
│ Emulator / Music Editor │
├─────────────────────────────────────┤
│ IAudioBackend (Interface) │
├──────────┬──────────┬───────────────┤
│ SDL2 │ SDL3 │ Platform │
│ Backend │ Backend │ Native │
└──────────┴──────────┴───────────────┘
```
**Key Components:**
1. **IAudioBackend Interface** (`src/app/emu/audio/audio_backend.h`)
- `Initialize(config)` - Setup audio device
- `QueueSamples(samples, count)` - Queue audio for playback
- `SetVolume(volume)` - Control output volume (0.0-1.0)
- `GetStatus()` - Query buffer state (queued frames, underruns)
- `Play/Pause/Stop/Clear()` - Playback control
2. **SDL2AudioBackend** (`src/app/emu/audio/audio_backend.cc`)
- Complete implementation using SDL2 audio API
- Smart buffer management (maintains 2-6 frames)
- Automatic underrun/overflow protection
- Volume scaling at backend level
3. **AudioBackendFactory**
- Factory pattern for creating backends
- Easy to add new backend types
- Minimal coupling to emulator core
**Usage in Emulator:**
```cpp
// Emulator automatically creates audio backend
void Emulator::Initialize() {
audio_backend_ = AudioBackendFactory::Create(BackendType::SDL2);
AudioConfig config{48000, 2, 1024, SampleFormat::INT16};
audio_backend_->Initialize(config);
}
// Smart buffer management in frame loop
void Emulator::Run() {
snes_.SetSamples(audio_buffer_, wanted_samples_);
auto status = audio_backend_->GetStatus();
if (status.queued_frames < 2) {
// Underrun risk - queue more
} else if (status.queued_frames > 6) {
// Overflow - clear and restart
audio_backend_->Clear();
}
audio_backend_->QueueSamples(audio_buffer_, wanted_samples_ * 2);
}
```
### APU Handshake Debugging System
The **ApuHandshakeTracker** provides comprehensive monitoring of CPU-SPC700 communication during the IPL ROM boot sequence.
**Features:**
- **Phase Tracking**: Monitors handshake progression through distinct phases
- `RESET` - Initial state after reset
- `IPL_BOOT` - SPC700 executing IPL ROM
- `WAITING_BBAA` - CPU waiting for SPC ready signal
- `HANDSHAKE_CC` - CPU sent acknowledge
- `TRANSFER_ACTIVE` - Data transfer in progress
- `TRANSFER_DONE` - Upload complete
- `RUNNING` - Audio driver executing
- **Port Activity Monitor**: Records last 1000 port write events
- Tracks both CPU→SPC and SPC→CPU communications
- Shows PC address for each write
- Displays port values (F4-F7)
- Timestamps for timing analysis
- **Visual Debugger UI**: Real-time display in APU Debugger window
- Current phase with color-coded status
- Port activity log with scrollable history
- Transfer progress bar
- Current port values table
- Manual handshake testing buttons
**Integration Points:**
```cpp
// In Snes::WriteBBus() - CPU writes to APU ports
if (adr >= 0x40 && adr < 0x44) { // $2140-$2143
apu_.in_ports_[adr & 0x3] = val;
if (handshake_tracker_) {
handshake_tracker_->OnCpuPortWrite(adr & 0x3, val, cpu_.PC);
}
}
// In Apu::Write() - SPC700 writes to output ports
if (adr >= 0xF4 && adr <= 0xF7) {
out_ports_[adr - 0xF4] = val;
if (handshake_tracker_) {
handshake_tracker_->OnSpcPortWrite(adr - 0xF4, val, spc700_.PC);
}
}
```
### IPL ROM Handshake Protocol
The SNES audio system uses a carefully orchestrated handshake between CPU and SPC700:
**Phase 1: IPL ROM Boot (SPC700 Side)**
1. SPC700 resets, PC = $FFC0 (IPL ROM)
2. Executes boot sequence
3. Writes $AA to port F4, $BB to port F5 (ready signal)
4. Enters wait loop at $FFDA: `CMP A, ($F4)` waiting for $CC
**Phase 2: CPU Handshake (From bank $00)**
1. CPU reads F4:F5, expects $BBAA
2. CPU writes $CC to F4 (acknowledge)
3. SPC detects $CC, proceeds to transfer loop
**Phase 3: Data Transfer**
1. CPU writes: size (2 bytes), dest (2 bytes), data bytes
2. Uses counter protocol: CPU writes data+counter, SPC echoes counter
3. Repeat until final block (F5 bit 0 = 1)
4. SPC disables IPL ROM, jumps to uploaded driver
**Debugging Stuck Handshakes:**
If stuck at `WAITING_BBAA`:
```
[APU_DEBUG] Phase: WAITING_BBAA
[APU_DEBUG] Port Activity:
[0001] SPC→ F4 = $AA @ PC=$FFD6
[0002] SPC→ F5 = $BB @ PC=$FFD8
(no CPU write of $CC)
```
**Diagnosis**: CPU not calling LoadIntroSongBank at $008029
- Set breakpoint at $008029 in CPU debugger
- Verify JSR executes
- Check reset vector points to bank $00
**Force Handshake Testing:**
Use "Force Handshake ($CC)" button in APU Debugger to manually test SPC response without CPU code.
### Music Editor Integration
The music editor is now integrated with the audio backend for live music playback.
**Features:**
```cpp
class MusicEditor {
void PlaySong(int song_id) {
// Write song request to game memory
emulator_->snes().Write(0x7E012C, song_id);
// Ensure audio is playing
if (auto* audio = emulator_->audio_backend()) {
audio->Play();
}
}
void SetVolume(float volume) {
if (auto* audio = emulator_->audio_backend()) {
audio->SetVolume(volume); // 0.0 - 1.0
}
}
void StopSong() {
if (auto* audio = emulator_->audio_backend()) {
audio->Stop();
}
}
};
```
**Workflow:**
1. User selects song from dropdown
2. Music editor calls `PlaySong(song_id)`
3. Writes to $7E012C triggers game's audio driver
4. SPC700 processes request and generates samples
5. DSP outputs samples to audio backend
6. User hears music through system audio
### Audio Testing & Diagnostics
**Quick Test:**
```bash
./build/bin/yaze.app/Contents/MacOS/yaze \
--log-level=DEBUG \
--log-categories=APU_DEBUG,AUDIO
# Look for:
# [AUDIO] Audio backend initialized: SDL2
# [APU_DEBUG] Phase: RUNNING
# [APU_DEBUG] SPC700_PC=$0200 (game code, not IPL ROM)
```
**APU Debugger Window:**
- View → APU Debugger
- Watch phase progression in real-time
- Monitor port activity log
- Check transfer progress
- Use force handshake button for testing
**Success Criteria:**
- Audio backend initializes without errors
- SPC ready signal ($BBAA) appears in port log
- CPU writes handshake acknowledge ($CC)
- Transfer completes (Phase = RUNNING)
- SPC PC leaves IPL ROM range ($FFxx)
- Audio samples are non-zero
- Music plays from speakers
### Future Enhancements
1. **SDL3 Backend** - When SDL3 is stable, add `SDL3AudioBackend` implementation
2. **Platform-Native Backends**:
- CoreAudio (macOS) - Lower latency
- WASAPI (Windows) - Exclusive mode support
- PulseAudio/ALSA (Linux) - Better integration
3. **Audio Recording** - Record gameplay audio to WAV/OGG
4. **Real-time DSP Effects** - Echo, reverb, EQ for music editor
5. **Multi-channel Mixer** - Solo/mute individual SPC700 channels
6. **Spectrum Analyzer** - Visualize audio frequencies in real-time
---
## 12. Next Steps & Roadmap ## 12. Next Steps & Roadmap
### 🎯 Immediate Priorities (Critical Path to Full Functionality) ### 🎯 Immediate Priorities (Critical Path to Full Functionality)

View File

@@ -2,6 +2,129 @@
## 0.3.3 (October 2025) ## 0.3.3 (October 2025)
### Emulator: Audio System Infrastructure ✅ COMPLETE
**Audio Backend Abstraction:**
- **IAudioBackend Interface**: Clean abstraction layer for audio implementations, enabling easy migration between SDL2, SDL3, and custom backends
- **SDL2AudioBackend**: Complete implementation with volume control, status queries, and smart buffer management (2-6 frames)
- **AudioBackendFactory**: Factory pattern for creating backends with minimal coupling
- **Benefits**: Future-proof audio system, easy to add platform-native backends (CoreAudio, WASAPI, PulseAudio)
**APU Debugging System:**
- **ApuHandshakeTracker**: Monitors CPU-SPC700 communication in real-time
- **Phase Tracking**: Tracks handshake progression (RESET → IPL_BOOT → WAITING_BBAA → HANDSHAKE_CC → TRANSFER_ACTIVE → RUNNING)
- **Port Activity Monitor**: Records last 1000 port write events with PC addresses
- **Visual Debugger UI**: Real-time phase display, port activity log, transfer progress bars, force handshake testing
- **Integration**: Connected to both CPU (Snes::WriteBBus) and SPC700 (Apu::Write) port operations
**Music Editor Integration:**
- **Live Playback**: `PlaySong(int song_id)` triggers songs via $7E012C memory write
- **Volume Control**: `SetVolume(float)` controls backend volume at abstraction layer
- **Playback Controls**: Stop/pause/resume functionality ready for UI integration
**Documentation:**
- Created comprehensive audio system guides covering IPL ROM protocol, handshake debugging, and testing procedures
### Emulator: Critical Performance Fixes
**Console Logging Performance Killer Fixed:**
- **Issue**: Console logging code was executing on EVERY instruction even when disabled, causing severe performance degradation (< 1 FPS)
- **Impact**: ~1,791,000 console writes per second with mutex locks and buffer flushes
- **Fix**: Removed 73 lines of console output from CPU instruction execution hot path
- **Result**: Emulator now runs at full 60 FPS
**Instruction Logging Default Changed:**
- **Changed**: `kLogInstructions` flag default from `true` to `false`
- **Reason**: Even without console spam, logging every instruction to DisassemblyViewer caused significant slowdown
- **Impact**: No logging overhead unless explicitly enabled by user
**Instruction Log Unbounded Growth Fixed:**
- **Issue**: Legacy `instruction_log_` vector growing to 60+ million entries after 10 minutes, consuming 6GB+ RAM
- **Fix**: Added automatic trimming to 10,000 most recent instructions
- **Result**: Memory usage stays bounded at ~50MB
**Audio Buffer Allocation Bug Fixed:**
- **Issue**: Audio buffer allocated as single `int16_t` instead of array, causing immediate buffer overflow
- **Fix**: Properly allocate as array using `new int16_t[size]` with custom deleter
- **Result**: Audio system can now queue samples without corruption
### Emulator: UI Organization & Input System
**New UI Architecture:**
- **Created `src/app/emu/ui/` directory** for separation of concerns
- **EmulatorUI Layer**: Separated all ImGui rendering code from emulator logic
- **Input Abstraction**: `IInputBackend` interface with SDL2 implementation for future SDL3 migration
- **InputHandler**: Continuous polling system using `SDL_GetKeyboardState()` instead of event-based ImGui keys
**Keyboard Input Fixed:**
- **Issue**: Event-based `ImGui::IsKeyPressed()` only fires once per press, doesn't work for held buttons
- **Fix**: New `InputHandler` uses continuous SDL keyboard state polling every frame
- **Result**: Proper game controls with held button detection
**DisassemblyViewer Enhancement:**
- **Sparse Address Map**: Mesen-style storage of unique addresses only, not every execution
- **Execution Counter**: Increments on re-execution for hotspot analysis
- **Performance**: Tracks millions of instructions with ~5MB RAM vs 6GB+ with old system
- **Always Active**: No need for toggle flag, efficiently active by default
**Feature Flags Cleanup:**
- Removed deprecated `kLogInstructions` flag entirely
- DisassemblyViewer now always active with zero performance cost
### Debugger: Breakpoint & Watchpoint Systems
**BreakpointManager:**
- **CRUD Operations**: Add/Remove/Enable/Disable breakpoints with unique IDs
- **Breakpoint Types**: Execute, Read, Write, Access, and Conditional breakpoints
- **Dual CPU Support**: Separate tracking for 65816 CPU and SPC700
- **Hit Counting**: Tracks how many times each breakpoint is triggered
- **CPU Integration**: Connected to CPU execution via callback system
**WatchpointManager:**
- **Memory Access Tracking**: Monitor reads/writes to memory ranges
- **Range-Based**: Watch single addresses or memory regions ($7E0000-$7E00FF)
- **Access History**: Deque-based storage of last 1000 memory accesses
- **Break-on-Access**: Optional execution pause when watchpoint triggered
- **Export**: CSV export of access history for analysis
**CPU Debugger UI Enhancements:**
- **Integrated Controls**: Play/Pause/Step/Reset buttons directly in debugger window
- **Breakpoint UI**: Address input (hex), add/remove buttons, enable/disable checkboxes, hit count display
- **Live Disassembly**: DisassemblyViewer showing real-time execution
- **Register Display**: Real-time CPU state (A, X, Y, D, SP, PC, PB, DB, flags)
### Build System Simplifications
**Eliminated Conditional Compilation:**
- **Before**: Optional flags for JSON (`YAZE_WITH_JSON`), gRPC (`YAZE_WITH_GRPC`), AI (`Z3ED_AI`)
- **After**: All features always enabled, no configuration required
- **Benefits**: Simpler development, easier onboarding, fewer ifdef-related bugs, consistent builds across all platforms
- **Build Command**: Just `cmake -B build && cmake --build build` - no flags needed!
**DisassemblyViewer Performance Limits:**
- Max 10,000 instructions stored (prevents memory bloat)
- Auto-trim to 8,000 when limit reached (keeps hottest code paths)
- Toggle recording on/off for performance testing
- Clear button to free memory
### Build System: Windows Platform Improvements
**gRPC v1.67.1 Upgrade:**
- **Issue**: v1.62.0 had template instantiation errors on MSVC
- **Fix**: Upgraded to v1.67.1 with MSVC template fixes and better C++17/20 compatibility
- **Result**: Builds successfully on Visual Studio 2022
**MSVC-Specific Compiler Flags:**
- `/bigobj` - Allow large object files (gRPC generates many)
- `/permissive-` - Standards conformance mode
- `/wd4267 /wd4244` - Suppress harmless conversion warnings
- `/constexpr:depth2048` - Handle deep template instantiations
**Cross-Platform Validation:**
- All new audio and input code uses cross-platform SDL2 APIs
- No platform-specific code in audio backend or input abstraction
- Ready for SDL3 migration with minimal changes
### GUI & UX Modernization ### GUI & UX Modernization
- **Theme System**: Implemented a comprehensive theme system (`AgentUITheme`) that centralizes all UI colors. All Agent UI components are now theme-aware, deriving colors from the main application theme. - **Theme System**: Implemented a comprehensive theme system (`AgentUITheme`) that centralizes all UI colors. All Agent UI components are now theme-aware, deriving colors from the main application theme.
- **UI Helper Library**: Created a library of 30+ reusable UI helper functions (`AgentUI::*` and `gui::*`) to standardize panel styles, section headers, status indicators, and buttons, reducing boilerplate code by over 50%. - **UI Helper Library**: Created a library of 30+ reusable UI helper functions (`AgentUI::*` and `gui::*`) to standardize panel styles, section headers, status indicators, and buttons, reducing boilerplate code by over 50%.