Files
Syplheed-Reborn/crates/sylpheed-formats/src/audio.rs
MechaCat02 f8127e73b0 feat: initialise workspace — Milestone 1 asset explorer
Three-crate Cargo workspace structured per PROJECT.md spec:
- crates/sylpheed-formats  — Xbox 360 format parsers (no Bevy)
- crates/sylpheed-viewer   — Bevy 0.15 asset viewer + egui UI
- crates/sylpheed-cli      — CLI tools (extract/list/sniff/texture)

Milestone 1 features:
- XISO disc image reading via xdvdfs 0.8
- XPR2 texture container parsing + Morton de-tiling
- D3DFORMAT → wgpu TextureFormat mapping (DXT1/3/5, DXN, ARGB)
- Custom Bevy AssetLoader for .xpr files
- Orbit camera (LMB orbit, RMB pan, scroll zoom)
- egui file browser + RE notes panel
- CLI: extract / list / sniff / texture info / texture export
- GitHub Actions CI (Linux, macOS, Windows, WASM)
- Trunk WASM build config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 21:04:07 +01:00

86 lines
2.9 KiB
Rust

//! Audio format parsing — XMA and XWB handling.
//!
//! Xbox 360 games use **XMA** (Xbox Media Audio) as their primary audio codec.
//! XMA is a proprietary Microsoft codec derived from WMA Pro.
//!
//! ## The Challenge
//! XMA decoding requires Microsoft's proprietary XMA decoder, which is only
//! available in the Windows DirectX runtime. There are two approaches:
//!
//! ### Option A: Convert on extraction (recommended for Milestone 1)
//! Use `xma2encode.exe` (from Xbox 360 SDK) or `ffmpeg` (has partial XMA support)
//! to pre-convert all audio to OGG/WAV during the extraction step.
//!
//! ```bash
//! # Convert a single XMA file to WAV using ffmpeg
//! ffmpeg -i audio.xma output.wav
//!
//! # Or use VGMStream's test.exe for more accurate XMA decoding
//! ```
//!
//! ### Option B: XMA2 software decoder
//! The `xma2dec` project provides an open-source XMA2 decoder.
//! GitHub: https://github.com/koolkdev/xmalib (research-quality)
//!
//! ### XWB Wave Bank format
//! Xbox 360 games typically bundle audio into XWB (Xbox Wave Bank) files.
//! These are containers that hold multiple XMA streams.
//! Reference: https://github.com/microsoft/DirectXTK/tree/main/Audio
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AudioError {
#[error("Unknown audio format magic: {0:?}")]
UnknownMagic([u8; 4]),
#[error("XMA decoding requires pre-conversion. See audio.rs for instructions.")]
XmaNotSupported,
#[error("Parse error: {0}")]
Parse(String),
}
/// An audio clip decoded to raw PCM, ready for Bevy's audio system.
#[derive(Debug, Clone)]
pub struct GameAudio {
/// Raw PCM samples (interleaved for stereo: L R L R ...)
pub samples: Vec<f32>,
pub channels: u16,
pub sample_rate: u32,
}
impl GameAudio {
/// Parse audio from raw bytes.
///
/// Currently only WAV/PCM is supported. For XMA files, pre-convert using:
/// `ffmpeg -i file.xma file.wav`
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AudioError> {
// Check for WAV magic
if bytes.len() >= 4 && &bytes[..4] == b"RIFF" {
return Self::from_wav(bytes);
}
// XMA magic bytes
if bytes.len() >= 4 && (
&bytes[..4] == b"XWB\0" || // Wave bank
&bytes[..4] == b"XMA2" // Raw XMA2
) {
return Err(AudioError::XmaNotSupported);
}
Err(AudioError::UnknownMagic(
bytes[..4.min(bytes.len())].try_into().unwrap_or([0u8; 4])
))
}
fn from_wav(bytes: &[u8]) -> Result<Self, AudioError> {
// Minimal WAV parser for PCM files
// A proper implementation should use the `hound` crate:
// https://crates.io/crates/hound
//
// TODO: Add `hound = "3"` to Cargo.toml and implement properly
Err(AudioError::Parse(
"WAV parsing TODO: add `hound` crate and implement".to_string()
))
}
}