Skip to content

Routing and mixer nodes: where the C nodes and the CPython twins disagree, and the lifecycle events no test covers #178

Description

@bdbarnett

A state-by-event read of audioroute (Splitter, SplitterTap, MidSide, Port), audiomixer (Mixer, MixerVoice) and RawSample at v0.6.3rc3. Items marked measured were run on the CPython twin, on desktop MicroPython and on desktop CircuitPython. The rest come from reading the code.

The twin and the native nodes disagree

  • Mixer level gate (measured). The native mixer forces a voice's gate open at the end of every inner chunk, and a chunk is capped by the source's buffer (Mixer.c:587-591). The twin forces it once per block (audiomixer.py:215-240). With a fresh voice, DC 4000 and 100-frame source buffers into a 256-frame block, the twin plays nothing in block 0 and both native builds go non-zero at frame 100. The same applies to any level or pan move. Every mixer test feeds sources at least as big as the block, so none sees it.
  • Mono SplitterTap (measured). The native mono tap hands out an inline buffer it refills on every pull (SplitterTap.c:294-299); the twin hands out a fresh copy (_audiodsp.c:935-955). A voice primed from a mono tap, then the tap pulled directly: native mixes the overtaken block, the twin mixes the snapshot. A class run has already hit this.
  • Mixer.deinit(). Native keeps every voice's source referenced (Mixer.c:99-109); the twin releases them (audiomixer.py:159-162). The test that asserts the release runs on CPython only.
  • Splitter with a source block that is not whole frames. Native drops the block silently after taking it (Splitter.c:75-77); the twin raises, keeps the bad bytes, and raises on every later pull.
  • MidSide.stop() and playing exist on the twin only, and a test exercises them.

Audio lost

  • MidSide.reset_buffer and MidSide.play() drop the source frames the node holds (MidSide.c:178-188, :99-103): 256 frames with 512-frame buffers. Tested only through one class.
  • Mono buffers with an odd number of frames lose their last frame on every buffer in the mixer (Mixer.c:386).
  • Mixer.reset_buffer's priming fetch consumes a block of the source.

No test at all

Housekeeping

  • MixerVoice's constructor sets three fields and relies on zero-fill for the rest (MixerVoice.c:146-150), while Port.c:103-104 says the allocator does not zero. One of the two is wrong.
  • The CircuitPython coverage binary writes gcov profile files into the shared build tree on every run.

See #176 and #177 for the same read of the other nodes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions