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_circuitpython_ogg-1.0.1.dist-info/METADATA +189 -0
- adafruit_circuitpython_ogg-1.0.1.dist-info/RECORD +21 -0
- adafruit_circuitpython_ogg-1.0.1.dist-info/WHEEL +5 -0
- adafruit_circuitpython_ogg-1.0.1.dist-info/licenses/LICENSE +21 -0
- adafruit_circuitpython_ogg-1.0.1.dist-info/top_level.txt +1 -0
- adafruit_ogg/__init__.py +1845 -0
- adafruit_ogg/_float.py +312 -0
- adafruit_ogg/ogg_kernel.armv6m.mpy +0 -0
- adafruit_ogg/ogg_kernel.armv6m.mpy.license +3 -0
- adafruit_ogg/ogg_kernel.armv7emsp.mpy +0 -0
- adafruit_ogg/ogg_kernel.armv7emsp.mpy.license +3 -0
- adafruit_ogg/ogg_kernel.rv32imc.mpy +0 -0
- adafruit_ogg/ogg_kernel.rv32imc.mpy.license +3 -0
- adafruit_ogg/ogg_kernel.xtensawin.mpy +0 -0
- adafruit_ogg/ogg_kernel.xtensawin.mpy.license +3 -0
- adafruit_ogg/ogg_native.armv6m.mpy +0 -0
- adafruit_ogg/ogg_native.armv6m.mpy.license +3 -0
- adafruit_ogg/ogg_native.armv7emsp.mpy +0 -0
- adafruit_ogg/ogg_native.armv7emsp.mpy.license +3 -0
- adafruit_ogg/ogg_native.xtensawin.mpy +0 -0
- adafruit_ogg/ogg_native.xtensawin.mpy.license +3 -0
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|