Я давно интересуюсь звуком, писал обзоры по железу, писал про Dolby Atmos. Меня не оставлял в покое формат Auro 3D, который нам обещает многоканальный звук, а чтобы послушать нужно покупать дорогой AVR, который из 5.17.1 мог извлечь верха.
Формат Auro 3D был представлен бельгийской компанией Galaxy Studios в 2006 году и тогда назывался Octopus. В 2011 стал доступен в потребительских устройствах, а в 2019 был представлен новый формат Auro-Cx, который кроме канального кодирования может использовать и объектное, как, например, в Dolby Atmos и DTS:X

Исследование
Скачав несколько демо роликов и образов дисков, я обнаружил лишь звуковые дорожки в формате DTS HD MA (или многоканальный Flacwav) классической раскладки 5.17.1, а популярная утилита MediaInfo ничего не показывала про высотные каналы, тогда как железный ресивер (с нужным декодером) эти дорожки показывал как Auro 9.1 или 13.1.
Тогда я начал искать официальную документацию, но большинство ссылок были устаревшими, а новые вели лишь на маркетинговое описание формата. Но webarchive и другие файлпомойки мне помогли и я все таки нашел старое описание декореда. Ссылка на доки в конце статьи.
Оттуда я узнал что Auro 3D встраивает информацию в нижние биты 24 битного PCM сигнала. Нейронки в 2025 мало что знали о формате или только информацию из wiki. Тогда я декодировал с помощью ffmpeg звук до многоканального PCM 24bit, разделил по каналам, быстренько накидал на python скрипт, который из 24 битного PCM извлекал фиксированные только верхние 4 бита и тогда я воочию увидел метаданные Auro, вот пример тишины, но содержащие магические биты информации:

Эти блоки повторялись (внутри были и постоянные данным и меняющиеся), а потом и смешивались с реальными звуковыми сигналами (их паттерн все еще можно было разглядеть в потоке двоичного кода). Центральный канал и LFE могли не содержать эту мету. Угадать, что за что какой бит отвечает, было нереально.
Потом начал искать декодер, приложения и официальный декодер платный, а все что удалось скачать — зашифрованообфусцировано. Те прошивки ресиверов которые удавалось расковырять, декодер auro был в формате для специализированных DSP. И наконец мне улыбнулась удача и я нашел декодер в ELF, да и еще с сохранёнными debug символами. И началась работа над реверс инжинирингом, сначала над детектором layout, а потом и над полноценным декодером.
Я долго ковырял этот формат изнутри и таки раскопал + помогли LLM и утилиты для реверс инжиниринга:
Примерно $1.5k, если бы покупал “в розницу” или по подписке за 20$*12 = 240$ + было еще сверх лимита.Вот сколько было потрачено

Сначала немного теории
Auro-3D — канальный иммерсивный формат: нижний bed (как 5.1/7.1) плюс слой высоты (HL, HR, HLS, HRS, Top/HC). Типичные потребительские раскладки:


-
9.1 = 5.1 + 4 высоты
-
11.1 = 7.1 + 4 высоты
-
13.1 = 7.1 + 5 высот + Top

Есть специфический Auro 2D, в котором в стерео (в контейнере он 2.1) смикширован звук в 5.1 (и по тестам, декодер очень качественно их восстанавливает). Так же есть редкие 4.0.4H или 5.1.2H.
Профессиональное оборудование поддерживает до 27 каналов — AuroMax® 26.1 и выглядит устрашающе:

Но по HDMI можно отдать только 8 дискретных каналов без сжатия, а для, например, 9.1 уже нужно 10. Это как раз позволяет сделать Auro:
закодировать высоту внутрь обычного PCM 5.1/7.1 так, чтобы без декодера файл звучал как нормальный surround, а с декодером — разворачивался в полный Auro с высотой.
Где прячется мета
Отдельного «мета-трека» нет. Всё живёт в младших битах 24-bit PCM.
Идея из white paper простая:

