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