~/bend-docscommunity

wav.bend source

wav.bend on the hub · documented module

# RIFF WAVE. PCM is little-endian signed 16-bit. Float is IEEE binary32.import Base# a parsed wave. kind 1 is signed 16-bit, kind 3 is binary32. frames are interleaved.type Wav is Data:  Wav{rate: U32, channels: U32, kind: U32, frames: List<&2, U32>}# fmt fields that passed the PCM checkstype Fm is Data:  Fm{kind: U32, ch: U32, rate: U32}# the chunk walker: phase 0 tag, 1 size, 2 payload, 3 pad bytetype St is Data:  St{    phase: U32, left: U32, tag: U32, sz: U32, seen: U32, kind: U32, ch: U32,    rate: U32, acc: List<&2, U32>, frames: List<&2, U32>  }# 'RIFF'def tag.riff() -> U32:  1380533830# 'WAVE'def tag.wave() -> U32:  1463899717# 'fmt 'def tag.fmt() -> U32:  1718449184# 'data'def tag.data() -> U32:  1684108385def be.u32(+aa: U32, +bb: U32, +cc: U32, +dd: U32) -> U32:  ((((aa << 24n : U32) .|. (bb << 16n : U32) : U32) .|. (cc << 8n : U32) : U32) .|. dd : U32)def le.u16(+aa: U32, +bb: U32) -> U32:  (aa .|. (bb << 8n : U32) : U32)def le.u32(+aa: U32, +bb: U32, +cc: U32, +dd: U32) -> U32:  (le.u16(aa, bb) .|. (le.u16(cc, dd) << 16n : U32) : U32)def put.u16(+vv: U32) -> List<&2, U32>:  [(vv .&. 255 : U32), ((vv >> 8n : U32) .&. 255 : U32)]def put.u32(+vv: U32) -> List<&2, U32>:  List.append(&2, U32, put.u16(vv), put.u16((vv >> 16n : U32)))def put.be(+vv: U32) -> List<&2, U32>:  [((vv >> 24n : U32) .&. 255 : U32), ((vv >> 16n : U32) .&. 255 : U32),   ((vv >> 8n : U32) .&. 255 : U32), (vv .&. 255 : U32)]def put.lo(+vv: U32) -> U32:  (vv .&. 255 : U32)def put.hi(+vv: U32) -> U32:  ((vv >> 8n : U32) .&. 255 : U32)def put.b2(+vv: U32) -> U32:  ((vv >> 16n : U32) .&. 255 : U32)def put.b3(+vv: U32) -> U32:  ((vv >> 24n : U32) .&. 255 : U32)def word4.pick(big: Bool, +aa: U32, +bb: U32, +cc: U32, +dd: U32) -> U32:  match big:    case True{}:      be.u32(aa, bb, cc, dd)    case False{}:      le.u32(aa, bb, cc, dd)def word.of(xs: List<&2, U32>, +big: U32) -> U32:  match xs:    case +dd <> +cc <> +bb <> +aa <> _tl:      word4.pick(U32.is_eq(big, 1), aa, bb, cc, dd)    case _:      0def st.empty() -> St:  St{0, 4, 0, 0, 0, 0, 0, 0, [], []}def st.set(  +phase: U32, +left: U32, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32,  +rate: U32, acc: List<&2, U32>, frames: List<&2, U32>) -> St:  St{phase, left, tag, sz, seen, kind, ch, rate, acc, frames}def fmt.rate(ok: Bool, +kind: U32, +ch: U32, +rate: U32) -> Maybe<&2, Fm>:  match ok:    case True{}:      Some{Fm{kind, ch, rate}}    case False{}:      None{}def fmt.ok(+kind: U32, +ch: U32, +rate: U32, +br: U32, +al: U32, +bits: U32) -> Maybe<&2, Fm>:  match kind ch bits:    case 1 1 16:      fmt.rate(U32.is_eq(al, 2) && U32.is_eq(br, (rate * 2 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)    case 1 2 16:      fmt.rate(U32.is_eq(al, 4) && U32.is_eq(br, (rate * 4 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)    case 3 1 32:      fmt.rate(U32.is_eq(al, 4) && U32.is_eq(br, (rate * 4 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)    case 3 2 32:      fmt.rate(U32.is_eq(al, 8) && U32.is_eq(br, (rate * 8 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)    case _ _ _:      None{}def fmt.pack(  +k0: U32, +k1: U32, +c0: U32, +c1: U32, +r0: U32, +r1: U32, +r2: U32, +r3: U32,  +b0: U32, +b1: U32, +b2: U32, +b3: U32, +a0: U32, +a1: U32, +t0: U32, +t1: U32) -> Maybe<&2, Fm>:  fmt.ok(    le.u16(k0, k1), le.u16(c0, c1), le.u32(r0, r1, r2, r3), le.u32(b0, b1, b2, b3),    le.u16(a0, a1), le.u16(t0, t1))def fmt.fields(ys: List<&2, U32>) -> Maybe<&2, Fm>:  match ys:    case +k0 <> +k1 <> +c0 <> +c1 <> +r0 <> +r1 <> +r2 <> +r3 <> +b0 <> +b1 <> +b2 <> +b3      <> +a0 <> +a1 <> +t0 <> +t1 <> _rest:      fmt.pack(k0, k1, c0, c1, r0, r1, r2, r3, b0, b1, b2, b3, a0, a1, t0, t1)    case _:      None{}def pcm.cons(got: Maybe<&2, List<&2, U32>>, +vv: U32) -> Maybe<&2, List<&2, U32>>:  match got:    case None{}:      None{}    case Some{rest}:      Some{vv <> rest}def pcm.s16(xs: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match xs:    case Nil{}:      Some{[]}    case +lo <> +hi <> tl:      pcm.cons(pcm.s16(tl), le.u16(lo, hi))    case _:      None{}def pcm.f32(xs: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match xs:    case Nil{}:      Some{[]}    case +a0 <> +a1 <> +a2 <> +a3 <> tl:      pcm.cons(pcm.f32(tl), le.u32(a0, a1, a2, a3))    case _:      None{}def pcm.of(+kind: U32, ys: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match kind:    case 1:      pcm.s16(ys)    case _:      pcm.f32(ys)def done.ok(ok: Bool, +rt: U32, +ch: U32, +kd: U32, fr: List<&2, U32>) -> Maybe<&2, Wav>:  match ok:    case True{}:      Some{Wav{rt, ch, kd, fr}}    case False{}:      None{}def st.done(st: St) -> Maybe<&2, Wav>:  match st:    case St{ph, lf, _tg, _sz, sn, kd, ch, rt, _ac, fr}:      done.ok(U32.is_eq(ph, 0) && U32.is_eq(lf, 4) && U32.is_eq(sn, 2), rt, ch, kd, fr)def step.next.pad(  odd: Bool, +sz: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,  frames: List<&2, U32>) -> St:  match odd:    case True{}:      st.set(3, 1, tag, sz, seen, kind, ch, rate, [], frames)    case False{}:      st.set(0, 4, tag, sz, seen, kind, ch, rate, [], frames)def step.next(  +sz: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32, frames: List<&2, U32>) -> St:  step.next.pad(U32.is_eq((sz .&. 1 : U32), 1), sz, tag, seen, kind, ch, rate, frames)def step.fmt.got(got: Maybe<&2, Fm>, +sz: U32, frames: List<&2, U32>) -> St:  match got:    case None{}:      step.next(sz, tag.fmt(), 3, 0, 0, 0, frames)    case Some{Fm{kd, cc, rt}}:      step.next(sz, tag.fmt(), 1, kd, cc, rt, frames)def step.fmt(  +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32, ys: List<&2, U32>,  frames: List<&2, U32>) -> St:  match seen:    case 0:      step.fmt.got(fmt.fields(ys), sz, frames)    case _:      step.next(sz, tag.fmt(), seen, kind, ch, rate, frames)def step.pcm.got(  got: Maybe<&2, List<&2, U32>>, +sz: U32, +kind: U32, +ch: U32, +rate: U32) -> St:  match got:    case None{}:      step.next(sz, tag.data(), 3, kind, ch, rate, [])    case Some{fr}:      step.next(sz, tag.data(), 2, kind, ch, rate, fr)def step.pcm(  ready: Bool, +sz: U32, +kind: U32, +ch: U32, +rate: U32, ys: List<&2, U32>,  frames: List<&2, U32>) -> St:  match ready:    case False{}:      step.next(sz, tag.data(), 3, kind, ch, rate, frames)    case True{}:      step.pcm.got(pcm.of(kind, ys), sz, kind, ch, rate)def step.dat(  isd: Bool, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,  ys: List<&2, U32>, frames: List<&2, U32>) -> St:  match isd:    case False{}:      step.next(sz, tag, seen, kind, ch, rate, frames)    case True{}:      step.pcm(U32.is_eq(seen, 1), sz, kind, ch, rate, ys, frames)def step.chunk.on(  isf: Bool, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,  ys: List<&2, U32>, frames: List<&2, U32>) -> St:  match isf:    case True{}:      step.fmt(sz, seen, kind, ch, rate, ys, frames)    case False{}:      step.dat(U32.is_eq(tag, tag.data()), tag, sz, seen, kind, ch, rate, ys, frames)def step.chunk(  +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,  ys: List<&2, U32>, frames: List<&2, U32>) -> St:  step.chunk.on(U32.is_eq(tag, tag.fmt()), tag, sz, seen, kind, ch, rate, ys, frames)def step.tag.at(  last: Bool, +left: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,  acc: List<&2, U32>, frames: List<&2, U32>) -> St:  match last:    case False{}:      st.set(0, (left - 1 : U32), 0, sz, seen, kind, ch, rate, acc, frames)    case True{}:      st.set(1, 4, word.of(acc, 1), sz, seen, kind, ch, rate, [], frames)def step.tag(st: St, +hd: U32) -> St:  match st:    case St{_ph, +left, _tg, sz, seen, kind, ch, rate, acc, frames}:      step.tag.at(U32.is_eq(left, 1), left, sz, seen, kind, ch, rate, hd <> acc, frames)def step.size.done(  +sz: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32, frames: List<&2, U32>) -> St:  match sz:    case 0:      st.set(0, 4, tag, sz, seen, kind, ch, rate, [], frames)    case _:      st.set(2, sz, tag, sz, seen, kind, ch, rate, [], frames)def step.size.at(  last: Bool, +left: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,  acc: List<&2, U32>, frames: List<&2, U32>) -> St:  match last:    case False{}:      st.set(1, (left - 1 : U32), tag, 0, seen, kind, ch, rate, acc, frames)    case True{}:      step.size.done(word.of(acc, 0), tag, seen, kind, ch, rate, frames)def step.size(st: St, +hd: U32) -> St:  match st:    case St{_ph, +left, tag, _sz, seen, kind, ch, rate, acc, frames}:      step.size.at(U32.is_eq(left, 1), left, tag, seen, kind, ch, rate, hd <> acc, frames)def step.pad(st: St) -> St:  match st:    case St{_phase, _left, tag, sz, seen, kind, ch, rate, _acc, frames}:      st.set(0, 4, tag, sz, seen, kind, ch, rate, [], frames)def step.pay.at(  last: Bool, +left: U32, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32,  +rate: U32, acc: List<&2, U32>, frames: List<&2, U32>) -> St:  match last:    case False{}:      st.set(2, (left - 1 : U32), tag, sz, seen, kind, ch, rate, acc, frames)    case True{}:      step.chunk(tag, sz, seen, kind, ch, rate, List.reverse(&2, U32, acc), frames)def step.pay(st: St, +hd: U32) -> St:  match st:    case St{_ph, +left, tag, sz, seen, kind, ch, rate, acc, frames}:      step.pay.at(U32.is_eq(left, 1), left, tag, sz, seen, kind, ch, rate, hd <> acc, frames)def step.err.dec(  last: Bool, +phase: U32, +left: U32, +tag: U32, +sz: U32, +kind: U32, +ch: U32,  +rate: U32, acc: List<&2, U32>, frames: List<&2, U32>) -> St:  match last:    case False{}:      st.set(phase, (left - 1 : U32), tag, sz, 3, kind, ch, rate, acc, frames)    case True{}:      st.set(0, 4, tag, sz, 3, kind, ch, rate, [], frames)def step.err.keep(+_phase: U32, st: St, +hd: U32) -> St:  match st:    case St{phase, +left, tag, sz, _sn, kind, ch, rate, acc, frames}:      step.err.dec(U32.is_eq(left, 1), phase, left, tag, sz, kind, ch, rate, hd <> acc, frames)def step.on(+phase: U32, st: St, +hd: U32) -> St:  match phase:    case 0:      step.tag(st, hd)    case 1:      step.size(st, hd)    case 2:      step.pay(st, hd)    case _:      step.pad(st)def step.bad(bad: Bool, +phase: U32, st: St, +hd: U32) -> St:  match bad:    case True{}:      step.err.keep(phase, st, hd)    case False{}:      step.on(phase, st, hd)def step.byte(st: St, +hd: U32) -> St:  match st:    case St{+phase, +left, +tag, +sz, +seen, +kind, +ch, +rate, +acc, +frames}:      step.bad(        U32.is_eq(seen, 3), phase,        st.set(phase, left, tag, sz, seen, kind, ch, rate, acc, frames), hd)def walk.go(xs: List<&2, U32>, st: St) -> Maybe<&2, Wav>:  match xs:    case Nil{}:      st.done(st)    case +hd <> tl:      walk.go(tl, step.byte(st, hd))def open.ok(ok: Bool, rest: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match ok:    case True{}:      Some{rest}    case False{}:      None{}def open.riff(xs: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match xs:    case +a <> +b <> +c <> +d <> +s0 <> +s1 <> +s2 <> +s3 <> +w0 <> +w1 <> +w2 <> +w3 <> rest:      open.ok(        U32.is_eq(be.u32(a, b, c, d), tag.riff()) && U32.is_eq(be.u32(w0, w1, w2, w3), tag.wave()),        rest)    case _:      None{}def wav.go(got: Maybe<&2, List<&2, U32>>) -> Maybe<&2, Wav>:  match got:    case None{}:      None{}    case Some{rest}:      walk.go(rest, st.empty())# bytes to a wave, or none when the form is not mono or stereo PCM or floatdef wav.parse(xs: List<&2, U32>) -> Maybe<&2, Wav>:  wav.go(open.riff(xs))def enc.s16(xs: List<&2, U32>) -> List<&2, U32>:  match xs:    case Nil{}:      []    case +hd <> tl:      put.lo(hd) <> put.hi(hd) <> enc.s16(tl)def enc.f32(xs: List<&2, U32>) -> List<&2, U32>:  match xs:    case Nil{}:      []    case +hd <> tl:      put.lo(hd) <> put.hi(hd) <> put.b2(hd) <> put.b3(hd) <> enc.f32(tl)def enc.pcm(+kind: U32, xs: List<&2, U32>) -> List<&2, U32>:  match kind:    case 1:      enc.s16(xs)    case _:      enc.f32(xs)def enc.ch(+nc: U32) -> Bool:  match nc:    case 1:      True{}    case 2:      True{}    case _:      False{}def enc.both(okc: Bool, okr: Bool) -> Bool:  match okc:    case False{}:      False{}    case True{}:      okrdef enc.ok(+nc: U32, +fk: U32, +rt: U32) -> Bool:  match fk:    case 1:      enc.both(enc.ch(nc), U32.is_ne(rt, 0))    case 3:      enc.both(enc.ch(nc), U32.is_ne(rt, 0))    case _:      False{}def enc.bytes(+kind: U32, +ns: U32) -> U32:  match kind:    case 1:      (ns * 2 : U32)    case _:      (ns * 4 : U32)def enc.cat(xs: List<&2, U32>, ys: List<&2, U32>) -> List<&2, U32>:  List.append(&2, U32, xs, ys)def align.of(+kind: U32, +ch: U32) -> U32:  match kind:    case 1:      (ch * 2 : U32)    case _:      (ch * 4 : U32)def width.of(+kind: U32) -> U32:  match kind:    case 1:      16    case _:      32def enc.head(+rate: U32, +ch: U32, +kind: U32, +nb: U32) -> List<&2, U32>:  enc.cat(put.be(tag.riff()),    enc.cat(put.u32((36 + nb : U32)),      enc.cat(put.be(tag.wave()),        enc.cat(put.be(tag.fmt()),          enc.cat(put.u32(16),            enc.cat(put.u16(kind),              enc.cat(put.u16(ch),                enc.cat(put.u32(rate),                  enc.cat(put.u32((rate * align.of(kind, ch) : U32)),                    enc.cat(put.u16(align.of(kind, ch)),                      enc.cat(put.u16(width.of(kind)),                        enc.cat(put.be(tag.data()), put.u32(nb)))))))))))))def write.some(  +rate: U32, +ch: U32, +kind: U32, frames: List<&2, U32>, pcm: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  nb = enc.bytes(kind, U32.from_nat(List.length(&2, U32, frames)))  Some{enc.cat(enc.head(rate, ch, kind, nb), pcm)}def write.if(  ok: Bool, +rate: U32, +ch: U32, +kind: U32, +frames: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match ok:    case False{}:      None{}    case True{}:      write.some(rate, ch, kind, frames, enc.pcm(kind, frames))# a wave file, or none when the clip is not 16-bit PCM or 32-bit float, mono or stereodef wav.write(+rate: U32, +ch: U32, +kind: U32, frames: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  write.if(enc.ok(ch, kind, rate), rate, ch, kind, frames)