-
для доставки звука 24 бита избыточны: реальный динамический диапазон системы/слуха ближе к 16–20 битам;
-
нижние 2–4 бита почти «шум»;
-
их и используют под служебные данные и residuals (ошибки предсказания).
Что именно лежит в LSB
В мете не просто пара флагов, а можно сказать, полноценный side-channel (конечно после восстановления):
Глобальная ADOL-мета (layout/boot):
-
целевой layout (например
5.1+4H); -
carrier layout (что физически в файле);
-
нужен ли
mix3(три выхода из одного carrier); -
metadata_block(часто 1000 или 1024 сэмпла); -
sync_sample— где впервые ловится синхрослово.
Per-frame данные по каналам:
-
какой выходной канал восстанавливаем;
-
от какого carrier-source предсказываем;
-
режим Extrapolate (
1/2/3); -
quant/scale;
-
параметры Golomb-Rice;
-
seeds предиктора;
-
упакованные residuals (не PCM высоты целиком!).
Синхронизация общая: декодер OR’ит LSB по живым carrier-каналам и ловит sync (типично 16 единиц подряд в младшем бите). Полезный битстрим с важным residuals при этом у каналов свой.
Внутри целая стейтмашина:
# ============================================================
# ADOL state machine (classic Auro-Codec, LSB side-channel)
# ============================================================
enum State:
SEEK_SYNC
PARSE_SYNC_HEADER
CHECK_CRC
PARSE_A3D_HEADER
PARSE_ADOL_BLOCKS
PARSE_ADOL_INSTR
APPLY_SEMANTICS
DONE
FAIL
procedure ScanAdol(pcm_channels):
meta = empty
for ch in pcm_channels:
samples = channel_pcm24(ch)
state = SEEK_SYNC
run_ones = 0
s = 0
while s < len(samples) and state != DONE:
switch state:
# --- 1) охота за sync: 16 единиц подряд в bit0 ---
case SEEK_SYNC:
if (samples[s] & 1) == 1:
run_ones += 1
else:
run_ones = 0
if run_ones >= 16:
sync_start = s - 15
state = PARSE_SYNC_HEADER
else:
s += 1
# --- 2) разбор sync: m (число LSB), block_size ---
case PARSE_SYNC_HEADER:
sync = ParseSyncAt(samples, sync_start)
if not sync.ok:
run_ones = 0
state = SEEK_SYNC
s += 1
continue
if sync_start + sync.block_size > len(samples):
state = FAIL
else:
state = CHECK_CRC
# --- 3) CRC16-CCITT по блоку ---
case CHECK_CRC:
if CRC16(samples[sync_start .. +block_size]) != ExpectedCRC(...):
run_ones = 0
state = SEEK_SYNC
s += 1
else:
state = PARSE_A3D_HEADER
# --- 4) заголовок A3D-блока до ADOL ---
case PARSE_A3D_HEADER:
bits = MuxBitstream(samples, sync_start, m=sync.m)
# первые 32 бита уже съедены sync'ом
skip: u8, 4×bool, u4, 2×u8, 2×bool, u2, u4, u8
adol_blocks = read_u8(bits)
skip: u8
# optional vector size зависит от count≠255 в 4 байтах
count = count_non_255(read 4×u8)
skip 4×u8
vec_sz = {2→2, 3→5, else FAIL}
skip vec_sz × u32
state = PARSE_ADOL_BLOCKS
b = 0
instructions = []
# --- 5) ADOL-блоки: каждый начинается с tag==1 ---
case PARSE_ADOL_BLOCKS:
if b >= adol_blocks:
state = APPLY_SEMANTICS
continue
tag = read_u8(bits)
if tag != 1:
state = FAIL
continue
state = PARSE_ADOL_INSTR
# --- 6) инструкции до opcode==0 (END) ---
case PARSE_ADOL_INSTR:
opcode = read_u8(bits)
ins = {block:b, tag, opcode}
switch opcode:
case 0x00: # END
# payload нет
case 0x01, 0x03, 0x5A..0x62:
ins.value = read_bits(16)
case 0x02, 0x04, 0x1E, 0x1F, 0x41, 0x47, 0x50..0x58:
ins.value = read_bits(8)
case 0x0E, 0x46, 0x6E..0x76, 0x80..0x85, 0x8C..0x90:
ins.value = read_bits(32)
case 0x40: # primary downmix gain
ins.value = read_bits(8) # channel
ins.value2 = read_bits(8) # scaler
case 0x64..0x6C: # encoder version и рядом
ins.value = read_bits(24)
default:
state = FAIL
continue
instructions.append(ins)
if opcode == 0x00:
b += 1
state = PARSE_ADOL_BLOCKS
# иначе остаёмся в PARSE_ADOL_INSTR
# --- 7) семантика ключевых opcode ---
case APPLY_SEMANTICS:
for ins in instructions:
switch ins.opcode:
case 0x1E: # ChannelInputConfig
meta.layout_id = OriginalLayout(ins.value)
meta.carrier_id = CarrierLayout(ins.value)
case 0x40:
meta.primary_downmix = (ch=ins.value, scaler=ins.value2)
case 0x41:
meta.limit_simple = ins.value
case 0x46:
meta.secondary_downmix = ins.value
case 0x47:
meta.auromatic = (profile=value>>4, mode=value&0xF)
case 0x64:
meta.encoder_version = ins.value & 0xFFFFFF
case 0x80..0x85:
meta.loudness = ins.value
meta.adol_instructions.append(ins)
if meta.layout_id == 0:
state = FAIL
else:
meta.found = true
meta.sync_sample = sync_start
meta.block_size = sync.block_size
meta.carrier_ch = ch
state = DONE
case FAIL:
run_ones = 0
state = SEEK_SYNC
s += 1
case DONE:
return meta
# после успешного блока прыгаем за его конец
if state == DONE:
break
if just_finished_good_block:
s = sync_start + sync.block_size
run_ones = 0
# можно продолжить сканировать следующие sync,
# но для boot достаточно первого валидного layout
return meta
Число младших бит не константа на весь трек. Кодек смотрит уровень блоками (~1 мс @ 48 kHz) и, если сигнал тихий, забирает меньше LSB, чтобы не портить тишину. Если громко — можно забрать больше из полезного сигнала.

