Skip to content

native ambisonics support#25

Merged
jmvalin merged 1 commit into
oac-nextfrom
jbuethe_oac_multichannel_clean
May 15, 2026
Merged

native ambisonics support#25
jmvalin merged 1 commit into
oac-nextfrom
jbuethe_oac_multichannel_clean

Conversation

@janpbuethe
Copy link
Copy Markdown
Contributor

Native Ambisonics Support
Summary
Adds native multi-channel ambisonics encoding/decoding to OAC, supporting ambisonics orders 0–5 (1, 4, 9, 16, 25, or 36 channels). In ambisonics mode, each channel is encoded independently per band using CELT, bypassing the stereo mid/side coupling path entirely.

Key Changes
New API surface (include/oac.h, include/oac_defines.h)

New format constants: OAC_FORMAT_STANDARD (mono/stereo) and OAC_FORMAT_AMBISONICS (up to 36ch)
oac_encoder_create(), oac_decoder_create(), and their _init/_get_size variants now take a format parameter
New CTL: OAC_GET_FORMAT to query the initialized format
CELT band quantization (celt/bands.c, celt/bands.h)

Renamed the existing oaci_quant_all_bands to quant_all_bands_twoch (static, handles 1–2 channels)
Added quant_all_bands_multi — a multi-mono quantization path where each channel is encoded independently per band with its own spectral folding norm, lowband scratch, and split memory
New public dispatcher oaci_quant_all_bands routes to the appropriate implementation based on channel count
Bit allocation (celt/rate.c)

Replaced hardcoded >>stereo shifts with
C
division to generalize fine-energy bit allocation for arbitrary channel counts
Energy quantization (celt/quant_bands.c)

Expanded prev[] arrays from fixed size 2 to OAC_MAX_CHANNELS for multi-channel coarse energy quantization/unquantization
Encoder/Decoder (src/oac_encoder.c, src/oac_decoder.c, celt/celt_encoder.c, celt/celt_decoder.c)

Encoder and decoder state structs carry a format field
Format/channel validation via oaci_validate_format_channels() in oac_private.h
Ambisonics mode forces CELT-only encoding (no SILK) and OAC_APPLICATION_AUDIO
Multistream (src/oac_multistream_encoder.c, src/oac_multistream_decoder.c)

Updated to pass format through to underlying encoder/decoder instances
Tests

New tests/test_oac_ambisonics.c — encode/decode round-trip tests for all valid ambisonics channel counts
Usage — oac_demo with ambisonics input

Encode a 4-channel (1st order) ambisonics file

./oac_demo -e -format ambix input_4ch.pcm encoded.oac 48000 4 64000

Decode back

./oac_demo -d -format ambix encoded.oac output_4ch.pcm 48000 4

Encode + decode in one pass (default)

./oac_demo -format ambix input_4ch.pcm output_4ch.pcm 48000 4 64000

-format ambix enables ambisonics mode (default is std for standard mono/stereo)

  • Valid channel counts: 1 (0th order), 4 (1st), 9 (2nd), 16 (3rd), 25 (4th), 36 (5th)
  • Input/output are raw interleaved 16-bit PCM
  • Bitrate is the total bitrate across all channels

Note that channel count is an out-of-band parameter for ambix i.e. we do not support order conversion (yet).

@thirv thirv self-requested a review March 26, 2026 19:04
Copy link
Copy Markdown

@thirv thirv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left couple comments/questions.

Comment thread celt/celt_decoder.c
Comment thread celt/bands.c
Comment thread celt/bands.c
@trsonic
Copy link
Copy Markdown

trsonic commented Mar 29, 2026

Could the support be extended to 7OA?

@janpbuethe
Copy link
Copy Markdown
Contributor Author

Could the support be extended to 7OA?

That's relatively easy to do.

@thirv thirv self-requested a review April 6, 2026 16:25
Copy link
Copy Markdown

@thirv thirv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we build the multichannel capacity, there are number of tools that people are interested in having to handle correlations and joint bit allocation. Clarifying the former, it seems that we want to include the mechanism of 1) constructing a downmix and 2) demixing at the decoder. Regardless of the methods of how to actually implement them, either/both of these could be static or dynamic.

I'd advocate to keep these possibilities open, and this PR seems like it would be a valid basis for adding these later. Feel free to discuss if the above does not resonate.

Comment thread celt/celt_encoder.c Outdated
@janpbuethe janpbuethe force-pushed the jbuethe_oac_multichannel_clean branch from c11b255 to c1e749d Compare April 10, 2026 18:14
Comment thread src/oac_decoder.c Outdated
Comment thread src/repacketizer.c Outdated
Comment thread tests/test_oac_api.c Outdated
Comment thread tests/test_oac_api.c Outdated
Comment thread tests/test_oac_api.c Outdated
Comment thread celt/bands.c Outdated
Comment thread celt/bands.c Outdated
Comment thread celt/celt_encoder.c Outdated
Comment thread celt/celt_encoder.c Outdated
Comment thread celt/celt_encoder.c
Comment thread celt/celt_encoder.c Outdated
Comment thread celt/ecintrin.h Outdated
@janpbuethe janpbuethe force-pushed the jbuethe_oac_multichannel_clean branch from 1411dc2 to eafaf8f Compare May 15, 2026 20:41
@janpbuethe janpbuethe force-pushed the jbuethe_oac_multichannel_clean branch from eafaf8f to d695da4 Compare May 15, 2026 20:54
@jmvalin jmvalin merged commit d695da4 into oac-next May 15, 2026
88 checks passed
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.

4 participants