///|
fn clamp_unit(value : Double) -> Double {
if value > 1.0 {
1.0
} else if value < -1.0 {
-1.0
} else {
value
}
}
///|
fn normalize_pcm_sample(sample : Int, bits : Int) -> Double {
match bits {
8 => (sample.to_double() - 128.0) / 128.0
16 => sample.to_double() / 32768.0
24 => sample.to_double() / 8388608.0
32 => sample.to_double() / 2147483648.0
_ => 0.0
}
}
///|
fn read_pcm_sample(reader : ByteReader, offset : Int, bits : Int) -> Int {
match bits {
8 => reader.byte_at(offset)
16 => reader.i16_le_at(offset)
24 => reader.i24_le_at(offset)
32 => reader.i32_le_at(offset)
_ => 0
}
}
///|
fn pow2(exp : Int) -> Double {
if exp == 0 {
1.0
} else if exp > 0 {
for _ in 0.. Double {
let sign = if (bits & 0x80000000) != 0 { -1.0 } else { 1.0 }
let exponent = (bits >> 23) & 0xff
let fraction = bits & 0x7fffff
if exponent == 0 && fraction == 0 {
0.0 * sign
} else if exponent == 255 {
0.0
} else if exponent == 0 {
sign * (fraction.to_double() / 8388608.0) * pow2(-126)
} else {
sign * (1.0 + fraction.to_double() / 8388608.0) * pow2(exponent - 127)
}
}
///|
fn read_float32_sample(reader : ByteReader, offset : Int) -> Double {
decode_ieee_float32_bits(reader.u32_le_at(offset))
}
///|
fn decode_to_float_buffer(
bytes : Array[Int],
wav : ParsedWav,
options : DecodeOptions,
) -> FloatBuffer {
let reader = ByteReader::new(bytes)
let bytes_per_sample = wav.format.bytes_per_sample()
let sample_count = wav.data_size / bytes_per_sample
let samples = Array::makei(sample_count, i => {
let offset = wav.data_offset + i * bytes_per_sample
let value = if wav.format.is_float() {
read_float32_sample(reader, offset)
} else {
normalize_pcm_sample(
read_pcm_sample(reader, offset, wav.format.bits_per_sample),
wav.format.bits_per_sample,
)
}
if options.clamp {
clamp_unit(value)
} else {
value
}
})
FloatBuffer::new(wav.format.channels, wav.format.sample_rate, samples)
}
///|
fn decode_to_pcm_buffer(bytes : Array[Int], wav : ParsedWav) -> PcmBuffer {
let reader = ByteReader::new(bytes)
let bytes_per_sample = wav.format.bytes_per_sample()
let sample_count = wav.data_size / bytes_per_sample
let samples = Array::makei(sample_count, i => {
let offset = wav.data_offset + i * bytes_per_sample
if wav.format.is_float() {
(read_float32_sample(reader, offset) * 2147483647.0).round().to_int()
} else {
read_pcm_sample(reader, offset, wav.format.bits_per_sample)
}
})
PcmBuffer::new(
wav.format.channels,
wav.format.sample_rate,
wav.format.bits_per_sample,
samples,
)
}
///|
pub fn decode_pcm(
bytes : Array[Int],
wav : ParsedWav,
options? : DecodeOptions = DecodeOptions::new(),
) -> DecodeResult {
if !options.is_valid() {
DecodeResult::failure(
WavError::new(ErrorInvalidArgument, "invalid decode options"),
)
} else if !wav.is_complete() {
DecodeResult::failure(
WavError::new(ErrorInvalidFormat, "parsed wav is incomplete"),
)
} else if !wav.format.is_supported() {
DecodeResult::failure(
WavError::new(ErrorUnsupportedFormat, "unsupported WAV sample format"),
)
} else if wav.data_size % wav.format.block_align != 0 {
DecodeResult::failure(
WavError::new(ErrorTruncatedData, "data chunk has partial frame"),
)
} else {
DecodeResult::success(
decode_to_pcm_buffer(bytes, wav),
decode_to_float_buffer(bytes, wav, options),
)
}
}
///|
pub fn parse_and_decode(
bytes : Array[Int],
options? : DecodeOptions = DecodeOptions::new(),
) -> DecodeResult {
let parsed = parse_wav(bytes)
if !parsed.ok {
DecodeResult::failure(parsed.error)
} else {
decode_pcm(bytes, parsed.wav, options~)
}
}
///|
pub fn split_channel(buffer : FloatBuffer, channel : Int) -> Array[Double] {
if !buffer.is_valid() || channel < 0 || channel >= buffer.channels {
[]
} else {
Array::makei(buffer.frame_count(), frame => {
buffer.samples[frame * buffer.channels + channel]
})
}
}
///|
pub fn downmix_to_mono(buffer : FloatBuffer) -> FloatBuffer {
if !buffer.is_valid() {
FloatBuffer::empty()
} else if buffer.channels == 1 {
buffer
} else {
let samples = Array::makei(buffer.frame_count(), frame => {
let sum = for channel in 0..