main.bend

emerging-ezaudio@0.4.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.4.0.0/src/wav.bend as Wav
8import emerging-ezaudio@0.4.0.0/src/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)