///|
/// PCM audio buffer with interleaved sample data.
pub(all) struct AudioBuffer {
channels : Int
sample_rate : Int
data : FixedArray[Float]
} derive(Eq, Show)
///|
/// Stable voice identifier.
pub(all) struct VoiceId(Int) derive(Eq, Show, Compare, Hash)
///|
/// Ring buffer for streaming audio data.
pub(all) struct RingBuffer {
data : FixedArray[Float]
capacity : Int
channels : Int
mut write_pos : Int
mut read_pos : Int
mut frames_available : Int
} derive(Show)
///|
/// Callback that pulls audio data into a buffer. Returns frames actually written.
pub(all) struct PullCallback((FixedArray[Float], Int) -> Int)
///|
/// Streaming audio source backed by a ring buffer.
pub(all) struct StreamingSource {
ring : RingBuffer
pull : PullCallback
channels : Int
sample_rate : Int
mut ended : Bool
mut total_pulled : Int
}
///|
pub impl Show for PullCallback with output(self, logger) {
ignore(self)
logger.write_string("")
}
///|
pub impl Show for StreamingSource with output(self, logger) {
logger.write_string(
"StreamingSource { channels: \{self.channels}, sample_rate: \{self.sample_rate}, ended: \{self.ended} }",
)
}
///|
/// Audio source: either a complete buffer or a streaming source.
pub(all) enum AudioSource {
Buffer(AudioBuffer)
Stream(StreamingSource)
} derive(Show)
///|
/// Voice playback state.
pub(all) enum VoiceState {
Playing
Paused
Stopped
} derive(Eq, Show)
///|
/// A single voice playing an audio source.
pub(all) struct Voice {
id : VoiceId
source : AudioSource
mut position : Float
mut gain : Float
mut pan : Float
mut state : VoiceState
looping : Bool
sample_rate : Int
loop_start : Int // frame index to loop back to (default 0)
loop_end : Int // frame index where loop restarts (0 = buffer end)
envelope : Envelope?
effects : Array[EffectNode]
} derive(Show)
///|
/// Resampling quality presets.
pub(all) enum ResampleQuality {
Nearest
Linear
Cubic
} derive(Eq, Show)
///|
/// Multi-voice mixer with master gain.
pub(all) struct Mixer {
mut voices : Array[Voice]
mut master_gain : Float
channels : Int
sample_rate : Int
resample_quality : ResampleQuality
mut next_voice_id : Int
}
///|
/// ADSR envelope configuration.
pub(all) struct EnvelopeConfig {
attack : Float // seconds
decay : Float // seconds
sustain : Float // level [0.0, 1.0]
release_time : Float // seconds
} derive(Eq, Show)
///|
/// Envelope phase in ADSR state machine.
pub(all) enum EnvelopePhase {
Attack
Decay
Sustain
Release
Done
} derive(Eq, Show)
///|
/// ADSR envelope state machine.
pub(all) struct Envelope {
config : EnvelopeConfig
sample_rate : Int
mut phase : EnvelopePhase
mut level : Float
mut phase_position : Int
mut release_start_level : Float
} derive(Show)
///|
/// Biquad filter state (Direct Form II Transposed).
pub(all) struct BiquadState {
b0 : Float
b1 : Float
b2 : Float
a1 : Float
a2 : Float
mut z1 : Float
mut z2 : Float
} derive(Show)
///|
/// Delay effect state.
pub(all) struct DelayState {
buffer : FixedArray[Float]
mut write_pos : Int
delay_samples : Int
feedback : Float
mix : Float
} derive(Show)
///|
/// Effect node in the processing chain.
pub(all) enum EffectNode {
Lowpass(BiquadState)
Highpass(BiquadState)
Delay(DelayState)
} derive(Show)
///|
/// Cache eviction policy for audio assets.
pub(all) enum CachePolicy {
AlwaysCache
Normal
StreamPrefer
} derive(Eq, Show)
///|
/// Single entry in the asset cache.
pub(all) struct CacheEntry {
buffer : AudioBuffer
policy : CachePolicy
byte_size : Int
mut last_access : Int
} derive(Show)
///|
/// LRU asset cache for audio buffers.
pub(all) struct AssetCache {
entries : Map[String, CacheEntry]
mut total_bytes : Int
max_bytes : Int
max_entries : Int
mut access_counter : Int
} derive(Show)
///|
/// LSB-first bit reader for Vorbis bitstream parsing.
struct BitReader {
data : Bytes
length_bits : Int
mut bit_pos : Int
} derive(Show)
///|
/// OGG page header.
struct OggPage {
header_type : Int
granule_position : Int64
serial_number : Int
page_sequence : Int
segments : Array[Bytes]
} derive(Show)
///|
/// OGG container demuxer.
struct OggDemuxer {
data : Bytes
mut offset : Int
mut current_packet : Array[Bytes]
} derive(Show)
///|
/// Audio subsystem errors.
pub suberror AudioError {
InvalidFormat(String)
DecodeFailed(String)
}
///|
pub impl Show for AudioError with output(self, logger) {
match self {
InvalidFormat(msg) => logger.write_string("InvalidFormat(\{msg})")
DecodeFailed(msg) => logger.write_string("DecodeFailed(\{msg})")
}
}
///|
test "AudioError display" {
let err : AudioError = InvalidFormat("bad header")
inspect(err, content="InvalidFormat(bad header)")
let err2 : AudioError = DecodeFailed("truncated")
inspect(err2, content="DecodeFailed(truncated)")
}