~/bend-docscommunity

main.bend source

main.bend on the hub · documented module

# ezaudio: audio for Bend 2, reading and writing WAV (RIFF PCM) and MP3 (MPEG-1 Layer III).## A Clip is a sample rate, a channel count, a sample format, and# interleaved PCM frames. WAV reads and writes RIFF PCM. MP3 reads and writes# MPEG-1 Layer III.import Baseimport ./src/wav.bend as Wavimport ./src/mp3.bend as Mp3# signed 16-bit PCM, or IEEE 754 binary32type Format is Data:  S16{}  Flt{}# one clip. pcm is interleaved. s16 lives in the low 16 bits.type Clip is Data:  Clip{hz: U32, nch: U32, fmt: Format, pcm: List<&2, U32>}# signed 16-bit little-endian PCMdef s16() -> Format:  S16{}# IEEE 754 binary32, stored as its bitsdef f32() -> Format:  Flt{}def kind.of(fmt: Format) -> U32:  match fmt:    case S16{}:      1    case Flt{}:      3def fmt.of(+kind: U32) -> Format:  match kind:    case 1:      S16{}    case _:      Flt{}def wav.clip(w: Wav.Wav) -> Clip:  match w:    case Wav.Wav{+rate, +channels, +kind, frames}:      Clip{rate, channels, fmt.of(kind), frames}def fmt.same(aa: Format, bb: Format) -> Bool:  match aa bb:    case S16{} S16{}:      True{}    case Flt{} Flt{}:      True{}    case S16{} Flt{}:      False{}    case Flt{} S16{}:      False{}# a clip from its rate, channel count, format, and interleaved framesdef clip(+rate: U32, +channels: U32, format: Format, frames: List<&2, U32>) -> Clip:  Clip{rate, channels, format, frames}# the sample rate, in hertzdef rate(c: Clip) -> U32:  match c:    case Clip{+hz, _nch, _fmt, _pcm}:      hz# how many channels are interleaveddef channels(c: Clip) -> U32:  match c:    case Clip{_hz, +nch, _fmt, _pcm}:      nch# s16 or f32def format(c: Clip) -> Format:  match c:    case Clip{_hz, _nch, +fmt, _pcm}:      fmt# the interleaved framesdef frames(c: Clip) -> List<&2, U32>:  match c:    case Clip{_hz, _nch, _fmt, +pcm}:      pcmdef count.of(+channels: U32, +nsamples: U32) -> U32:  match channels:    case 0:      0    case _:      U32.div(nsamples, channels)# how many frames. zero channels count as none.def count(c: Clip) -> U32:  match c:    case Clip{_hz, +nch, _fmt, +pcm}:      count.of(nch, U32.from_nat(List.length(&2, U32, pcm)))# one sample, repeated across nframes framesdef fill(+rate: U32, +channels: U32, format: Format, +sample: U32, +nframes: U32) -> Clip:  Clip{rate, channels, format, List.replicate(U32, U32.to_nat((nframes * channels : U32)), sample)}def ls.drop(hop: Nat, xs: List<&2, U32>) -> List<&2, U32>:  match hop:    case 0n:      xs    case 1n+p:      match xs:        case Nil{}:          []        case _hd <> tl:          ls.drop(p, tl)def ls.take(hop: Nat, xs: List<&2, U32>, acc: List<&2, U32>) -> List<&2, U32>:  match hop:    case 0n:      List.reverse(&2, U32, acc)    case 1n+p:      match xs:        case Nil{}:          List.reverse(&2, U32, acc)        case +hd <> tl:          ls.take(p, tl, hd <> acc)def ls.slice(xs: List<&2, U32>, +dropn: U32, +taken: U32) -> List<&2, U32>:  ls.take(U32.to_nat(taken), ls.drop(U32.to_nat(dropn), xs), [])def trim.go(  ok: Bool,  +rate: U32,  +channels: U32,  format: Format,  frames: List<&2, U32>,  +start: U32,  +nframes: U32) -> Maybe<&2, Clip>:  match ok:    case False{}:      None{}    case True{}:      Some{Clip{rate, channels, format,        ls.slice(frames, (start * channels : U32), (nframes * channels : U32))}}# frames [start, start + nframes). none when that span passes the end.def trim(c: Clip, +start: U32, +nframes: U32) -> Maybe<&2, Clip>:  match c:    case Clip{+hz, +nch, fmt, +pcm}:      trim.go(U32.is_le((start + nframes : U32), count.of(nch, U32.from_nat(List.length(&2, U32, pcm)))), hz, nch,        fmt, pcm, start, nframes)def eq.all(ar: Bool, ac: Bool, af: Bool) -> Bool:  match ar:    case False{}:      False{}    case True{}:      match ac:        case False{}:          False{}        case True{}:          afdef concat.go(  ok: Bool,  +rate: U32,  +channels: U32,  format: Format,  left: List<&2, U32>,  right: List<&2, U32>) -> Maybe<&2, Clip>:  match ok:    case False{}:      None{}    case True{}:      Some{Clip{rate, channels, format, List.append(&2, U32, left, right)}}def concat.pair(+rate: U32, +channels: U32, +format: Format, left: List<&2, U32>, other: Clip) -> Maybe<&2, Clip>:  match other:    case Clip{+rb, +cb, fb, right}:      concat.go(eq.all(U32.is_eq(rate, rb), U32.is_eq(channels, cb), fmt.same(format, fb)), rate, channels, format,        left, right)# the frames of both clips, or none when rate, channels, or format differdef concat(left: Clip, right: Clip) -> Maybe<&2, Clip>:  match left:    case Clip{+hz, +nch, +fmt, pcm}:      concat.pair(hz, nch, fmt, pcm, right)def list.empty(xs: List<&2, U32>) -> Bool:  match xs:    case Nil{}:      True{}    case _hd <> _tl:      False{}def wav.bytes(empty: Bool, +rate: U32, +channels: U32, +kind: U32, frames: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match empty:    case True{}:      None{}    case False{}:      Wav.wav.write(rate, channels, kind, frames)def wav.parsed(got: Maybe<&2, Wav.Wav>) -> Maybe<&2, Clip>:  match got:    case None{}:      None{}    case Some{w}:      Some{wav.clip(w)}# a RIFF WAVE clip, or none when the bytes are not mono or stereo PCM or floatdef decode_wav(xs: List<&2, U32>) -> Maybe<&2, Clip>:  wav.parsed(Wav.wav.parse(xs))# a RIFF WAVE file. none when there are no frames, or the rate or channels are unfit.def encode_wav(c: Clip) -> Maybe<&2, List<&2, U32>>:  match c:    case Clip{+hz, +nch, fmt, +pcm}:      wav.bytes(list.empty(pcm), hz, nch, kind.of(fmt), pcm)def mp3.clip(p: Mp3.Pcm) -> Clip:  match p:    case Mp3.Pcm{+hz, +ch, pcm}:      Clip{hz, ch, Flt{}, pcm}def mp3.parsed(got: Maybe<&2, Mp3.Pcm>) -> Maybe<&2, Clip>:  match got:    case None{}:      None{}    case Some{p}:      Some{mp3.clip(p)}# MPEG-1 Layer III to f32 frames. Layer I, Layer II, joint stereo, and a CRC are none.def decode_mp3(xs: List<&2, U32>) -> Maybe<&2, Clip>:  mp3.parsed(Mp3.mp3.decode(xs))# MPEG-1 Layer III at 320 kbit/s. rates are 32000, 44100, and 48000. none when unfit or empty.def encode_mp3(c: Clip) -> Maybe<&2, List<&2, U32>>:  match c:    case Clip{+hz, +nch, fmt, pcm}:      Mp3.mp3.encode(hz, nch, kind.of(fmt), pcm)