///|
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 : Int32, bits : Int32) -> 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 : Int32, bits : Int32) -> Int32 {
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 decode_ieee_float32_bits(bits : Int32) -> Double {
// Preserve the decoder's silence policy for NaN and infinity. Widening
// the reinterpreted Float retains all finite binary32 values and signed zero.
if (bits & 0x7f800000) == 0x7f800000 {
0.0
} else {
Float::reinterpret_from_int(bits).to_double()
}
}
///|
fn decode_samples(
bytes : Array[Int32],
wav : ParsedWav,
options : DecodeOptions,
) -> DecodeResult {
let reader = ByteReader::new(bytes)
let width = wav.format.bytes_per_sample()
let count = wav.data_size / width
let pcm = Array::make(count, 0)
let floats = Array::make(count, 0.0)
let is_float = wav.format.is_float()
for i in 0.. 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 options.target_channels != 0 &&
options.target_channels != wav.format.channels {
DecodeResult::failure(
WavError::new(
ErrorInvalidArgument,
"decode does not perform channel conversion; use zero or source channels",
),
)
} else if wav.data_offset < 0 ||
wav.data_size < 0 ||
wav.data_offset > bytes.length() ||
wav.data_size > bytes.length() - wav.data_offset {
DecodeResult::failure(
WavError::new(
ErrorTruncatedData,
"data chunk extends beyond the provided byte array",
offset=wav.data_offset,
),
)
} 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 {
decode_samples(bytes, wav, options)
}
}
///|
pub fn parse_and_decode(
bytes : Array[Int32],
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 : Int32) -> 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..