Skip to content

AudioStreamConverter - #1551

Merged
Mysticial merged 15 commits into
mainfrom
wip-audio-convert
Oct 11, 2026
Merged

Mysticial merged 15 commits into
mainfrom
wip-audio-convert

Conversation

@Mysticial

@Mysticial Mysticial commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Adds 3 new audio processing modules:

  • AudioStreamConverter: Takes a raw audio stream and converts to/from the float format. (this is a newer version of AudioStreamConversion with more SIMD)
  • AudioChannelInterleave: Takes a stream of audio frames (interleaved channels) and splits them into separate single-channel streams. Also does the reverse direction.
  • AudioChannelMixer: Takes an N-channel audio stream and converts it to an M-channel stream using a custom weight matrix.

Thus our future audio pipeline will be:

  1. Receive an arbitrary audio stream from the capture card.
  2. If it's not already float we convert it to float using AudioStreamConverter.
  3. If it's multiple channels, we deinterleave it into separate streams using AudioChannelInterleave.

For the audio playback path:
4. If the sample rate does not match the audio sink's sample rate, resample it using AudioResampler.
5. Re-interleave the channels using AudioChannelInterleave.
6. Convert to the audio sink's desired format using AudioStreamConverter.

For the FFT/inference path:
4. If the sample rate is not 48 kHz, we resample it to 48 kHz using AudioResampler.
5. We collapse the streams into 1 channel using AudioChannelMixer.
6. Perform FFTs and inference.

Step 4 on both branches may be optimized to one step if the conversion is identical.

@Mysticial Mysticial changed the title AudioStreamConversion: Default+AVX512 AudioStreamConverter Oct 10, 2026
@Mysticial
Mysticial marked this pull request as ready for review October 11, 2026 06:42
@Mysticial
Mysticial merged commit 37ad058 into main Oct 11, 2026
20 checks passed
@Mysticial
Mysticial deleted the wip-audio-convert branch October 11, 2026 06:42
Mysticial pushed a commit that referenced this pull request Oct 13, 2026
…er (+ in_stride fix from #1568) (#1569)

* AudioChannelMixer: honor in_stride in the 2-input Default fast paths; fix test comparing an implementation to itself.

mix_channels_Default<2, 1> and <2, 2> loaded in[0]/in[1] instead of
in[in_stride * 0]/in[in_stride * 1], so any strided input (the layout the
AudioPipeline ChannelMixer always produces) read the wrong samples. The
SSE/FMA3/AVX512 implementations all index with in_stride, and the generic
Default fallback did too; only the two fast paths were wrong.

The existing kernel tests never caught this because
Kernels_AudioChannelMixer_TestInfra's Test constructor initialized
m_convert0 with convert1, comparing an implementation against itself.

Verified: the fixed Default implementation now matches
mix_channels_GenericDefault bit-exactly for all 1-4 channel in/out
combinations across packed and strided inputs (in_stride 1/5/512,
sample counts 2/3/17/930).

(cherry picked from commit 1efc832)

* AudioPipeline: add unit tests for Raw<->Working round-trip and ChannelMixer.

Mirrors the TestInfra pattern and sample-size progression from the
AudioChannelMixer/AudioStreamConverter/AudioChannelInterleave kernels
(#1551): for (size_t samples = 2; samples < 1000; samples = (size_t)(samples * 1.1) + 1).

- Raw<->Working round-trip (Kernels_AudioPipeline_RoundTrip_TestInfra):
  the two converters are tested as a round-trip through both, since the
  test belongs to neither class. Random raw samples are pushed through
  RawAudio2WorkingAudio -> WorkingAudio2RawAudio (1.0f multiplier) into
  a recording RawAudioStreamListener and compared against the input,
  bit-exactly. Loops all sample formats (u8/s16/s32/f32), all channel
  counts 1-4, and the #1551 sample sizes. s32 input is masked to
  0xfffff000 and all values are integer-valued so the round-trip is
  lossless (same convention as the AudioStreamConverter tests).

- ChannelMixer (Kernels_ChannelMixer_TestInfra):
  - MixerIdentity: identity weight matrix for 1-4 channels; random
    integer samples must come out unchanged.
  - Mixer_1to2_2to1: a 1->2 mixer with the averaging matrix of a single
    channel (identity, weights 1.0) feeding a 2->1 averaging mixer
    (weights 0.5, exactly representable). Random integer inputs keep
    every intermediate value exactly representable, so the 1->2 output
    is identical to the input on both channels and the 2->1 output is
    identical to the original input.

Registered via AudioPipeline::add_tests() from Kernels_Tests.cpp;
~1092 test instances in total.

* AudioPipeline tests: 256-sample converter buffer; four mixer identity tests; one 1->2->1 round-trip.

- The round-trip test used the default converter buffer (4096 + 256), which is
  larger than every sample size in the sweep, so the chunk loop never ran more
  than once. Override it to 256 so the sweep exercises multiple chunks.
- The ChannelMixer tests are one identity test per channel count (1-4, 1000
  samples each) and one 1->2->1 averaging round-trip (1000 samples), instead of
  sweeping the sample sizes.
- Kernels_AudioPipeline.h: include <cstddef> for size_t, like the sibling
  kernel headers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant