///|
/// Compute Vorbis window function value.
/// w(i) = sin(π/2 · sin²(π(i+0.5)/n))
fn vorbis_window(i : Int, n : Int) -> Float {
let pi = Float::from_double(@math.PI)
let half_pi = pi / (2.0 : Float)
let inner = pi * (Float::from_int(i) + 0.5) / Float::from_int(n)
let sin_inner = @math.sinf(inner)
let sin_sq = sin_inner * sin_inner
@math.sinf(half_pi * sin_sq)
}
///|
/// Apply the Vorbis window function to a buffer in place.
fn apply_vorbis_window(data : Array[Float], n : Int) -> Unit {
for i in 0.. (Array[Float], Array[Float]) raise AudioError {
let br = new_bit_reader(packet)
// Packet type must be 0 (audio)
let packet_type = read_bit(br)
if packet_type {
raise AudioError::DecodeFailed("Vorbis: not an audio packet")
}
// Mode number
let mode_bits = ilog(info.modes.length() - 1)
let mode_number = read_bits(br, mode_bits)
if mode_number >= info.modes.length() {
raise AudioError::DecodeFailed("Vorbis: invalid mode number")
}
let mode = info.modes[mode_number]
let n = if mode.block_flag { info.blocksize_1 } else { info.blocksize_0 }
let half_n = n / 2
let mapping = info.mappings[mode.mapping]
// Decode floors
let floors_data : Array[Array[Float]?] = []
let no_residue : Array[Bool] = []
for ch in 0.. {
floors_data.push(Some(curve))
no_residue.push(false)
}
None => {
floors_data.push(None)
no_residue.push(true)
}
}
} else {
floors_data.push(None)
no_residue.push(true)
}
}
// Decode residues
let residue_output : Array[Array[Float]] = []
for submap_idx in 0.. 0 {
let res_data = decode_residue(
br,
info.residues[residue_idx],
info.codebooks,
half_n,
ch_count,
do_not_decode,
)
for c in res_data {
residue_output.push(c)
}
}
}
}
// Coupling
for step in (mapping.coupling_steps - 1)..<=0 {
if step < 0 {
break
}
let mag_ch = mapping.coupling_magnitude[step]
let ang_ch = mapping.coupling_angle[step]
if mag_ch < residue_output.length() && ang_ch < residue_output.length() {
for i in 0.. 0.0 {
if a > 0.0 {
(m, m - a)
} else {
(m + a, m)
}
} else if a > 0.0 {
(m, m + a)
} else {
(m - a, m)
}
residue_output[mag_ch][i] = new_m
residue_output[ang_ch][i] = new_a
}
}
}
// Floor multiply + IMDCT + windowing per channel
let left : Array[Float] = Array::make(n, (0.0 : Float))
let right : Array[Float] = Array::make(n, (0.0 : Float))
for ch in 0..
for i in 0..
for i in 0..= 0.0 { diff } else { -diff }
assert_true(abs_diff < 0.001)
}
}
///|
test "vorbis_window boundary values" {
let n = 32
// Window at edges should be near 0
let w0 = vorbis_window(0, n)
assert_true(w0 > 0.0 && w0 < 0.2)
// Window at center should be near 1
let w_mid = vorbis_window(n / 2, n)
assert_true(w_mid > 0.8)
}
///|
test "apply_vorbis_window" {
let data : Array[Float] = Array::make(8, (1.0 : Float))
apply_vorbis_window(data, 8)
// After windowing, edges should be attenuated
assert_true(data[0] < 0.5)
assert_true(data[7] < 0.5)
// Middle should be closer to 1
assert_true(data[3] > 0.5)
assert_true(data[4] > 0.5)
}