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)