Found by reading the code at v0.6.3rc3. Not reproduced: nothing was run.
After deinit(), the native audioverb.Tank still accepts set(), clear() and play() (Tank.c:283, :305, :187), and the native audioconvolve.Convolver still accepts clear(), load() and synthesize() (Convolver.c:227, :159, :196). None checks that the object is still live. The CPython twins guard all of them (audioverb.py:145, :163, :171, :183, :191; audioconvolve.py:115, :123, :152, :160), so the difference does not show in any test that runs on CPython.
For the Convolver it may be worse than a missing error. deinit() sets storage to NULL (Convolver.c:346), and the comment at Convolver.c:342-345 calls storage the sole GC root for the kernel's buffers. A later clear(), load() or synthesize() writes through the state's pointers into memory the collector may have taken back. That would be a use-after-free on a board.
What to establish first
- On desktop MicroPython and CircuitPython:
deinit(), force a collection, allocate, then call each method. Does it raise, corrupt, or crash?
- Whether
audioecho.FeedbackDelay and the other nodes have the same gap. tests/parity/deinit_surface_probe.py checks channel_count, get_buffer and reset_buffer only (:170-172).
Wanted
Every method of every node raises after deinit(), the same way on all three bindings, and the deinit probe covers every method, not three.
A related gap, lower priority
The Tank's bandwidth, low-cut and damping one-poles skip their update when their corner is 0 (audiodsp_tank.c:528, :533, :564), so their state freezes and comes back stale when the control returns. It is the shape of #168. No class reaches a corner of 0 today, so nothing audible depends on it. The LFO does the same at a rate of 0 (:499-500).
Found by reading the code at v0.6.3rc3. Not reproduced: nothing was run.
After
deinit(), the nativeaudioverb.Tankstill acceptsset(),clear()andplay()(Tank.c:283,:305,:187), and the nativeaudioconvolve.Convolverstill acceptsclear(),load()andsynthesize()(Convolver.c:227,:159,:196). None checks that the object is still live. The CPython twins guard all of them (audioverb.py:145,:163,:171,:183,:191;audioconvolve.py:115,:123,:152,:160), so the difference does not show in any test that runs on CPython.For the Convolver it may be worse than a missing error.
deinit()setsstorageto NULL (Convolver.c:346), and the comment atConvolver.c:342-345callsstoragethe sole GC root for the kernel's buffers. A laterclear(),load()orsynthesize()writes through the state's pointers into memory the collector may have taken back. That would be a use-after-free on a board.What to establish first
deinit(), force a collection, allocate, then call each method. Does it raise, corrupt, or crash?audioecho.FeedbackDelayand the other nodes have the same gap.tests/parity/deinit_surface_probe.pycheckschannel_count,get_bufferandreset_bufferonly (:170-172).Wanted
Every method of every node raises after
deinit(), the same way on all three bindings, and the deinit probe covers every method, not three.A related gap, lower priority
The Tank's bandwidth, low-cut and damping one-poles skip their update when their corner is 0 (
audiodsp_tank.c:528,:533,:564), so their state freezes and comes back stale when the control returns. It is the shape of #168. No class reaches a corner of 0 today, so nothing audible depends on it. The LFO does the same at a rate of 0 (:499-500).