Files
suaveplan/packages/media/audio/audio-analysis-pitch/src/algorithms/downmix.md
2026-05-27 23:47:53 +01:00

2.8 KiB
Raw Blame History

downmix

Purpose

downmix.ts provides the per-block mono-folding utilities shared by all three detectors in @suaveplan/audio-analysis-pitch. Detectors only operate on a single mono channel, so each block emitted by AudioDocument.stream() is folded to a single Float32Array before analysis. Centralising this in one module avoids three duplicated implementations across the detector files and gives one well-tested code path for every block in the package.

Features

  • downmixMono averages an arbitrary number of channels into one Float32Array.
  • Handles zero-channel, sparse-channel, and single-channel inputs without branching at every call site.
  • blockLength is a tiny helper that returns the first channel's sample count or zero — the canonical way of reading the block size from a doc.stream() yield.

Usage: Basic

import { blockLength, downmixMono } from "@suaveplan/audio-analysis-pitch/algorithms/downmix";

for await (const block of doc.stream()) {
    const length = blockLength(block);
    const mono = downmixMono(block, length);
    // ... analyse mono
}

Usage: Advanced

import { blockLength, downmixMono } from "@suaveplan/audio-analysis-pitch/algorithms/downmix";

function streamAnalyse(block: Float32Array[], analyse: (m: Float32Array) => number) {
    const length = blockLength(block);
    if (length === 0) return 0;
    return analyse(downmixMono(block, length));
}

API Reference

  • downmixMono(channels: readonly Float32Array[], length: number): Float32Array — averages across channels.
  • blockLength(block: readonly Float32Array[]): number — first-channel length or zero.

Implementation Notes

The function tolerates sparse channel arrays (TypeScript's readonly Float32Array[] could in theory hold undefined); it checks each channel and skips undefined slots. The output length is caller-determined so callers can pre-allocate for known block sizes. The function never allocates per-sample and runs in O(channels × length).

The averaging is unweighted across channels — for a stereo block of equal amplitude tones in both ears the output is the same single-channel waveform. For richer downmixing (ITU-R BS.775 surround coefficients, equal-power panning, etc.) callers should fold the input themselves before passing a one-channel array to the detector entry points. Pitch and key detection are mostly invariant to the choice of downmix, so the simple mean is the right default and any pluggable mixer would be premature complexity in this module.

blockLength returns a number rather than the channel reference because every consumer in this package wants the length, never the array. Reading the length once and passing it through downmixMono lets the compiler hoist the bounds check and avoids re-reading block[0]?.length inside the hot loop.