adafruit-circuitpython-ogg 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
adafruit_ogg/_float.py ADDED
@@ -0,0 +1,312 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ # SPDX-License-Identifier: MIT
3
+ """Plain Python (float) synthesis for adafruit_ogg, used only when no turbo kernel loads.
4
+
5
+ Correct but several times slower than real time on a microcontroller. With ``ulab`` the
6
+ transform, windowing and PCM conversion are vectorized.
7
+ """
8
+
9
+ import math
10
+ from array import array
11
+
12
+ try:
13
+ from ulab import numpy as _np
14
+
15
+ _ULAB = True
16
+ except ImportError:
17
+ try:
18
+ import numpy as _np # desktop testing with CPython
19
+ except ImportError:
20
+ _np = None
21
+ _ULAB = False
22
+
23
+ _PI = math.pi
24
+
25
+
26
+ def _ilog(x):
27
+ n = 0
28
+ while x > 0:
29
+ n += 1
30
+ x >>= 1
31
+ return n
32
+
33
+
34
+ class _Transform:
35
+ """IMDCT of one block size via an N/4 point complex FFT, plus windows."""
36
+
37
+ def __init__(self, n, bs0, accel):
38
+ self.n = n
39
+ m = n >> 1
40
+ h = n >> 2
41
+ self.m = m
42
+ self.h = h
43
+ # pre-twiddle exp(-i*pi*(4j+1)/(4m)), post-twiddle exp(-i*pi*j/m)
44
+ c1 = [math.cos(-_PI * (4 * j + 1) / (4 * m)) for j in range(h)]
45
+ s1 = [math.sin(-_PI * (4 * j + 1) / (4 * m)) for j in range(h)]
46
+ c2 = [math.cos(-_PI * j / m) for j in range(h)]
47
+ s2 = [math.sin(-_PI * j / m) for j in range(h)]
48
+ self.slope = _slope(n >> 1)
49
+ self.short_slope = _slope(bs0 >> 1)
50
+ self.bs0 = bs0
51
+ self.windows = {}
52
+ if accel:
53
+ self.c1 = _np.array(c1)
54
+ self.s1 = _np.array(s1)
55
+ self.c2 = _np.array(c2)
56
+ self.s2 = _np.array(s2)
57
+ else:
58
+ self.c1 = c1
59
+ self.s1 = s1
60
+ self.c2 = c2
61
+ self.s2 = s2
62
+ bits = _ilog(h) - 1
63
+ rev = []
64
+ for i in range(h):
65
+ r = 0
66
+ v = i
67
+ for _ in range(bits):
68
+ r = (r << 1) | (v & 1)
69
+ v >>= 1
70
+ rev.append(r)
71
+ self.rev = rev
72
+ self.fc = [math.cos(2 * _PI * k / h) for k in range(h >> 1)]
73
+ self.fs = [-math.sin(2 * _PI * k / h) for k in range(h >> 1)]
74
+ self.accel = accel
75
+
76
+ def window(self, long_block, prev, nxt):
77
+ """Section 4.3.1 window for this block size and neighbor flags."""
78
+ key = (prev, nxt) if long_block else (1, 1)
79
+ w = self.windows.get(key)
80
+ if w is not None:
81
+ return w
82
+ n = self.n
83
+ bs0 = self.bs0
84
+ w = [0.0] * n
85
+ if long_block and not prev:
86
+ ls = n // 4 - bs0 // 4
87
+ lslope = self.short_slope
88
+ else:
89
+ ls = 0
90
+ lslope = self.slope
91
+ if long_block and not nxt:
92
+ rs = n * 3 // 4 - bs0 // 4
93
+ rslope = self.short_slope
94
+ else:
95
+ rs = n // 2
96
+ rslope = self.slope
97
+ le = ls + len(lslope)
98
+ for i in range(len(lslope)):
99
+ w[ls + i] = lslope[i]
100
+ for i in range(le, rs):
101
+ w[i] = 1.0
102
+ rn = len(rslope)
103
+ for i in range(rn):
104
+ w[rs + i] = rslope[rn - 1 - i]
105
+ if self.accel:
106
+ w = _np.array(w)
107
+ self.windows[key] = w
108
+ return w
109
+
110
+ def imdct(self, x, win):
111
+ """x: n/2 coefficients. Returns n windowed samples."""
112
+ if self.accel:
113
+ return self._imdct_np(x, win)
114
+ m = self.m
115
+ h = self.h
116
+ c1 = self.c1
117
+ s1 = self.s1
118
+ re = [0.0] * h
119
+ im = [0.0] * h
120
+ rev = self.rev
121
+ for j in range(h):
122
+ a = x[2 * j]
123
+ b = x[m - 1 - 2 * j]
124
+ r = rev[j]
125
+ re[r] = a * c1[j] - b * s1[j]
126
+ im[r] = a * s1[j] + b * c1[j]
127
+ fc = self.fc
128
+ fs = self.fs
129
+ size = 2
130
+ while size <= h:
131
+ half = size >> 1
132
+ step = h // size
133
+ for start in range(0, h, size):
134
+ k = 0
135
+ for j in range(start, start + half):
136
+ l = j + half
137
+ wr = fc[k]
138
+ wi = fs[k]
139
+ rl = re[l]
140
+ il = im[l]
141
+ tr = wr * rl - wi * il
142
+ ti = wr * il + wi * rl
143
+ rj = re[j]
144
+ ij = im[j]
145
+ re[l] = rj - tr
146
+ im[l] = ij - ti
147
+ re[j] = rj + tr
148
+ im[j] = ij + ti
149
+ k += step
150
+ size <<= 1
151
+ c2 = self.c2
152
+ s2 = self.s2
153
+ u = [0.0] * m
154
+ for j in range(h):
155
+ a = re[j]
156
+ b = im[j]
157
+ u[2 * j] = a * c2[j] - b * s2[j]
158
+ u[m - 1 - 2 * j] = -(a * s2[j] + b * c2[j])
159
+ # unfold the DCT-IV into the n point IMDCT output and window it
160
+ n = self.n
161
+ y = [0.0] * n
162
+ q = m >> 1
163
+ for i in range(q):
164
+ y[i] = u[i + q] * win[i]
165
+ for i in range(q, q + m):
166
+ y[i] = -u[m + q - 1 - i] * win[i]
167
+ for i in range(q + m, n):
168
+ y[i] = -u[i - q - m] * win[i]
169
+ return y
170
+
171
+ def _imdct_np(self, x, win):
172
+ np = _np
173
+ m = self.m
174
+ a = x[0::2]
175
+ b = x[m - 1 :: -2]
176
+ re = a * self.c1 - b * self.s1
177
+ im = a * self.s1 + b * self.c1
178
+ if _ULAB:
179
+ fr, fi = np.fft.fft(re, im)
180
+ else:
181
+ z = np.fft.fft(re + 1j * im)
182
+ fr = z.real
183
+ fi = z.imag
184
+ u = np.zeros(m)
185
+ u[0::2] = fr * self.c2 - fi * self.s2
186
+ u[m - 1 :: -2] = -(fr * self.s2 + fi * self.c2)
187
+ q = m >> 1
188
+ y = np.concatenate((u[q:], -u[::-1], -u[:q]))
189
+ return y * win
190
+
191
+
192
+ def _slope(n):
193
+ # rising half of the Vorbis power-sine window, section 4.3.1
194
+ hp = _PI / 2
195
+ return [math.sin(hp * math.pow(math.sin((i + 0.5) / n * hp), 2)) for i in range(n)]
196
+
197
+
198
+ def synth_py(self, spec, floors, mp, xf, win, n):
199
+ ch = self._channels
200
+ n2 = n >> 1
201
+ # inverse coupling (4.3.5)
202
+ for m, a in reversed(mp.coupling):
203
+ mv = spec[m]
204
+ av = spec[a]
205
+ for i in range(n2):
206
+ mm = mv[i]
207
+ aa = av[i]
208
+ if mm > 0:
209
+ if aa > 0:
210
+ av[i] = mm - aa
211
+ else:
212
+ av[i] = mm
213
+ mv[i] = mm + aa
214
+ elif aa > 0:
215
+ av[i] = mm + aa
216
+ else:
217
+ av[i] = mm
218
+ mv[i] = mm - aa
219
+ prevs = self._prev
220
+ pn = self._prev_n
221
+ if pn:
222
+ off = pn // 4 - n // 4
223
+ length = pn // 4 + n // 4
224
+ pcm = array("h", bytearray(2 * length * ch))
225
+ else:
226
+ pcm = None
227
+ length = 0
228
+ newprev = []
229
+ for c in range(ch):
230
+ f = floors[c]
231
+ if f is None:
232
+ y = None
233
+ else:
234
+ x = spec[c]
235
+ for i in range(n2):
236
+ x[i] *= f[i]
237
+ y = xf.imdct(x, win)
238
+ if pcm is not None:
239
+ pr = prevs[c]
240
+ k = c
241
+ for q in range(length):
242
+ v = pr[q] if (pr is not None and q < len(pr)) else 0.0
243
+ j = q - off
244
+ if y is not None and 0 <= j < n:
245
+ v += y[j]
246
+ s = int(v * 32768.0 + (0.5 if v > 0 else -0.5))
247
+ if s > 32767:
248
+ s = 32767
249
+ elif s < -32768:
250
+ s = -32768
251
+ pcm[k] = s
252
+ k += ch
253
+ newprev.append(y[n2:] if y is not None else None)
254
+ self._prev = newprev
255
+ self._prev_n = n
256
+ if pcm is None:
257
+ return None
258
+ return pcm, length
259
+
260
+
261
+ def synth_np(self, spec, floors, mp, xf, win, n):
262
+ np = _np
263
+ ch = self._channels
264
+ n2 = n >> 1
265
+ arrs = [np.array(s) if s is not None else None for s in spec]
266
+ for m, a in reversed(mp.coupling):
267
+ mm = arrs[m]
268
+ aa = arrs[a]
269
+ # branch-free form of the 4.3.5 table
270
+ pa = aa > 0
271
+ sm = (mm > 0) * 2.0 - 1.0
272
+ t = aa * sm
273
+ arrs[m] = mm + t - pa * t
274
+ arrs[a] = mm - pa * t
275
+ prevs = self._prev
276
+ pn = self._prev_n
277
+ if pn:
278
+ off = pn // 4 - n // 4
279
+ length = pn // 4 + n // 4
280
+ pcm = np.zeros(length * ch, dtype=np.int16)
281
+ else:
282
+ pcm = None
283
+ length = 0
284
+ newprev = []
285
+ for c in range(ch):
286
+ f = floors[c]
287
+ y = None
288
+ if f is not None:
289
+ y = xf.imdct(arrs[c] * np.array(f), win)
290
+ if pcm is not None:
291
+ acc = np.zeros(length)
292
+ pr = prevs[c]
293
+ if pr is not None:
294
+ k = len(pr) if len(pr) < length else length
295
+ acc[:k] = pr[:k]
296
+ if y is not None:
297
+ # out[q] += y[q - off] where 0 <= q - off < n
298
+ q0 = off if off > 0 else 0
299
+ j0 = q0 - off
300
+ k = length - q0
301
+ acc[q0:] = acc[q0:] + y[j0 : j0 + k] # noqa: PLR6104, ulab slices have no +=
302
+ acc = np.clip(np.around(acc * 32768.0), -32768.0, 32767.0)
303
+ if ch == 1:
304
+ pcm = np.array(acc, dtype=np.int16)
305
+ else:
306
+ pcm[c::ch] = np.array(acc, dtype=np.int16)
307
+ newprev.append(y[n2:] if y is not None else None)
308
+ self._prev = newprev
309
+ self._prev_n = n
310
+ if pcm is None:
311
+ return None
312
+ return pcm, length
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
Binary file
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Mikey Sklar for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT