// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Dynamic audio buffer passed to stream callbacks (subset).
///
/// This is a MoonBit-specific API surface (not present in upstream `cpal`). It exists to
/// represent input/output audio buffers without relying on Rust-style generic slice callbacks.
///
/// Current scope:
/// - store raw bytes + `SampleFormat`
/// - safe typed decoders for common PCM formats, returning `Option`
/// - signed: I8/I16/I24/I32/I64
/// - unsigned: U8/U16/U24/U32/U64
/// - float: F32/F64
/// - (DSD formats are intentionally not decoded at this layer yet)
///|
pub struct Data {
sample_format : SampleFormat
bytes : FixedArray[Byte]
} derive(Debug, Eq)
///|
pub fn Data::Data(
sample_format : SampleFormat,
bytes : FixedArray[Byte],
) -> Data {
{ sample_format, bytes }
}
///|
pub fn Data::new(
sample_format : SampleFormat,
bytes : FixedArray[Byte],
) -> Data {
Data(sample_format, bytes)
}
///|
pub fn Data::sample_format(self : Data) -> SampleFormat {
self.sample_format
}
///|
/// Number of samples in the buffer (floor if the byte length is not aligned to the sample size).
pub fn Data::len(self : Data) -> Int {
let sz = self.sample_format.sample_size()
if sz <= 0 {
0
} else {
self.bytes.length() / sz
}
}
///|
pub fn Data::bytes(self : Data) -> FixedArray[Byte] {
self.bytes
}
///|
fn read_u16_le(bytes : FixedArray[Byte], offset : Int) -> UInt16? {
match (bytes.get(offset), bytes.get(offset + 1)) {
(Some(b0), Some(b1)) => {
let v = b0.to_int() | (b1.to_int() << 8)
Some(v.to_uint16())
}
_ => None
}
}
///|
fn read_u32_le(bytes : FixedArray[Byte], offset : Int) -> UInt? {
match
(
bytes.get(offset),
bytes.get(offset + 1),
bytes.get(offset + 2),
bytes.get(offset + 3),
) {
(Some(b0), Some(b1), Some(b2), Some(b3)) => {
let w = b0.to_uint() |
(b1.to_uint() << 8) |
(b2.to_uint() << 16) |
(b3.to_uint() << 24)
Some(w)
}
_ => None
}
}
///|
fn read_u64_le(bytes : FixedArray[Byte], offset : Int) -> UInt64? {
match
(
bytes.get(offset),
bytes.get(offset + 1),
bytes.get(offset + 2),
bytes.get(offset + 3),
bytes.get(offset + 4),
bytes.get(offset + 5),
bytes.get(offset + 6),
bytes.get(offset + 7),
) {
(
Some(b0),
Some(b1),
Some(b2),
Some(b3),
Some(b4),
Some(b5),
Some(b6),
Some(b7),
) => {
let lo : UInt64 = b0.to_uint64() |
(b1.to_uint64() << 8) |
(b2.to_uint64() << 16) |
(b3.to_uint64() << 24)
let hi : UInt64 = b4.to_uint64() |
(b5.to_uint64() << 8) |
(b6.to_uint64() << 16) |
(b7.to_uint64() << 24)
Some(lo | (hi << 32))
}
_ => None
}
}
///|
fn write_u16_le(bytes : FixedArray[Byte], offset : Int, v : UInt16) -> Bool {
if offset < 0 || offset + 1 >= bytes.length() {
return false
}
let w = v.to_uint().reinterpret_as_int()
bytes[offset] = (w & 0xff).to_byte()
bytes[offset + 1] = ((w >> 8) & 0xff).to_byte()
true
}
///|
fn write_u32_le(bytes : FixedArray[Byte], offset : Int, v : UInt) -> Bool {
if offset < 0 || offset + 3 >= bytes.length() {
return false
}
let w = v.reinterpret_as_int()
bytes[offset] = (w & 0xff).to_byte()
bytes[offset + 1] = ((w >> 8) & 0xff).to_byte()
bytes[offset + 2] = ((w >> 16) & 0xff).to_byte()
bytes[offset + 3] = ((w >> 24) & 0xff).to_byte()
true
}
///|
fn write_u64_le(bytes : FixedArray[Byte], offset : Int, v : UInt64) -> Bool {
if offset < 0 || offset + 7 >= bytes.length() {
return false
}
let w0 = (v & (0xffffffff : UInt64)).to_uint()
let w1 = (v >> 32).to_uint()
if !write_u32_le(bytes, offset, w0) {
return false
}
if !write_u32_le(bytes, offset + 4, w1) {
return false
}
true
}
///|
pub fn Data::try_as_i8(self : Data) -> Array[Int]? {
if self.sample_format != I8 {
return None
}
let out : Array[Int] = []
for b in self.bytes.op_as_view() {
let w = b.to_int()
out.push(if w >= 128 { w - 256 } else { w })
}
Some(out)
}
///|
pub fn Data::try_as_u8(self : Data) -> Array[Byte]? {
if self.sample_format != U8 {
return None
}
Some(self.bytes.op_as_view().to_owned())
}
///|
pub fn Data::try_as_u16(self : Data) -> Array[UInt16]? {
if self.sample_format != U16 {
return None
}
if self.bytes.length() % 2 != 0 {
return None
}
let n = self.bytes.length() / 2
let out : Array[UInt16] = []
let mut i = 0
while i < n {
let off = i * 2
match read_u16_le(self.bytes, off) {
None => return None
Some(v) => out.push(v)
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_i16(self : Data) -> Array[Int16]? {
if self.sample_format != I16 {
return None
}
if self.bytes.length() % 2 != 0 {
return None
}
let n = self.bytes.length() / 2
let out : Array[Int16] = []
let mut i = 0
while i < n {
let off = i * 2
match read_u16_le(self.bytes, off) {
None => return None
Some(v) => out.push(Int16::reinterpret_from_uint16(v))
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_u24(self : Data) -> Array[U24]? {
if self.sample_format != U24 {
return None
}
if self.bytes.length() % 4 != 0 {
return None
}
let n = self.bytes.length() / 4
let out : Array[U24] = []
let mut i = 0
while i < n {
let off = i * 4
match read_u32_le(self.bytes, off) {
None => return None
Some(bits) => {
let raw = (bits & (0x00ff_ffff : UInt)).reinterpret_as_int()
out.push(U24(raw))
}
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_i24(self : Data) -> Array[I24]? {
if self.sample_format != I24 {
return None
}
if self.bytes.length() % 4 != 0 {
return None
}
let n = self.bytes.length() / 4
let out : Array[I24] = []
let mut i = 0
while i < n {
let off = i * 4
match read_u32_le(self.bytes, off) {
None => return None
Some(bits) => {
let u = bits & (0x00ff_ffff : UInt)
let raw = if (u & (0x0080_0000 : UInt)) != (0 : UInt) {
(u | (0xff00_0000 : UInt)).reinterpret_as_int()
} else {
u.reinterpret_as_int()
}
out.push(I24(raw))
}
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_u32(self : Data) -> Array[UInt]? {
if self.sample_format != U32 {
return None
}
if self.bytes.length() % 4 != 0 {
return None
}
let n = self.bytes.length() / 4
let out : Array[UInt] = []
let mut i = 0
while i < n {
let off = i * 4
match read_u32_le(self.bytes, off) {
None => return None
Some(v) => out.push(v)
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_i32(self : Data) -> Array[Int]? {
if self.sample_format != I32 {
return None
}
if self.bytes.length() % 4 != 0 {
return None
}
let n = self.bytes.length() / 4
let out : Array[Int] = []
let mut i = 0
while i < n {
let off = i * 4
match read_u32_le(self.bytes, off) {
None => return None
Some(bits) => out.push(bits.reinterpret_as_int())
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_f32(self : Data) -> Array[Float]? {
if self.sample_format != F32 {
return None
}
if self.bytes.length() % 4 != 0 {
return None
}
let n = self.bytes.length() / 4
let out : Array[Float] = []
let mut i = 0
while i < n {
let off = i * 4
match read_u32_le(self.bytes, off) {
None => return None
Some(bits) => out.push(Float::reinterpret_from_uint(bits))
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_u64(self : Data) -> Array[UInt64]? {
if self.sample_format != U64 {
return None
}
if self.bytes.length() % 8 != 0 {
return None
}
let n = self.bytes.length() / 8
let out : Array[UInt64] = []
let mut i = 0
while i < n {
let off = i * 8
match read_u64_le(self.bytes, off) {
None => return None
Some(v) => out.push(v)
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_i64(self : Data) -> Array[Int64]? {
if self.sample_format != I64 {
return None
}
if self.bytes.length() % 8 != 0 {
return None
}
let n = self.bytes.length() / 8
let out : Array[Int64] = []
let mut i = 0
while i < n {
let off = i * 8
match read_u64_le(self.bytes, off) {
None => return None
Some(bits) => out.push(bits.reinterpret_as_int64())
}
i = i + 1
}
Some(out)
}
///|
pub fn Data::try_as_f64(self : Data) -> Array[Double]? {
if self.sample_format != F64 {
return None
}
if self.bytes.length() % 8 != 0 {
return None
}
let n = self.bytes.length() / 8
let out : Array[Double] = []
let mut i = 0
while i < n {
let off = i * 8
match read_u64_le(self.bytes, off) {
None => return None
Some(bits) => out.push(bits.reinterpret_as_double())
}
i = i + 1
}
Some(out)
}
///|
/// Fill the underlying buffer with the sample-format equilibrium value (silence).
pub fn Data::clear(self : Data) -> Unit {
match self.sample_format {
U8 => {
// U8 equilibrium is 128 (unsigned).
let mut i = 0
while i < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x80)
i = i + 1
}
}
U16 => {
// U16 equilibrium is 0x8000 (little-endian bytes: 00 80).
let mut i = 0
while i + 1 < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
self.bytes[i + 1] = Int::to_byte(0x80)
i = i + 2
}
if i < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
}
}
U24 => {
// U24 equilibrium is 0x800000 (little-endian bytes: 00 00 80).
let mut i = 0
while i + 2 < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
self.bytes[i + 1] = Int::to_byte(0x00)
self.bytes[i + 2] = Int::to_byte(0x80)
i = i + 3
}
while i < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
i = i + 1
}
}
U32 => {
// U32 equilibrium is 0x80000000 (little-endian bytes: 00 00 00 80).
let mut i = 0
while i + 3 < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
self.bytes[i + 1] = Int::to_byte(0x00)
self.bytes[i + 2] = Int::to_byte(0x00)
self.bytes[i + 3] = Int::to_byte(0x80)
i = i + 4
}
while i < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
i = i + 1
}
}
U64 => {
// U64 equilibrium is 0x8000_0000_0000_0000.
let mut i = 0
while i + 7 < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
self.bytes[i + 1] = Int::to_byte(0x00)
self.bytes[i + 2] = Int::to_byte(0x00)
self.bytes[i + 3] = Int::to_byte(0x00)
self.bytes[i + 4] = Int::to_byte(0x00)
self.bytes[i + 5] = Int::to_byte(0x00)
self.bytes[i + 6] = Int::to_byte(0x00)
self.bytes[i + 7] = Int::to_byte(0x80)
i = i + 8
}
while i < self.bytes.length() {
self.bytes[i] = Int::to_byte(0x00)
i = i + 1
}
}
_ => {
// Signed integer and float equilibrium is 0.
let mut i = 0
while i < self.bytes.length() {
self.bytes[i] = (0 : Int).to_byte()
i = i + 1
}
}
}
}
///|
/// Best-effort encoder for I8 output buffers.
pub fn Data::write_i8(self : Data, samples : Array[Int]) -> Bool {
if self.sample_format != I8 {
return false
}
if self.bytes.length() != samples.length() {
return false
}
let mut i = 0
while i < samples.length() {
let v = samples[i]
if v < -128 || v > 127 {
return false
}
let u = if v < 0 { v + 256 } else { v }
self.bytes[i] = u.to_byte()
i = i + 1
}
true
}
///|
/// Best-effort encoder for U8 output buffers.
pub fn Data::write_u8(self : Data, samples : Array[Byte]) -> Bool {
if self.sample_format != U8 {
return false
}
if self.bytes.length() != samples.length() {
return false
}
let mut i = 0
while i < samples.length() {
self.bytes[i] = samples[i]
i = i + 1
}
true
}
///|
/// Best-effort encoder for U16 (little-endian) output buffers.
pub fn Data::write_u16(self : Data, samples : Array[UInt16]) -> Bool {
if self.sample_format != U16 {
return false
}
if self.bytes.length() != samples.length() * 2 {
return false
}
let mut i = 0
while i < samples.length() {
if !write_u16_le(self.bytes, i * 2, samples[i]) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for I16 (little-endian) output buffers.
pub fn Data::write_i16(self : Data, samples : Array[Int16]) -> Bool {
if self.sample_format != I16 {
return false
}
if self.bytes.length() != samples.length() * 2 {
return false
}
let mut i = 0
while i < samples.length() {
if !write_u16_le(self.bytes, i * 2, samples[i].reinterpret_as_uint16()) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for U24 (little-endian, stored in the low 24 bits of a 32-bit word).
pub fn Data::write_u24(self : Data, samples : Array[U24]) -> Bool {
if self.sample_format != U24 {
return false
}
if self.bytes.length() != samples.length() * 4 {
return false
}
let mut i = 0
while i < samples.length() {
let raw = samples[i].to_int()
if raw < 0 || raw > 16_777_215 {
return false
}
if !write_u32_le(self.bytes, i * 4, raw.reinterpret_as_uint()) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for I24 (little-endian, stored in the low 24 bits of a 32-bit word).
pub fn Data::write_i24(self : Data, samples : Array[I24]) -> Bool {
if self.sample_format != I24 {
return false
}
if self.bytes.length() != samples.length() * 4 {
return false
}
let mut i = 0
while i < samples.length() {
let raw = samples[i].to_int()
if raw < -8_388_608 || raw > 8_388_607 {
return false
}
let u = if raw < 0 { raw + 16_777_216 } else { raw }
if !write_u32_le(self.bytes, i * 4, u.reinterpret_as_uint()) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for U32 (little-endian).
pub fn Data::write_u32(self : Data, samples : Array[UInt]) -> Bool {
if self.sample_format != U32 {
return false
}
if self.bytes.length() != samples.length() * 4 {
return false
}
let max_u32 = (0xffff_ffff : UInt)
let mut i = 0
while i < samples.length() {
let v = samples[i]
if v > max_u32 {
return false
}
if !write_u32_le(self.bytes, i * 4, v) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for I32 (little-endian).
pub fn Data::write_i32(self : Data, samples : Array[Int]) -> Bool {
if self.sample_format != I32 {
return false
}
if self.bytes.length() != samples.length() * 4 {
return false
}
let mut i = 0
while i < samples.length() {
let v = samples[i]
if !write_u32_le(self.bytes, i * 4, v.reinterpret_as_uint()) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for F32 (IEEE-754, little-endian) output buffers.
pub fn Data::write_f32(self : Data, samples : Array[Float]) -> Bool {
if self.sample_format != F32 {
return false
}
if self.bytes.length() != samples.length() * 4 {
return false
}
let mut i = 0
while i < samples.length() {
if !write_u32_le(self.bytes, i * 4, samples[i].reinterpret_as_uint()) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for U64 (little-endian).
pub fn Data::write_u64(self : Data, samples : Array[UInt64]) -> Bool {
if self.sample_format != U64 {
return false
}
if self.bytes.length() != samples.length() * 8 {
return false
}
let mut i = 0
while i < samples.length() {
if !write_u64_le(self.bytes, i * 8, samples[i]) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for I64 (little-endian).
pub fn Data::write_i64(self : Data, samples : Array[Int64]) -> Bool {
if self.sample_format != I64 {
return false
}
if self.bytes.length() != samples.length() * 8 {
return false
}
let mut i = 0
while i < samples.length() {
if !write_u64_le(self.bytes, i * 8, samples[i].reinterpret_as_uint64()) {
return false
}
i = i + 1
}
true
}
///|
/// Best-effort encoder for F64 (IEEE-754, little-endian).
pub fn Data::write_f64(self : Data, samples : Array[Double]) -> Bool {
if self.sample_format != F64 {
return false
}
if self.bytes.length() != samples.length() * 8 {
return false
}
let mut i = 0
while i < samples.length() {
if !write_u64_le(self.bytes, i * 8, samples[i].reinterpret_as_uint64()) {
return false
}
i = i + 1
}
true
}