// Utility functions for WAV audio
///|
/// Format WAV info as a human-readable string.
pub fn format_wav_info(info : WavInfo) -> String {
let format_str = match info.audio_format {
1 => "PCM"
3 => "IEEE Float"
_ => "Unknown (" + info.audio_format.to_string() + ")"
}
let channel_str = match info.num_channels {
1 => "Mono"
2 => "Stereo"
_ => info.num_channels.to_string() + " channels"
}
let duration = if info.byte_rate > 0 {
Double::from_int(info.data_size) / Double::from_int(info.byte_rate)
} else {
0.0
}
"Format: " + format_str +
"\nChannels: " + channel_str +
"\nSample Rate: " + info.sample_rate.to_string() + " Hz" +
"\nBits Per Sample: " + info.bits_per_sample.to_string() +
"\nByte Rate: " + info.byte_rate.to_string() +
"\nBlock Align: " + info.block_align.to_string() +
"\nData Size: " + info.data_size.to_string() + " bytes" +
"\nDuration: " + duration.to_string() + " seconds"
}
///|
/// Split stereo samples into left and right channels.
///
/// # Arguments
/// * `samples` - Interleaved stereo samples [L0, R0, L1, R1, ...]
///
/// # Returns
/// * `(Array[Int], Array[Int])` - (left_channel, right_channel)
pub fn split_stereo(samples : Array[Int]) -> (Array[Int], Array[Int]) {
let left = []
let right = []
let mut i = 0
while i + 1 < samples.length() {
left.push(samples[i])
right.push(samples[i + 1])
i = i + 2
}
(left, right)
}
///|
/// Combine left and right channels into interleaved stereo samples.
///
/// # Arguments
/// * `left` - Left channel samples
/// * `right` - Right channel samples
///
/// # Returns
/// * `Array[Int]` - Interleaved stereo samples [L0, R0, L1, R1, ...]
pub fn combine_stereo(
left : Array[Int],
right : Array[Int],
) -> Array[Int] {
let result = []
let max_len = if left.length() > right.length() { left.length() } else { right.length() }
let mut i = 0
while i < max_len {
let l = if i < left.length() { left[i] } else { 0 }
let r = if i < right.length() { right[i] } else { 0 }
result.push(l)
result.push(r)
i = i + 1
}
result
}
///|
/// Get the duration of audio in seconds.
pub fn get_duration(audio : AudioData) -> Double {
if audio.info.byte_rate > 0 {
Double::from_int(audio.info.data_size) / Double::from_int(audio.info.byte_rate)
} else {
0.0
}
}
///|
/// Get the number of frames (samples per channel).
pub fn get_frame_count(audio : AudioData) -> Int {
if audio.info.num_channels > 0 {
audio.samples.length() / audio.info.num_channels
} else {
audio.samples.length()
}
}
///|
/// Extract a single channel from multi-channel audio.
///
/// # Arguments
/// * `samples` - Interleaved multi-channel samples
/// * `num_channels` - Total number of channels
/// * `channel` - Channel index to extract (0-based)
///
/// # Returns
/// * `Array[Int]` - Extracted channel samples
pub fn extract_channel(
samples : Array[Int],
num_channels : Int,
channel : Int,
) -> Array[Int] {
let result = []
let mut i = channel
while i < samples.length() {
result.push(samples[i])
i = i + num_channels
}
result
}
///|
/// Mix multiple audio streams with equal weight.
///
/// # Arguments
/// * `streams` - Array of audio sample arrays
///
/// # Returns
/// * `Array[Int]` - Mixed samples
pub fn mix_multiple(streams : Array[Array[Int]]) -> Array[Int] {
if streams.length() == 0 {
return []
}
// Find max length
let mut max_len = 0
let mut i = 0
while i < streams.length() {
if streams[i].length() > max_len {
max_len = streams[i].length()
}
i = i + 1
}
let result = []
let count = Double::from_int(streams.length())
let mut pos = 0
while pos < max_len {
let mut sum = 0.0
let mut j = 0
while j < streams.length() {
if pos < streams[j].length() {
sum = sum + Double::from_int(streams[j][pos])
}
j = j + 1
}
result.push((sum / count).to_int())
pos = pos + 1
}
result
}
///|
/// Repeat audio samples a specified number of times.
///
/// # Arguments
/// * `samples` - Input audio samples
/// * `times` - Number of times to repeat
///
/// # Returns
/// * `Array[Int]` - Repeated samples
pub fn repeat(samples : Array[Int], times : Int) -> Array[Int] {
let result = []
let mut t = 0
while t < times {
let mut i = 0
while i < samples.length() {
result.push(samples[i])
i = i + 1
}
t = t + 1
}
result
}
///|
/// Pad audio with silence to reach a target length.
///
/// # Arguments
/// * `samples` - Input audio samples
/// * `target_length` - Target number of samples
///
/// # Returns
/// * `Array[Int]` - Padded samples
pub fn pad_to_length(
samples : Array[Int],
target_length : Int,
) -> Array[Int] {
let result = []
let mut i = 0
while i < target_length {
if i < samples.length() {
result.push(samples[i])
} else {
result.push(0)
}
i = i + 1
}
result
}
///|
/// Calculate the cross-correlation between two signals at zero lag.
/// Useful for comparing similarity of two audio signals.
pub fn cross_correlation_zero_lag(a : Array[Int], b : Array[Int]) -> Double {
let len = if a.length() < b.length() { a.length() } else { b.length() }
if len == 0 {
return 0.0
}
let mut sum = 0.0
let mut i = 0
while i < len {
sum = sum + Double::from_int(a[i]) * Double::from_int(b[i])
i = i + 1
}
sum / Double::from_int(len)
}