Repository navigation
Expand file tree
/
Copy pathmicropython.mk
More file actions
356 lines (334 loc) · 17.7 KB
/
Copy pathmicropython.mk
File metadata and controls
356 lines (334 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# MicroPython Make-based build glue for audiodsp (unix, windows).
# For CMake-based ports (esp32, rp2, …), see micropython.cmake in this dir.
#
# Discovered via USER_C_MODULES pointing at the workspace directory that
# contains this repo (its parent) — see the parent workspace's own build
# script.
# A CircuitPython tree builds this repository through circuitpython.mk instead:
# CircuitPython already has audiocore, synthio, audiomixer and the effects
# modules, so it compiles only the modules CircuitPython lacks. Its py/ has
# circuitpy_mpconfig.h and MicroPython's does not; that is the test, because
# the unix port never sets a CIRCUITPY make variable.
ifneq ($(wildcard $(TOP)/py/circuitpy_mpconfig.h),)
include $(USERMOD_DIR)/circuitpython.mk
else
MPAUDIO_MOD_DIR := $(USERMOD_DIR)
MPAUDIO_SRC_DIR := $(MPAUDIO_MOD_DIR)/src
CFLAGS_USERMOD += -I$(MPAUDIO_SRC_DIR)
# --- which audiodsp this firmware was built from -----------------------------
#
# audiodsp#55. `os.uname().version` answers for MicroPython and says nothing
# about us, so a board digest that moves cannot be attributed to a revision.
# Computed HERE, from the directory the build is actually reading, because that
# is the only place that knows: on 2026-09-09 a firmware had been built from a
# throwaway source tree during a pin move, and the commit was not recoverable
# from the working tree or from the .bin files left on disk.
#
# `--tags --match 'v[0-9]*'` so it names the release: the release tags are
# lightweight, and a plain `git describe` skips those for the last annotated
# tag (v0.0.3). `--always --dirty` so a tree with uncommitted changes says so.
# Both default to "unknown" rather than guessing if git is absent or this is a
# tarball -- see src/cp_compat/audiodsp_build.c.
MPAUDIO_VERSION := $(shell cat $(MPAUDIO_MOD_DIR)/VERSION 2>/dev/null || echo 0.0.0+unknown)
MPAUDIO_REVISION := $(shell git -C $(MPAUDIO_MOD_DIR) describe --tags --match 'v[0-9]*' --always --dirty --abbrev=7 2>/dev/null || echo unknown)
CFLAGS_USERMOD += -DAUDIODSP_VERSION='"$(MPAUDIO_VERSION)"'
CFLAGS_USERMOD += -DAUDIODSP_REVISION='"$(MPAUDIO_REVISION)"'
# Make recompiles nothing when only a -D value changes, so in a reused build
# directory the strings above would stay those of the first build (#156). The
# one object that carries them depends on a stamp, rewritten only when they
# change. py.mk puts a module's objects under $(BUILD)/<module directory name>/.
MPAUDIO_BUILD_STAMP := $(BUILD)/audiodsp_build.stamp
$(shell mkdir -p $(BUILD) && echo '$(MPAUDIO_VERSION) $(MPAUDIO_REVISION)' > $(MPAUDIO_BUILD_STAMP).new && \
{ cmp -s $(MPAUDIO_BUILD_STAMP).new $(MPAUDIO_BUILD_STAMP) && rm -f $(MPAUDIO_BUILD_STAMP).new || mv -f $(MPAUDIO_BUILD_STAMP).new $(MPAUDIO_BUILD_STAMP); })
$(BUILD)/$(notdir $(MPAUDIO_MOD_DIR))/src/cp_compat/audiodsp_build.o: $(MPAUDIO_BUILD_STAMP)
# --- cp_compat: small shims for CircuitPython-only helpers (mp_arg_validate_*,
# cp_enum, MP_PROPERTY_GETTER/GETSET, default___enter__/__exit__) that the
# ported CircuitPython audio/synthio sources call directly. See
# docs/porting-plan.md tier 0 and src/cp_compat/*.h for provenance.
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/cp_compat/audiodsp_build.c \
$(MPAUDIO_SRC_DIR)/cp_compat/argcheck.c \
$(MPAUDIO_SRC_DIR)/cp_compat/enum.c \
$(MPAUDIO_SRC_DIR)/cp_compat/context_manager_helpers.c \
$(MPAUDIO_SRC_DIR)/cp_compat/proto.c \
$(MPAUDIO_SRC_DIR)/cp_compat/util.c \
$(MPAUDIO_SRC_DIR)/cp_compat/objproperty.c \
$(MPAUDIO_SRC_DIR)/cp_compat/namedtuple.c
# --- runtime-neutral sample protocol/state shared with the CPython wheel
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_sample.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_port.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_pump_lock.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_rawsample.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_synth_dsp.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_envelope.c \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_distortion.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_biquad.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_echo.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_phaser.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_chorus.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_flanger.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_granular_pitch_shift.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_multitap.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_pitchshift.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_freeverb.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_dynamics.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_splitter.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_midside.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_remix.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_multiply.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_suboctave.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_feedback_delay.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_filter_f32.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_shaper.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_samplehold.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_ladder.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_trig.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_fft.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_convolve.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_tank.c
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/shared/audiodsp_modal.c
# --- the pump engine: the portable half of the live audio path --------------
#
# The pull loop, service(), the push ring, the event queue, the tap, the output
# ring, the status words, the fault register, retarget/park and the finaliser
# guard that makes the pump die with the VM. No thread, no mutex, no clock and
# no sink in any of it -- those arrive through shared/audiodsp_port.h from a
# driver that may or may not be linked. With none linked this is the
# WebAssembly shape: one thread, audiopump.service() drives the loop.
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiopump/audiopump.c \
$(MPAUDIO_SRC_DIR)/audiopump/audiopump_ring.c \
$(MPAUDIO_SRC_DIR)/audiopump/audiopump_events.c \
$(MPAUDIO_SRC_DIR)/audiopump/audiopump_tap.c
# --- module skeleton: empty `import <name>` targets for every top-level
# module this tree will grow into (see docs/porting-plan.md). Tiers add
# real classes/functions by editing mpaudio_modules.c's globals tables
# directly, so this stays the single source of each module's contents.
SRC_USERMOD_C += $(MPAUDIO_SRC_DIR)/mpaudio_modules.c
# --- tier 1: audiocore (the audiosample protocol, RawSample, WaveFile) ---
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiocore/__init__.c \
$(MPAUDIO_SRC_DIR)/audiocore/RawSample.c \
$(MPAUDIO_SRC_DIR)/audiocore/WaveFile.c \
$(MPAUDIO_SRC_DIR)/audiocore/module.c
# --- tier 2: synthio (block layer, Math, LFO, Biquad, Note, Synthesizer,
# MidiTrack) ---
#
# CIRCUITPY_SYNTHIO_MAX_CHANNELS: src/synthio/__init__.h defaults this to 2
# (CP's own conservative default -- see the comment there), which caps
# concurrent Notes across an entire Synthesizer, not per-key. Any
# realistic polyphonic patch blows past 2 immediately (two notes held on a
# 2-oscillator/detuned voice is already 4 concurrent Notes), and the excess
# is silently REFUSED -- not stolen, not truncated. Corrected 2026-09-02
# (audiodsp#14): this comment used to say "silently stealing/truncating",
# which describes a gentler failure than the one that happens.
# find_channel_with_note (src/synthio/__init__.c:361) scans only channels
# where the note is NOT playing and takes the quietest, so it reclaims from
# RELEASED notes only; with every channel held it returns -1 and
# synthio_span_change_note drops the press at :425. The difference matters:
# a stolen note is one you hear cut short, a refused note never sounds at
# all, and only the second is inaudible as a fault. Upstream
# handles this per-port (ports/raspberrypi/mpconfigport.mk: 24,
# ports/nordic: 12, ...); this workspace's CircuitPython parity oracle
# (the parent workspace's circuitpython checkout, unix `coverage` variant,
# bin/circuitpython) is itself built with 14
# (ports/unix/variants/coverage/mpconfigvariant.mk).
# so this file matched that until 2026-09-03, when the ceiling moved to 64
# (#31). The oracle stays at 14 and is not rebuilt, so material that crosses
# 14 channels is non-parity by construction and declared as such in
# docs/upstream-diff.md. Everything under 14 channels still byte-matches: the
# mix-down limiter only reshapes samples past +/-28000, and all four parity
# gates are byte-identical at 14, 36 and 56 (measured).
#
# CORRECTION 2026-09-03: this comment used to say "unix/windows/wasm (every
# port this Make-based file covers)". That is wrong about its own reach and
# had been since it was written. py/py.mk globs every usermod .mk file in,
# so this line lands on EVERY Make port -- verified: samd, stm32, nrf,
# mimxrt, esp8266, renesas-ra, alif, cc3200, rp2, unix, windows,
# webassembly. It is not the desktop file; it is the default for everything
# not built through CMake, SAMD51 audio boards included. That mattered when
# this said 14 and CMake said 8, because a proposal to raise "desktop" here
# would silently have raised those mcu boards too. It matters less now that
# all three paths read 14 on purpose, but the comment should be true.
CFLAGS_USERMOD += -DCIRCUITPY_SYNTHIO_MAX_CHANNELS=64
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/synthio/__init__.c \
$(MPAUDIO_SRC_DIR)/synthio/Math.c \
$(MPAUDIO_SRC_DIR)/synthio/LFO.c \
$(MPAUDIO_SRC_DIR)/synthio/Biquad.c \
$(MPAUDIO_SRC_DIR)/synthio/Note.c \
$(MPAUDIO_SRC_DIR)/synthio/Synthesizer.c \
$(MPAUDIO_SRC_DIR)/synthio/MidiTrack.c
# --- tier 3: audiomixer (Mixer, MixerVoice) ---
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiomixer/Mixer.c \
$(MPAUDIO_SRC_DIR)/audiomixer/MixerVoice.c \
$(MPAUDIO_SRC_DIR)/audiomixer/module.c
# --- tier 4: effects (audiospeed) ---
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiospeed/Resampler.c \
$(MPAUDIO_SRC_DIR)/audiospeed/SpeedChanger.c \
$(MPAUDIO_SRC_DIR)/audiospeed/module.c
# --- tier 4: effects (audiofreeverb) ---
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiofreeverb/Freeverb.c \
$(MPAUDIO_SRC_DIR)/audiofreeverb/module.c
# --- tier 4: effects (audiofilters) ---
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiofilters/__init__.c \
$(MPAUDIO_SRC_DIR)/audiofilters/Filter.c \
$(MPAUDIO_SRC_DIR)/audiofilters/Distortion.c \
$(MPAUDIO_SRC_DIR)/audiofilters/Phaser.c \
$(MPAUDIO_SRC_DIR)/audiofilters/module.c
# --- tier 4: effects (audiodelays) ---
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiodelays/Chorus.c \
$(MPAUDIO_SRC_DIR)/audiodelays/Echo.c \
$(MPAUDIO_SRC_DIR)/audiodelays/Flanger.c \
$(MPAUDIO_SRC_DIR)/audiodelays/GranularPitchShift.c \
$(MPAUDIO_SRC_DIR)/audiodelays/MultiTapDelay.c \
$(MPAUDIO_SRC_DIR)/audiodelays/PitchShift.c \
$(MPAUDIO_SRC_DIR)/audiodelays/module.c
# --- tier 6: audiodynamics (Dynamics, DYN_*), audioroute (Splitter,
# MidSide), audiomath (Multiply, SubOctave), audioecho
# (FeedbackDelay), audioladder (Ladder), audioconvolve (Convolver),
# audiobiquad (Biquad, AllPass), audioverb (Tank) and
# audiomodal (Bank) ---
#
# The modules here are not CircuitPython ports. The first two come from
# micropython-vst3's `vstaudio` usermod, which grew them for its effects
# library; the rest are audiodsp's own -- audiomath is the only way to modulate
# a stream at audio rate and the only way to divide one down in frequency,
# audioecho puts a filter inside a delay's feedback loop, audioladder puts
# four one-poles and a saturator inside a filter's, audioconvolve applies a
# measured impulse response rather than imitating one, audiobiquad
# filters in float so a tail reaches exact zero, which neither ported
# kernel can (audiodsp#23, #36), and audioverb is a reverberation tank whose
# line lengths and output taps come from Python rather than being compiled
# in the way audiofreeverb's are, and audiomodal sums a bank of
# resonators before the quantiser, which a chain of audiobiquads cannot
# do at any width (audioroute.Splitter stops at four taps) or any depth
# (every node in the chain quantises, and a quiet mode is then noise).
# See docs/upstream-diff.md.
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiodynamics/Dynamics.c \
$(MPAUDIO_SRC_DIR)/audiodynamics/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audioroute/MidSide.c \
$(MPAUDIO_SRC_DIR)/audioroute/Port.c \
$(MPAUDIO_SRC_DIR)/audioroute/Splitter.c \
$(MPAUDIO_SRC_DIR)/audioroute/SplitterTap.c \
$(MPAUDIO_SRC_DIR)/audioroute/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiomath/Multiply.c \
$(MPAUDIO_SRC_DIR)/audiomath/SubOctave.c \
$(MPAUDIO_SRC_DIR)/audiomath/module.c
# audiometer: band levels for a spectrum meter (media modules roadmap, Phase 6)
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/shared/audiodsp_meter.c \
$(MPAUDIO_SRC_DIR)/audiometer/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audioecho/FeedbackDelay.c \
$(MPAUDIO_SRC_DIR)/audioecho/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audioshaper/Waveshaper.c \
$(MPAUDIO_SRC_DIR)/audioshaper/SampleHold.c \
$(MPAUDIO_SRC_DIR)/audioshaper/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audioladder/Ladder.c \
$(MPAUDIO_SRC_DIR)/audioladder/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audioconvolve/Convolver.c \
$(MPAUDIO_SRC_DIR)/audioconvolve/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiobiquad/Biquad.c \
$(MPAUDIO_SRC_DIR)/audiobiquad/AllPass.c \
$(MPAUDIO_SRC_DIR)/audiobiquad/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audioverb/Tank.c \
$(MPAUDIO_SRC_DIR)/audioverb/module.c
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiomodal/Bank.c \
$(MPAUDIO_SRC_DIR)/audiomodal/module.c
# --- tier 5: audiomp3 (MP3Decoder) ---
#
# lib/mp3: a cloned sibling dependency in the parent workspace (the upstream
# adafruit/Adafruit_MP3 repo, pinned to the exact commit the parent
# workspace's circuitpython checkout vendors in its own lib/mp3), not a
# port. Its `src/` is the Helix
# fixed-point MP3 decoder (RealNetworks, 2003), under RPSL 1.0/RCSL 1.0
# per-file headers, NOT MIT; CircuitPython itself carries it unmodified
# under those terms rather than relicensing it, and so does this port --
# see docs/upstream-diff.md, "Tier 5 audiomp3: license" for the full check.
# One local patch was needed on top of the pinned commit
# (mp3/src/assembly.h in that sibling clone): its MSVC-only inline-asm branch
# guards on `defined _WIN32`, which mingw-w64 GCC (this workspace's Windows
# MicroPython target) also defines, so it now also excludes __GNUC__ and
# falls through to the portable C path instead -- CircuitPython itself never
# hits this, since it has no Windows port. See docs/upstream-diff.md for the
# full patch rationale.
# mp3dec.h picks its platform-specific fixed-point/asm path by matching a
# closed list of (__GNUC__ + arch) combinations, with an explicit
# MP3DEC_GENERIC escape hatch for anything else -- `#error No platform
# defined` otherwise. Emscripten/wasm32 matches none of the listed
# combinations (it's not __i386__/__amd64__/ARM), so it needs that escape
# hatch; unix (__amd64__) and windows/mingw-w64 (__i386__ or __amd64__,
# already covered by the same __GNUC__ branches unix uses) both match a
# named branch already and must NOT get this -- it would silently swap
# their working platform-specific path for the portable fallback. Detect
# the wasm port the same way the parent workspace's wasmbridge module does,
# since there's no other reliable way to ask "which port is this" from inside a
# Make port's shared USER_C_MODULES glue.
MPAUDIO_WASM_PORT := $(findstring /ports/webassembly,$(abspath $(CURDIR)))
ifneq ($(MPAUDIO_WASM_PORT),)
CFLAGS_USERMOD += -DMP3DEC_GENERIC
endif
MPAUDIO_MP3_SRC_DIR := $(abspath $(MPAUDIO_MOD_DIR)/.deps/mp3/src)
ifeq ($(wildcard $(MPAUDIO_MP3_SRC_DIR)/mp3dec.c),)
MPAUDIO_MP3_SRC_DIR := $(abspath $(MPAUDIO_MOD_DIR)/../mp3/src)
endif
# Neither there: fetch our own pinned copy (DEPENDENCIES.lock, plus our
# patches) into .deps/, the way a fresh clone outside any workspace builds.
ifeq ($(wildcard $(MPAUDIO_MP3_SRC_DIR)/mp3dec.c),)
ifneq ($(AUDIODSP_OPTIONAL_DEPS),1)
$(info audiodsp: fetching Adafruit_MP3 into .deps/ (scripts/fetch_deps.sh))
MPAUDIO_FETCH := $(shell bash $(MPAUDIO_MOD_DIR)/scripts/fetch_deps.sh 1>&2; echo $$?)
MPAUDIO_MP3_SRC_DIR := $(abspath $(MPAUDIO_MOD_DIR)/.deps/mp3/src)
ifeq ($(wildcard $(MPAUDIO_MP3_SRC_DIR)/mp3dec.c),)
$(error audiodsp: fetching Adafruit_MP3 failed (scripts/fetch_deps.sh exit $(MPAUDIO_FETCH)). Set AUDIODSP_OPTIONAL_DEPS=1 to build without audiomp3)
endif
endif
endif
ifneq ($(wildcard $(MPAUDIO_MP3_SRC_DIR)/mp3dec.c),)
CFLAGS_USERMOD += -I$(MPAUDIO_MP3_SRC_DIR)
SRC_USERMOD_C += $(addprefix $(MPAUDIO_MP3_SRC_DIR)/, \
bitstream.c \
buffers.c \
dct32.c \
dequant.c \
dqchan.c \
huffman.c \
hufftabs.c \
imdct.c \
mp3dec.c \
mp3tabs.c \
polyphase.c \
scalfact.c \
stproc.c \
subband.c \
trigtabs.c \
)
# buffers.c's MPDEC_ALLOCATOR(x)/MPDEC_FREE(x) macros (used nowhere else in
# lib/mp3) route through this port's own mp3_alloc()/mp3_free()
# (src/audiomp3/mp3_alloc.h -> m_malloc_maybe/m_free), matching upstream's
# own production (non-coverage-build) allocator wiring exactly, including
# the -fwrapv upstream applies to this one file only (Helix's fixed-point
# DSP relies on defined signed-overflow wraparound here).
$(BUILD)/mp3/src/buffers.o: CFLAGS += -include "$(MPAUDIO_SRC_DIR)/audiomp3/mp3_alloc.h" -D'MPDEC_ALLOCATOR(x)=mp3_alloc(x)' -D'MPDEC_FREE(x)=mp3_free(x)' -fwrapv
SRC_USERMOD_C += \
$(MPAUDIO_SRC_DIR)/audiomp3/mp3_alloc.c \
$(MPAUDIO_SRC_DIR)/audiomp3/MP3Decoder.c \
$(MPAUDIO_SRC_DIR)/audiomp3/module.c
endif
endif # not CircuitPython