wav.bend

emerging-ezaudio@0.3.0.0 · 480 lines · 0 definitions

1# RIFF WAVE. PCM is little-endian signed 16-bit. Float is IEEE binary32.
2import Base
3
4# a parsed wave. kind 1 is signed 16-bit, kind 3 is binary32. frames are interleaved.
5type Wav is Data:
6  Wav{rate: U32, channels: U32, kind: U32, frames: List<&2, U32>}
7
8# fmt fields that passed the PCM checks
9type Fm is Data:
10  Fm{kind: U32, ch: U32, rate: U32}
11
12# the chunk walker: phase 0 tag, 1 size, 2 payload, 3 pad byte
13type St is Data:
14  St{
15    phase: U32, left: U32, tag: U32, sz: U32, seen: U32, kind: U32, ch: U32,
16    rate: U32, acc: List<&2, U32>, frames: List<&2, U32>
17  }
18
19# 'RIFF'
20def tag.riff() -> U32:
21  1380533830
22
23# 'WAVE'
24def tag.wave() -> U32:
25  1463899717
26
27# 'fmt '
28def tag.fmt() -> U32:
29  1718449184
30
31# 'data'
32def tag.data() -> U32:
33  1684108385
34
35def be.u32(+aa: U32, +bb: U32, +cc: U32, +dd: U32) -> U32:
36  ((((aa << 24n : U32) .|. (bb << 16n : U32) : U32) .|. (cc << 8n : U32) : U32) .|. dd : U32)
37
38def le.u16(+aa: U32, +bb: U32) -> U32:
39  (aa .|. (bb << 8n : U32) : U32)
40
41def le.u32(+aa: U32, +bb: U32, +cc: U32, +dd: U32) -> U32:
42  (le.u16(aa, bb) .|. (le.u16(cc, dd) << 16n : U32) : U32)
43
44def put.u16(+vv: U32) -> List<&2, U32>:
45  [(vv .&. 255 : U32), ((vv >> 8n : U32) .&. 255 : U32)]
46
47def put.u32(+vv: U32) -> List<&2, U32>:
48  List.append(&2, U32, put.u16(vv), put.u16((vv >> 16n : U32)))
49
50def put.be(+vv: U32) -> List<&2, U32>:
51  [((vv >> 24n : U32) .&. 255 : U32), ((vv >> 16n : U32) .&. 255 : U32),
52   ((vv >> 8n : U32) .&. 255 : U32), (vv .&. 255 : U32)]
53
54def put.lo(+vv: U32) -> U32:
55  (vv .&. 255 : U32)
56
57def put.hi(+vv: U32) -> U32:
58  ((vv >> 8n : U32) .&. 255 : U32)
59
60def put.b2(+vv: U32) -> U32:
61  ((vv >> 16n : U32) .&. 255 : U32)
62
63def put.b3(+vv: U32) -> U32:
64  ((vv >> 24n : U32) .&. 255 : U32)
65
66def word4.pick(big: Bool, +aa: U32, +bb: U32, +cc: U32, +dd: U32) -> U32:
67  match big:
68    case True{}:
69      be.u32(aa, bb, cc, dd)
70    case False{}:
71      le.u32(aa, bb, cc, dd)
72
73def word.of(xs: List<&2, U32>, +big: U32) -> U32:
74  match xs:
75    case +dd <> +cc <> +bb <> +aa <> _tl:
76      word4.pick(U32.is_eq(big, 1), aa, bb, cc, dd)
77    case _:
78      0
79
80def st.empty() -> St:
81  St{0, 4, 0, 0, 0, 0, 0, 0, [], []}
82
83def st.set(
84  +phase: U32, +left: U32, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32,
85  +rate: U32, acc: List<&2, U32>, frames: List<&2, U32>
86) -> St:
87  St{phase, left, tag, sz, seen, kind, ch, rate, acc, frames}
88
89def fmt.rate(ok: Bool, +kind: U32, +ch: U32, +rate: U32) -> Maybe<&2, Fm>:
90  match ok:
91    case True{}:
92      Some{Fm{kind, ch, rate}}
93    case False{}:
94      None{}
95
96def fmt.ok(+kind: U32, +ch: U32, +rate: U32, +br: U32, +al: U32, +bits: U32) -> Maybe<&2, Fm>:
97  match kind ch bits:
98    case 1 1 16:
99      fmt.rate(U32.is_eq(al, 2) && U32.is_eq(br, (rate * 2 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)
100    case 1 2 16:
101      fmt.rate(U32.is_eq(al, 4) && U32.is_eq(br, (rate * 4 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)
102    case 3 1 32:
103      fmt.rate(U32.is_eq(al, 4) && U32.is_eq(br, (rate * 4 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)
104    case 3 2 32:
105      fmt.rate(U32.is_eq(al, 8) && U32.is_eq(br, (rate * 8 : U32)) && U32.is_ne(rate, 0), kind, ch, rate)
106    case _ _ _:
107      None{}
108
109def fmt.pack(
110  +k0: U32, +k1: U32, +c0: U32, +c1: U32, +r0: U32, +r1: U32, +r2: U32, +r3: U32,
111  +b0: U32, +b1: U32, +b2: U32, +b3: U32, +a0: U32, +a1: U32, +t0: U32, +t1: U32
112) -> Maybe<&2, Fm>:
113  fmt.ok(
114    le.u16(k0, k1), le.u16(c0, c1), le.u32(r0, r1, r2, r3), le.u32(b0, b1, b2, b3),
115    le.u16(a0, a1), le.u16(t0, t1))
116
117def fmt.fields(ys: List<&2, U32>) -> Maybe<&2, Fm>:
118  match ys:
119    case +k0 <> +k1 <> +c0 <> +c1 <> +r0 <> +r1 <> +r2 <> +r3 <> +b0 <> +b1 <> +b2 <> +b3
120      <> +a0 <> +a1 <> +t0 <> +t1 <> _rest:
121      fmt.pack(k0, k1, c0, c1, r0, r1, r2, r3, b0, b1, b2, b3, a0, a1, t0, t1)
122    case _:
123      None{}
124
125def pcm.cons(got: Maybe<&2, List<&2, U32>>, +vv: U32) -> Maybe<&2, List<&2, U32>>:
126  match got:
127    case None{}:
128      None{}
129    case Some{rest}:
130      Some{vv <> rest}
131
132def pcm.s16(xs: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
133  match xs:
134    case Nil{}:
135      Some{[]}
136    case +lo <> +hi <> tl:
137      pcm.cons(pcm.s16(tl), le.u16(lo, hi))
138    case _:
139      None{}
140
141def pcm.f32(xs: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
142  match xs:
143    case Nil{}:
144      Some{[]}
145    case +a0 <> +a1 <> +a2 <> +a3 <> tl:
146      pcm.cons(pcm.f32(tl), le.u32(a0, a1, a2, a3))
147    case _:
148      None{}
149
150def pcm.of(+kind: U32, ys: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
151  match kind:
152    case 1:
153      pcm.s16(ys)
154    case _:
155      pcm.f32(ys)
156
157def done.ok(ok: Bool, +rt: U32, +ch: U32, +kd: U32, fr: List<&2, U32>) -> Maybe<&2, Wav>:
158  match ok:
159    case True{}:
160      Some{Wav{rt, ch, kd, fr}}
161    case False{}:
162      None{}
163
164def st.done(st: St) -> Maybe<&2, Wav>:
165  match st:
166    case St{ph, lf, _tg, _sz, sn, kd, ch, rt, _ac, fr}:
167      done.ok(U32.is_eq(ph, 0) && U32.is_eq(lf, 4) && U32.is_eq(sn, 2), rt, ch, kd, fr)
168
169def step.next.pad(
170  odd: Bool, +sz: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,
171  frames: List<&2, U32>
172) -> St:
173  match odd:
174    case True{}:
175      st.set(3, 1, tag, sz, seen, kind, ch, rate, [], frames)
176    case False{}:
177      st.set(0, 4, tag, sz, seen, kind, ch, rate, [], frames)
178
179def step.next(
180  +sz: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32, frames: List<&2, U32>
181) -> St:
182  step.next.pad(U32.is_eq((sz .&. 1 : U32), 1), sz, tag, seen, kind, ch, rate, frames)
183
184def step.fmt.got(got: Maybe<&2, Fm>, +sz: U32, frames: List<&2, U32>) -> St:
185  match got:
186    case None{}:
187      step.next(sz, tag.fmt(), 3, 0, 0, 0, frames)
188    case Some{Fm{kd, cc, rt}}:
189      step.next(sz, tag.fmt(), 1, kd, cc, rt, frames)
190
191def step.fmt(
192  +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32, ys: List<&2, U32>,
193  frames: List<&2, U32>
194) -> St:
195  match seen:
196    case 0:
197      step.fmt.got(fmt.fields(ys), sz, frames)
198    case _:
199      step.next(sz, tag.fmt(), seen, kind, ch, rate, frames)
200
201def step.pcm.got(
202  got: Maybe<&2, List<&2, U32>>, +sz: U32, +kind: U32, +ch: U32, +rate: U32
203) -> St:
204  match got:
205    case None{}:
206      step.next(sz, tag.data(), 3, kind, ch, rate, [])
207    case Some{fr}:
208      step.next(sz, tag.data(), 2, kind, ch, rate, fr)
209
210def step.pcm(
211  ready: Bool, +sz: U32, +kind: U32, +ch: U32, +rate: U32, ys: List<&2, U32>,
212  frames: List<&2, U32>
213) -> St:
214  match ready:
215    case False{}:
216      step.next(sz, tag.data(), 3, kind, ch, rate, frames)
217    case True{}:
218      step.pcm.got(pcm.of(kind, ys), sz, kind, ch, rate)
219
220def step.dat(
221  isd: Bool, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,
222  ys: List<&2, U32>, frames: List<&2, U32>
223) -> St:
224  match isd:
225    case False{}:
226      step.next(sz, tag, seen, kind, ch, rate, frames)
227    case True{}:
228      step.pcm(U32.is_eq(seen, 1), sz, kind, ch, rate, ys, frames)
229
230def step.chunk.on(
231  isf: Bool, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,
232  ys: List<&2, U32>, frames: List<&2, U32>
233) -> St:
234  match isf:
235    case True{}:
236      step.fmt(sz, seen, kind, ch, rate, ys, frames)
237    case False{}:
238      step.dat(U32.is_eq(tag, tag.data()), tag, sz, seen, kind, ch, rate, ys, frames)
239
240def step.chunk(
241  +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,
242  ys: List<&2, U32>, frames: List<&2, U32>
243) -> St:
244  step.chunk.on(U32.is_eq(tag, tag.fmt()), tag, sz, seen, kind, ch, rate, ys, frames)
245
246def step.tag.at(
247  last: Bool, +left: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,
248  acc: List<&2, U32>, frames: List<&2, U32>
249) -> St:
250  match last:
251    case False{}:
252      st.set(0, (left - 1 : U32), 0, sz, seen, kind, ch, rate, acc, frames)
253    case True{}:
254      st.set(1, 4, word.of(acc, 1), sz, seen, kind, ch, rate, [], frames)
255
256def step.tag(st: St, +hd: U32) -> St:
257  match st:
258    case St{_ph, +left, _tg, sz, seen, kind, ch, rate, acc, frames}:
259      step.tag.at(U32.is_eq(left, 1), left, sz, seen, kind, ch, rate, hd <> acc, frames)
260
261def step.size.done(
262  +sz: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32, frames: List<&2, U32>
263) -> St:
264  match sz:
265    case 0:
266      st.set(0, 4, tag, sz, seen, kind, ch, rate, [], frames)
267    case _:
268      st.set(2, sz, tag, sz, seen, kind, ch, rate, [], frames)
269
270def step.size.at(
271  last: Bool, +left: U32, +tag: U32, +seen: U32, +kind: U32, +ch: U32, +rate: U32,
272  acc: List<&2, U32>, frames: List<&2, U32>
273) -> St:
274  match last:
275    case False{}:
276      st.set(1, (left - 1 : U32), tag, 0, seen, kind, ch, rate, acc, frames)
277    case True{}:
278      step.size.done(word.of(acc, 0), tag, seen, kind, ch, rate, frames)
279
280def step.size(st: St, +hd: U32) -> St:
281  match st:
282    case St{_ph, +left, tag, _sz, seen, kind, ch, rate, acc, frames}:
283      step.size.at(U32.is_eq(left, 1), left, tag, seen, kind, ch, rate, hd <> acc, frames)
284
285def step.pad(st: St) -> St:
286  match st:
287    case St{_phase, _left, tag, sz, seen, kind, ch, rate, _acc, frames}:
288      st.set(0, 4, tag, sz, seen, kind, ch, rate, [], frames)
289
290def step.pay.at(
291  last: Bool, +left: U32, +tag: U32, +sz: U32, +seen: U32, +kind: U32, +ch: U32,
292  +rate: U32, acc: List<&2, U32>, frames: List<&2, U32>
293) -> St:
294  match last:
295    case False{}:
296      st.set(2, (left - 1 : U32), tag, sz, seen, kind, ch, rate, acc, frames)
297    case True{}:
298      step.chunk(tag, sz, seen, kind, ch, rate, List.reverse(&2, U32, acc), frames)
299
300def step.pay(st: St, +hd: U32) -> St:
301  match st:
302    case St{_ph, +left, tag, sz, seen, kind, ch, rate, acc, frames}:
303      step.pay.at(U32.is_eq(left, 1), left, tag, sz, seen, kind, ch, rate, hd <> acc, frames)
304
305def step.err.dec(
306  last: Bool, +phase: U32, +left: U32, +tag: U32, +sz: U32, +kind: U32, +ch: U32,
307  +rate: U32, acc: List<&2, U32>, frames: List<&2, U32>
308) -> St:
309  match last:
310    case False{}:
311      st.set(phase, (left - 1 : U32), tag, sz, 3, kind, ch, rate, acc, frames)
312    case True{}:
313      st.set(0, 4, tag, sz, 3, kind, ch, rate, [], frames)
314
315def step.err.keep(+_phase: U32, st: St, +hd: U32) -> St:
316  match st:
317    case St{phase, +left, tag, sz, _sn, kind, ch, rate, acc, frames}:
318      step.err.dec(U32.is_eq(left, 1), phase, left, tag, sz, kind, ch, rate, hd <> acc, frames)
319
320def step.on(+phase: U32, st: St, +hd: U32) -> St:
321  match phase:
322    case 0:
323      step.tag(st, hd)
324    case 1:
325      step.size(st, hd)
326    case 2:
327      step.pay(st, hd)
328    case _:
329      step.pad(st)
330
331def step.bad(bad: Bool, +phase: U32, st: St, +hd: U32) -> St:
332  match bad:
333    case True{}:
334      step.err.keep(phase, st, hd)
335    case False{}:
336      step.on(phase, st, hd)
337
338def step.byte(st: St, +hd: U32) -> St:
339  match st:
340    case St{+phase, +left, +tag, +sz, +seen, +kind, +ch, +rate, +acc, +frames}:
341      step.bad(
342        U32.is_eq(seen, 3), phase,
343        st.set(phase, left, tag, sz, seen, kind, ch, rate, acc, frames), hd)
344
345def walk.go(xs: List<&2, U32>, st: St) -> Maybe<&2, Wav>:
346  match xs:
347    case Nil{}:
348      st.done(st)
349    case +hd <> tl:
350      walk.go(tl, step.byte(st, hd))
351
352def open.ok(ok: Bool, rest: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
353  match ok:
354    case True{}:
355      Some{rest}
356    case False{}:
357      None{}
358
359def open.riff(xs: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
360  match xs:
361    case +a <> +b <> +c <> +d <> +s0 <> +s1 <> +s2 <> +s3 <> +w0 <> +w1 <> +w2 <> +w3 <> rest:
362      open.ok(
363        U32.is_eq(be.u32(a, b, c, d), tag.riff()) && U32.is_eq(be.u32(w0, w1, w2, w3), tag.wave()),
364        rest)
365    case _:
366      None{}
367
368def wav.go(got: Maybe<&2, List<&2, U32>>) -> Maybe<&2, Wav>:
369  match got:
370    case None{}:
371      None{}
372    case Some{rest}:
373      walk.go(rest, st.empty())
374
375# bytes to a wave, or none when the form is not mono or stereo PCM or float
376def wav.parse(xs: List<&2, U32>) -> Maybe<&2, Wav>:
377  wav.go(open.riff(xs))
378
379def enc.s16(xs: List<&2, U32>) -> List<&2, U32>:
380  match xs:
381    case Nil{}:
382      []
383    case +hd <> tl:
384      put.lo(hd) <> put.hi(hd) <> enc.s16(tl)
385
386def enc.f32(xs: List<&2, U32>) -> List<&2, U32>:
387  match xs:
388    case Nil{}:
389      []
390    case +hd <> tl:
391      put.lo(hd) <> put.hi(hd) <> put.b2(hd) <> put.b3(hd) <> enc.f32(tl)
392
393def enc.pcm(+kind: U32, xs: List<&2, U32>) -> List<&2, U32>:
394  match kind:
395    case 1:
396      enc.s16(xs)
397    case _:
398      enc.f32(xs)
399
400def enc.ch(+nc: U32) -> Bool:
401  match nc:
402    case 1:
403      True{}
404    case 2:
405      True{}
406    case _:
407      False{}
408
409def enc.both(okc: Bool, okr: Bool) -> Bool:
410  match okc:
411    case False{}:
412      False{}
413    case True{}:
414      okr
415
416def enc.ok(+nc: U32, +fk: U32, +rt: U32) -> Bool:
417  match fk:
418    case 1:
419      enc.both(enc.ch(nc), U32.is_ne(rt, 0))
420    case 3:
421      enc.both(enc.ch(nc), U32.is_ne(rt, 0))
422    case _:
423      False{}
424
425def enc.bytes(+kind: U32, +ns: U32) -> U32:
426  match kind:
427    case 1:
428      (ns * 2 : U32)
429    case _:
430      (ns * 4 : U32)
431
432def enc.cat(xs: List<&2, U32>, ys: List<&2, U32>) -> List<&2, U32>:
433  List.append(&2, U32, xs, ys)
434
435def align.of(+kind: U32, +ch: U32) -> U32:
436  match kind:
437    case 1:
438      (ch * 2 : U32)
439    case _:
440      (ch * 4 : U32)
441
442def width.of(+kind: U32) -> U32:
443  match kind:
444    case 1:
445      16
446    case _:
447      32
448
449def enc.head(+rate: U32, +ch: U32, +kind: U32, +nb: U32) -> List<&2, U32>:
450  enc.cat(put.be(tag.riff()),
451    enc.cat(put.u32((36 + nb : U32)),
452      enc.cat(put.be(tag.wave()),
453        enc.cat(put.be(tag.fmt()),
454          enc.cat(put.u32(16),
455            enc.cat(put.u16(kind),
456              enc.cat(put.u16(ch),
457                enc.cat(put.u32(rate),
458                  enc.cat(put.u32((rate * align.of(kind, ch) : U32)),
459                    enc.cat(put.u16(align.of(kind, ch)),
460                      enc.cat(put.u16(width.of(kind)),
461                        enc.cat(put.be(tag.data()), put.u32(nb)))))))))))))
462
463def write.some(
464  +rate: U32, +ch: U32, +kind: U32, frames: List<&2, U32>, pcm: List<&2, U32>
465) -> Maybe<&2, List<&2, U32>>:
466  nb = enc.bytes(kind, U32.from_nat(List.length(&2, U32, frames)))
467  Some{enc.cat(enc.head(rate, ch, kind, nb), pcm)}
468
469def write.if(
470  ok: Bool, +rate: U32, +ch: U32, +kind: U32, +frames: List<&2, U32>
471) -> Maybe<&2, List<&2, U32>>:
472  match ok:
473    case False{}:
474      None{}
475    case True{}:
476      write.some(rate, ch, kind, frames, enc.pcm(kind, frames))
477
478# a wave file, or none when the clip is not 16-bit PCM or 32-bit float, mono or stereo
479def wav.write(+rate: U32, +ch: U32, +kind: U32, frames: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
480  write.if(enc.ok(ch, kind, rate), rate, ch, kind, frames)