main.bend

emerging-ezaudio@0.2.9.0 · 236 lines · 0 definitions

1# ezaudio: a Clip is a sample rate, a channel count, a sample format, and
2# interleaved PCM frames. WAV reads and writes RIFF PCM. MP3 reads and writes
3# MPEG-1 Layer III.
4import Base
5import emerging-ezaudio@0.2.9.0/wav.bend as Wav
6import emerging-ezaudio@0.2.9.0/mp3.bend as Mp3
7
8# signed 16-bit PCM, or IEEE 754 binary32
9type Format is Data:
10  S16{}
11  Flt{}
12
13# one clip. pcm is interleaved. s16 lives in the low 16 bits.
14type Clip is Data:
15  Clip{hz: U32, nch: U32, fmt: Format, pcm: List<&2, U32>}
16
17# signed 16-bit little-endian PCM
18def s16() -> Format:
19  S16{}
20
21# IEEE 754 binary32, stored as its bits
22def f32() -> Format:
23  Flt{}
24
25def kind.of(fmt: Format) -> U32:
26  match fmt:
27    case S16{}:
28      1
29    case Flt{}:
30      3
31
32def fmt.of(+kind: U32) -> Format:
33  match kind:
34    case 1:
35      S16{}
36    case _:
37      Flt{}
38
39def wav.clip(w: Wav.Wav) -> Clip:
40  match w:
41    case Wav.Wav{+rate, +channels, +kind, frames}:
42      Clip{rate, channels, fmt.of(kind), frames}
43
44def fmt.same(aa: Format, bb: Format) -> Bool:
45  match aa bb:
46    case S16{} S16{}:
47      True{}
48    case Flt{} Flt{}:
49      True{}
50    case S16{} Flt{}:
51      False{}
52    case Flt{} S16{}:
53      False{}
54
55# a clip from its rate, channel count, format, and interleaved frames
56def clip(+rate: U32, +channels: U32, format: Format, frames: List<&2, U32>) -> Clip:
57  Clip{rate, channels, format, frames}
58
59# the sample rate, in hertz
60def rate(c: Clip) -> U32:
61  match c:
62    case Clip{+hz, _nch, _fmt, _pcm}:
63      hz
64
65# how many channels are interleaved
66def channels(c: Clip) -> U32:
67  match c:
68    case Clip{_hz, +nch, _fmt, _pcm}:
69      nch
70
71# s16 or f32
72def format(c: Clip) -> Format:
73  match c:
74    case Clip{_hz, _nch, +fmt, _pcm}:
75      fmt
76
77# the interleaved frames
78def frames(c: Clip) -> List<&2, U32>:
79  match c:
80    case Clip{_hz, _nch, _fmt, +pcm}:
81      pcm
82
83def count.of(+channels: U32, +nsamples: U32) -> U32:
84  match channels:
85    case 0:
86      0
87    case _:
88      U32.div(nsamples, channels)
89
90# how many frames. zero channels count as none.
91def count(c: Clip) -> U32:
92  match c:
93    case Clip{_hz, +nch, _fmt, +pcm}:
94      count.of(nch, U32.from_nat(List.length(&2, U32, pcm)))
95
96# one sample, repeated across nframes frames
97def fill(+rate: U32, +channels: U32, format: Format, +sample: U32, +nframes: U32) -> Clip:
98  Clip{rate, channels, format, List.replicate(U32, U32.to_nat((nframes * channels : U32)), sample)}
99
100def ls.drop(hop: Nat, xs: List<&2, U32>) -> List<&2, U32>:
101  match hop:
102    case 0n:
103      xs
104    case 1n+p:
105      match xs:
106        case Nil{}:
107          []
108        case _hd <> tl:
109          ls.drop(p, tl)
110
111def ls.take(hop: Nat, xs: List<&2, U32>, acc: List<&2, U32>) -> List<&2, U32>:
112  match hop:
113    case 0n:
114      List.reverse(&2, U32, acc)
115    case 1n+p:
116      match xs:
117        case Nil{}:
118          List.reverse(&2, U32, acc)
119        case +hd <> tl:
120          ls.take(p, tl, hd <> acc)
121
122def ls.slice(xs: List<&2, U32>, +dropn: U32, +taken: U32) -> List<&2, U32>:
123  ls.take(U32.to_nat(taken), ls.drop(U32.to_nat(dropn), xs), [])
124
125def trim.go(
126  ok: Bool,
127  +rate: U32,
128  +channels: U32,
129  format: Format,
130  frames: List<&2, U32>,
131  +start: U32,
132  +nframes: U32
133) -> Maybe<&2, Clip>:
134  match ok:
135    case False{}:
136      None{}
137    case True{}:
138      Some{Clip{rate, channels, format,
139        ls.slice(frames, (start * channels : U32), (nframes * channels : U32))}}
140
141# frames [start, start + nframes). none when that span passes the end.
142def trim(c: Clip, +start: U32, +nframes: U32) -> Maybe<&2, Clip>:
143  match c:
144    case Clip{+hz, +nch, fmt, +pcm}:
145      trim.go(U32.is_le((start + nframes : U32), count.of(nch, U32.from_nat(List.length(&2, U32, pcm)))), hz, nch,
146        fmt, pcm, start, nframes)
147
148def eq.all(ar: Bool, ac: Bool, af: Bool) -> Bool:
149  match ar:
150    case False{}:
151      False{}
152    case True{}:
153      match ac:
154        case False{}:
155          False{}
156        case True{}:
157          af
158
159def concat.go(
160  ok: Bool,
161  +rate: U32,
162  +channels: U32,
163  format: Format,
164  left: List<&2, U32>,
165  right: List<&2, U32>
166) -> Maybe<&2, Clip>:
167  match ok:
168    case False{}:
169      None{}
170    case True{}:
171      Some{Clip{rate, channels, format, List.append(&2, U32, left, right)}}
172
173def concat.pair(+rate: U32, +channels: U32, +format: Format, left: List<&2, U32>, other: Clip) -> Maybe<&2, Clip>:
174  match other:
175    case Clip{+rb, +cb, fb, right}:
176      concat.go(eq.all(U32.is_eq(rate, rb), U32.is_eq(channels, cb), fmt.same(format, fb)), rate, channels, format,
177        left, right)
178
179# the frames of both clips, or none when rate, channels, or format differ
180def concat(left: Clip, right: Clip) -> Maybe<&2, Clip>:
181  match left:
182    case Clip{+hz, +nch, +fmt, pcm}:
183      concat.pair(hz, nch, fmt, pcm, right)
184
185def list.empty(xs: List<&2, U32>) -> Bool:
186  match xs:
187    case Nil{}:
188      True{}
189    case _hd <> _tl:
190      False{}
191
192def wav.bytes(empty: Bool, +rate: U32, +channels: U32, +kind: U32, frames: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
193  match empty:
194    case True{}:
195      None{}
196    case False{}:
197      Wav.wav.write(rate, channels, kind, frames)
198
199def wav.parsed(got: Maybe<&2, Wav.Wav>) -> Maybe<&2, Clip>:
200  match got:
201    case None{}:
202      None{}
203    case Some{w}:
204      Some{wav.clip(w)}
205
206# a RIFF WAVE clip, or none when the bytes are not mono or stereo PCM or float
207def decode_wav(xs: List<&2, U32>) -> Maybe<&2, Clip>:
208  wav.parsed(Wav.wav.parse(xs))
209
210# a RIFF WAVE file. none when there are no frames, or the rate or channels are unfit.
211def encode_wav(c: Clip) -> Maybe<&2, List<&2, U32>>:
212  match c:
213    case Clip{+hz, +nch, fmt, +pcm}:
214      wav.bytes(list.empty(pcm), hz, nch, kind.of(fmt), pcm)
215
216def mp3.clip(p: Mp3.Pcm) -> Clip:
217  match p:
218    case Mp3.Pcm{+hz, +ch, pcm}:
219      Clip{hz, ch, Flt{}, pcm}
220
221def mp3.parsed(got: Maybe<&2, Mp3.Pcm>) -> Maybe<&2, Clip>:
222  match got:
223    case None{}:
224      None{}
225    case Some{p}:
226      Some{mp3.clip(p)}
227
228# MPEG-1 Layer III to f32 frames. Layer I, Layer II, joint stereo, and a CRC are none.
229def decode_mp3(xs: List<&2, U32>) -> Maybe<&2, Clip>:
230  mp3.parsed(Mp3.mp3.decode(xs))
231
232# MPEG-1 Layer III at 320 kbit/s. rates are 32000, 44100, and 48000. none when unfit or empty.
233def encode_mp3(c: Clip) -> Maybe<&2, List<&2, U32>>:
234  match c:
235    case Clip{+hz, +nch, fmt, pcm}:
236      Mp3.mp3.encode(hz, nch, kind.of(fmt), pcm)