Любая нормализация/ресемпл/громкость «поверх» такого PCM легко убивает мету.
Как восстанавливаются высотные каналы
Это не просто интерполяция, как в старых matrix-кодеках и не психоакустическая модель.
Это controlled mix → unmix.
На encode:
оригинал bed + height
↓ художественный mix (с gain’ами)
carrier 5.1/7.1 (уже содержит энергию высоты)
+
LSB: residuals + параметры разделения
На decode:
DelayLine(carrier)
→ Sync/Format detector
→ Parser (frame meta)
→ Golomb-Rice (распаковка residuals)
→ Extrapolate (unmix)
→ discrete bed + height
Режимы Extrapolate
Mode 2 (mix2) — из одного carrier + одного residual-потока собираются два канала.
Mode 3 (mix3) — из одного carrier + двух residuals — уже три выхода. Нужен для Auro-2D и тяжёлых раскладок вроде 7.1 + 5H + T. Предиктор трёхфазный: первые сэмплы получаются из меты, дальше что-то в духе 4*last − 3*previous, residuals раскидываются по фазе.
Как высоты «вычитаются» из нижних
Схематично

Без Auro обычная сумма звуковых каналов необратима: S = A + B. С Auro в LSB есть информация, чтобы максимально приближённо восстановить A' и B' и при этом развести то, что было смешано с gain’ами.
Упрощённо для пары bed/height:
carrier ≈ g1·bed + g2·height (+ квантование/LSB-замещение)
декодирование:
height’ = f(carrier, residuals, predictor, scales)
bed’ = unmix(carrier, height’, gains…)
На практике OutputGenerator не «отнимает height из WAV линейно одной формулой на всё», а:
-
берёт carrier-сэмплы из DelayLine;
-
разворачивает residuals через Golomb-Rice;
-
прогоняет Extrapolate — получает до 2–3 выходов;
-
bed-слот, который участвовал в dematrix, помечается как восстановленный bed (иногда почти тихий, если в carrier почти вся энергия была «высотной» для identification-стримов);
-
height уходит в native-выход.
Важный практический момент: после mix3 нельзя просто скопировать сырые LS/RS из carrier обратно в bed. В channel-id тестах там как раз остаются «объявления» высотных каналов. Bed надо брать из dematrix/unmix, height — из native-реконструкции.
В декодере есть отдельный пайплайн, когда может запрашиваться большее количество каналов чем то, что получилось после декодирования, тогда в дело вступает Auromatic — специальный апмиксер, он достраивает алгоритмически недостающие каналы.
Без декодера вы слышите совместимый surround: высота уже «вшита» в bed-микс. С декодером этот микс разделяется обратно на слои (до трех).
Теряется ли качество после dematrix?
На слух нет, НО битовой точности — нет ¯_(ツ)_/¯
Звук в Auro может быть сохранен внутри любого lossless контейнера поддерживающий многоканальный звук в 24bit это WAV, FLAC, DTS HD MA и даже Dolby TrueHD. Но железные плееры обычно работают только с DTS HD MA.
Сама компания Auro называет это virtually lossless:
-
это не perceptual coding как mp3/AAC/AC-3 (частоты не выкидывают «потому что мозг не услышит»);
-
residuals и quant дают маленькую ошибку;
-
в white paper артефакты часто ниже −85 dBFS, иногда около −90 dBFS;
-
спектр ошибки больше похож на низкоуровневый шум, чем на pre-echo/filter-bank кашу lossy-кодеков;
-
заявляется 100% разделение каналов после декодирования.
Где качество всё же страдает:
-
LSB substitution — младшие биты carrier больше не содержат «чистого» звука, а смешаны с метой. Напомню при прослушивании без Auro декодера обычно неслышно, но это уже не исходный 24-bit master as-is.
-
Quant/scale — если side-budget жмут, энкодер сильнее квантует residuals (rate–distortion).
-
Округления предиктора — Extrapolate работает в фиксированной арифметике.
-
Повторные бессмысленные encode/decode в целом заявлены устойчивыми, но любой «кривой» remux с громкостью/дизером поверх Auro-PCM ломает все.
В моих тестовых файлах оказалось лишь два файлы в исходном многоканальном формате и закодированный в Auro и вот результаты сравнения:
orig5.1.flac vs декод auro2d.flac 96000 Hz, 6 ch
|
ch |
corr |
ref_rms |
cand_rms |
gain_db |
|---|---|---|---|---|
|
FL |
0.999527 |
0.009154 |
0.009147 |
-0.006 |
|
FR |
0.999676 |
0.008253 |
0.008248 |
-0.005 |
|
C |
0.999692 |
0.008783 |
0.008778 |
-0.005 |
|
LFE |
в дорожку auro2d не включили lfe канал |
0.003858 |
0.000000 |
nan |
|
LS |
0.999410 |
0.008814 |
0.008806 |
-0.008 |
|
RS |
0.999783 |
0.007584 |
0.007581 |
-0.004 |
Сравнение других муз треков с WAV 7.1.4 → Auro 5.1+5H+T (без BL/BR):
|
ch |
corr |
ref_rms |
cand_rms |
gain_db |
|---|---|---|---|---|
|
FL |
0.980811 |
0.063336 |
0.067622 |
0.569 |
|
FR |
0.974268 |
0.062315 |
0.066608 |
0.579 |
|
C |
1.000000 |
0.092218 |
0.086228 |
-0.583 |
|
LFE |
1.000000 |
0.046164 |
0.043083 |
-0.600 |
|
SL |
0.861498 |
0.023975 |
0.020202 |
-1.487 |
|
SR |
0.819390 |
0.023686 |
0.018894 |
-1.963 |
|
TFL |
0.999849 |
0.010921 |
0.011993 |
0.813 |
|
TFR |
0.999815 |
0.012149 |
0.013190 |
0.714 |
|
TBL |
0.886241 |
0.010921 |
0.016209 |
3.430 |
|
TBR |
0.922870 |
0.012149 |
0.017331 |
3.086 |
-
corr — корреляция Пирсона между каналом reference и candidate (после выравнивания по lag).
1.0= формы волны совпадают,0= не связаны, отрицательное = в противофазе.nan= один из каналов тишина (нулевая энергия). -
ref_rms — RMS reference-канала, нормированный на full-scale PCM24 (
÷ 2²³). По сути относительная громкость оригинала (0…1). -
cand_rms — то же для candidate (декод).
-
gain_db — разница уровней:
20·log10(cand_rms / ref_rms).0≈ одинаковая громкость,+= декод громче,−= тише.nan— если RMS одного из каналов ноль.
Dolby Atmos vs DTS:X vs Auro-3D — сравнение
|
Параметр |
Dolby Atmos |
DTS:X |
Auro-3D |
|---|---|---|---|
|
Разработчик |
Dolby Laboratories |
DTS |
Auro Technologies / Galaxy Studios |
|
Принцип работы |
Объектно-ориентированный звук |
Объектно-ориентированный звук |
Канальная архитектура с вертикальными слоями |
|
Пространственная модель |
Объекты свободно размещаются в 3D-пространстве |
Объекты свободно размещаются в 3D-пространстве |
Нижний слой + верхний слой + Voice of God |
|
Типичные конфигурации |
5.1.2, 5.1.4, 7.1.4, 7.1.6 |
5.1, 7.1, 5.1.2, 7.1.4 и другие |
9.1, 10.1, 11.1, 13.1 |
|
Высотные каналы |
Потолочные или up-firing |
Потолочные или другие высотные позиции |
Верхний слой колонок + Voice of God |
|
Voice of God |
Нет |
Нет |
Да, в старших конфигурациях |
|
Работа с объектами |
Да |
Да |
В классическом Auro-3D — нет; AuroCX использует объектный подход |
|
Blu-ray / UHD Blu-ray |
Dolby TrueHD + Atmos |
DTS-HD MA + DTS:X |
PCMFlac или DTS-HD MA в зависимости от реализации |
|
Стриминг |
Очень широкая поддержка |
Ограниченная поддержка |
Практически отсутствует |
|
Игры |
Широкая поддержка |
Ограниченная поддержка |
Практически не используется |
|
Музыка |
Большой и постоянно растущий каталог |
Ограниченный каталог |
Сильная сторона формата; существуют специальные музыкальные релизы |
|
Апмиксер |
Dolby Surround |
DTS Neural:X |
Auro-Matic |
|
Главное преимущество |
Максимальная экосистема и доступность контента |
Гибкость и качественный объектный звук |
Естественная вертикальная звуковая сцена |
|
Главный недостаток |
Качество зависит от конкретного микса и установки системы |
Меньше контента, особенно в стриминге |
Мало нативного контента и специфические требования к акустике |
|
Лучше всего подходит для |
Фильмов, сериалов, игр и стриминга |
Домашнего кино и физических носителей |
Музыки, апмиксинга и энтузиастов многоканального аудио |
|
Итог |
Лучший универсальный выбор |
Сильная альтернатива Atmos |
Интересный вариант для музыки и специализированных систем |
Декодер
По результатам года работы был написан декодер на основе реверс инжиниринга оригинального от Auro. Это бета версия.
orua3d-decode — консольный декодер Auro-3D. Берёт файл с Auro-Codec (PCM/FLAC/MKV/DTS…) или AuroCX (MP4 a3ds), находит мету в LSB, разворачивает высоты/bed и пишет обычный многоканальный (с высотными каналами) WAV/FLAC.
На входе нужен ffmpeg/ffprobe в PATH (для demux). Рядом с WAV пишется XML с картой каналов.
Минимум:
orua3d-decode -i input.flac
Параметры
Вход / выход
|
Параметр |
Описание |
|---|---|
|
|
Входной файл: WAV, FLAC, MKV, MP4, M2TS, DTS, raw |
|
|
Куда писать. По умолчанию |
|
|
|
|
|
Глубина PCM на выходе: |
|
|
Вход — сырой interleaved s24le; нужны |
|
|
Частота и число каналов для |
Диагностика (без декода в файл)
|
Параметр |
Описание |
|---|---|
|
|
Показать layout, carrier, sync, ADOL, маски — без записи PCM |
|
|
Схема «что из carrier → что на выходе» (dematrix / native / passthrough) |
|
|
Подробный лог |
Ядро декода (лучше не трогать)
|
Параметр |
Описание |
|---|---|
|
|
Размер внутреннего host-блока. По умолчанию подбирается так, чтобы кадры кодека не рвались; fallback 832 (как у native) |
|
|
Сколько каналов на выходе. |
|
|
«Сила» DSP/рендера, |
|
|
Запас по уровню на всём выходе, |
Порядок каналов и экспорт
|
Параметр |
Описание |
|---|---|
|
|
Переставить каналы в канонический WAVEFORMATEXTENSIBLE порядок и прописать |
|
|
Обнулить младшие |
|
|
Дополнительно выгрузить каждый канал отдельным моно-файлом: |
Наушники / виртуализация
|
Параметр |
Описание |
|---|---|
|
|
Свести декод в стерео через HRTF (принудительно 48 kHz) |
|
|
Помещение: |
|
|
HRTF: |
|
|
|
Экспериментальное
|
Параметр |
Описание |
|---|---|
|
|
Если LFE тихий/пустой — синтезировать из bed (mono sum + LPF 120 Hz, −10 dB). Не native-путь |
Что декодер делает по шагам
-
Demux через ffmpeg → PCM24
-
Скан LSB → sync / ADOL → layout + carrier
-
Codec-v3: DelayLine → Sync → Parser → Golomb-Rice → Extrapolate (unmix bed/height)
-
(Опционально) binaural / restore-lfe / wav-standard / clear LSB
-
WAV/FLAC + XML mapping
Ссылки
Вся официальная дока, которую только смог найти.
Мой получившийся декодер, можете использовать бесплатно, не для коммерческого использования, с указанием автора @almirus.
Автор: almirus


