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. 4 import Base 5 import ./wav.bend as Wav 6 import ./mp3.bend as Mp3 7 8 # signed 16-bit PCM, or IEEE 754 binary32 9 type Format is Data: 10 S16{} 11 Flt{} 12 13 # one clip. pcm is interleaved. s16 lives in the low 16 bits. 14 type Clip is Data: 15 Clip{hz: U32, nch: U32, fmt: Format, pcm: List<&2, U32>} 16 17 # signed 16-bit little-endian PCM 18 def s16() -> Format: 19 S16{} 20 21 # IEEE 754 binary32, stored as its bits 22 def f32() -> Format: 23 Flt{} 24 25 def kind.of(fmt: Format) -> U32: 26 match fmt: 27 case S16{}: 28 1 29 case Flt{}: 30 3 31 32 def fmt.of(+kind: U32) -> Format: 33 match kind: 34 case 1: 35 S16{} 36 case _: 37 Flt{} 38 39 def 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 44 def 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 56 def 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 60 def rate(c: Clip) -> U32: 61 match c: 62 case Clip{+hz, _nch, _fmt, _pcm}: 63 hz 64 65 # how many channels are interleaved 66 def channels(c: Clip) -> U32: 67 match c: 68 case Clip{_hz, +nch, _fmt, _pcm}: 69 nch 70 71 # s16 or f32 72 def format(c: Clip) -> Format: 73 match c: 74 case Clip{_hz, _nch, +fmt, _pcm}: 75 fmt 76 77 # the interleaved frames 78 def frames(c: Clip) -> List<&2, U32>: 79 match c: 80 case Clip{_hz, _nch, _fmt, +pcm}: 81 pcm 82 83 def 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. 91 def 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 97 def 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 100 def 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 111 def 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 122 def 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 125 def 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. 142 def 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 148 def 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 159 def 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 173 def 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 180 def 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 185 def list.empty(xs: List<&2, U32>) -> Bool: 186 match xs: 187 case Nil{}: 188 True{} 189 case _hd <> _tl: 190 False{} 191 192 def 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 199 def 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 207 def 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. 211 def 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 216 def mp3.clip(p: Mp3.Pcm) -> Clip: 217 match p: 218 case Mp3.Pcm{+hz, +ch, pcm}: 219 Clip{hz, ch, Flt{}, pcm} 220 221 def 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. 229 def 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. 233 def 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)