- BrainTools - https://www.braintools.ru -

Auro-3D: как в обычном PCM прячут высотные каналы

Я давно интересуюсь звуком, писал обзоры по железу, писал про Dolby Atmos. Меня не оставлял в покое формат Auro 3D, который нам обещает многоканальный звук, а чтобы послушать нужно покупать дорогой AVR, который из 5.17.1 мог извлечь верха.

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

Микрофоны для записи auro 3d

Микрофоны для записи auro 3d

Исследование

Скачав несколько демо роликов и образов дисков, я обнаружил лишь звуковые дорожки в формате DTS HD MA (или многоканальный Flacwav) классической раскладки 5.17.1, а популярная утилита MediaInfo [1] ничего не показывала про высотные каналы, тогда как железный ресивер (с нужным декодером) эти дорожки показывал как Auro 9.1 или 13.1.

Тогда я начал искать официальную документацию, но большинство ссылок были устаревшими, а новые вели лишь на маркетинговое описание формата. Но webarchive и другие файлпомойки мне помогли и я все таки нашел старое описание декореда. Ссылка на доки в конце статьи [2].

Оттуда я узнал что Auro 3D встраивает информацию в нижние биты 24 битного PCM сигнала. Нейронки в 2025 мало что знали о формате или только информацию из wiki. Тогда я декодировал с помощью ffmpeg звук до многоканального PCM 24bit, разделил по каналам, быстренько накидал на python скрипт, который из 24 битного PCM извлекал фиксированные только верхние 4 бита и тогда я воочию увидел метаданные Auro, вот пример тишины, но содержащие магические биты информации:

Auro-3D: как в обычном PCM прячут высотные каналы - 2

Эти блоки повторялись (внутри были и постоянные данным и меняющиеся), а потом и смешивались с реальными звуковыми сигналами (их паттерн все еще можно было разглядеть в потоке двоичного кода). Центральный канал и LFE могли не содержать эту мету. Угадать, что за что какой бит отвечает, было нереально.

Потом начал искать декодер, приложения и официальный декодер платный, а все что удалось скачать — зашифрованообфусцировано. Те прошивки ресиверов которые удавалось расковырять, декодер auro был в формате для специализированных DSP. И наконец мне улыбнулась удача и я нашел декодер в ELF, да и еще с сохранёнными debug символами. И началась работа над реверс инжинирингом, сначала над детектором layout [3], а потом и над полноценным декодером.

Я долго ковырял этот формат изнутри и таки раскопал + помогли LLM и утилиты для реверс инжиниринга:

Вот сколько было потрачено
Auro-3D: как в обычном PCM прячут высотные каналы - 3

Примерно $1.5k, если бы покупал “в розницу” или по подписке за 20$*12 = 240$ + было еще сверх лимита.

Сначала немного теории

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

Auro-3D: как в обычном PCM прячут высотные каналы - 4
Auro-3D: как в обычном PCM прячут высотные каналы - 5
  • 9.1 = 5.1 + 4 высоты

  • 11.1 = 7.1 + 4 высоты

  • 13.1 = 7.1 + 5 высот + Top

Auro-3D: как в обычном PCM прячут высотные каналы - 6

Есть специфический Auro 2D, в котором в стерео (в контейнере он 2.1) смикширован звук в 5.1 (и по тестам, декодер очень качественно их восстанавливает). Так же есть редкие 4.0.4H или 5.1.2H.

Профессиональное оборудование поддерживает до 27 каналов — AuroMax® 26.1 и выглядит устрашающе:

Auro-3D: как в обычном PCM прячут высотные каналы - 7

Но по HDMI можно отдать только 8 дискретных каналов без сжатия, а для, например, 9.1 уже нужно 10. Это как раз позволяет сделать Auro:

закодировать высоту внутрь обычного PCM 5.1/7.1 так, чтобы без декодера файл звучал как нормальный surround, а с декодером — разворачивался в полный Auro с высотой.

Где прячется мета

Отдельного «мета-трека» нет. Всё живёт в младших битах 24-bit PCM.

Идея из white paper [2] простая:

Auro-3D: как в обычном PCM прячут высотные каналы - 8
  • для доставки звука 24 бита избыточны: реальный динамический диапазон системы/слуха ближе к 16–20 битам;

  • нижние 2–4 бита почти «шум»;

  • их и используют под служебные данные и residuals (ошибки [4] предсказания).

Что именно лежит в 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, чтобы не портить тишину. Если громко — можно забрать больше из полезного сигнала.

Auro-3D: как в обычном PCM прячут высотные каналы - 9

Любая нормализация/ресемпл/громкость «поверх» такого 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-3D: как в обычном PCM прячут высотные каналы - 10

Без 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 линейно одной формулой на всё», а:

  1. берёт carrier-сэмплы из DelayLine;

  2. разворачивает residuals через Golomb-Rice;

  3. прогоняет Extrapolate — получает до 2–3 выходов;

  4. bed-слот, который участвовал в dematrix, помечается как восстановленный bed (иногда почти тихий, если в carrier почти вся энергия была «высотной» для identification-стримов);

  5. height уходит в native-выход.

Важный практический момент: после mix3 нельзя просто скопировать сырые LS/RS из carrier обратно в bed. В channel-id тестах там как раз остаются «объявления» высотных каналов. Bed надо брать из dematrix/unmix, height — из native-реконструкции.

В декодере есть отдельный пайплайн, когда может запрашиваться большее количество каналов чем то, что получилось после декодирования, тогда в дело вступает Auromatic — специальный апмиксер, он достраивает алгоритмически недостающие каналы.

Без декодера вы слышите совместимый surround: высота уже «вшита» в bed-микс. С декодером этот микс разделяется обратно на слои (до трех).

Теряется ли качество после dematrix?

На слух [5] нет, НО битовой точности — нет ¯_(ツ)_/¯

Звук в Auro может быть сохранен внутри любого lossless контейнера поддерживающий многоканальный звук в 24bit это WAV, FLAC, DTS HD MA и даже Dolby TrueHD. Но железные плееры обычно работают только с DTS HD MA.

Сама компания Auro называет это virtually lossless:

  • это не perceptual coding как mp3/AAC/AC-3 (частоты не выкидывают «потому что мозг [6] не услышит»);

  • residuals и quant дают маленькую ошибку;

  • в white paper артефакты часто ниже −85 dBFS, иногда около −90 dBFS;

  • спектр ошибки больше похож на низкоуровневый шум, чем на pre-echo/filter-bank кашу lossy-кодеков;

  • заявляется 100% разделение каналов после декодирования.

Где качество всё же страдает:

  1. LSB substitution — младшие биты carrier больше не содержат «чистого» звука, а смешаны с метой. Напомню при прослушивании без Auro декодера обычно неслышно, но это уже не исходный 24-bit master as-is.

  2. Quant/scale — если side-budget жмут, энкодер сильнее квантует residuals (rate–distortion).

  3. Округления предиктора — Extrapolate работает в фиксированной арифметике.

  4. Повторные бессмысленные encode/decode в целом заявлены устойчивыми, но любой «кривой» remux с громкостью/дизером поверх Auro-PCM ломает все.

В моих тестовых файлах оказалось лишь два файлы в исходном многоканальном формате и закодированный в Auro и вот результаты сравнения:

orig5.1.flac [7] vs декод auro2d.flac [8] 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 [9] — консольный декодер 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 

Параметры

Вход / выход

Параметр

Описание

-i, --input

Входной файл: WAV, FLAC, MKV, MP4, M2TS, DTS, raw .s24le

-o, --output

Куда писать. По умолчанию <input>_decoded.wav (или decodedbinaural.wav с --binaural)

--output-format

wav (без лимита каналов) или flac (макс. 8 каналов)

--output-bits

Глубина PCM на выходе: 16 или 24 (по умолчанию 24)

--raw

Вход — сырой interleaved s24le; нужны --rate и --channels

--rate / --channels

Частота и число каналов для --raw

Диагностика (без декода в файл)

Параметр

Описание

--probe

Показать layout, carrier, sync, ADOL, маски — без записи PCM

--channel-diagram

Схема «что из carrier → что на выходе» (dematrix / native / passthrough)

-v, --verbose

Подробный лог

Ядро декода (лучше не трогать) 

Параметр

Описание

--block N

Размер внутреннего host-блока. По умолчанию подбирается так, чтобы кадры кодека не рвались; fallback 832 (как у native)

--dsp-output-channels N

Сколько каналов на выходе. 0 / не указано = авто из меты. Потолок native — 27. Для legacy PCM без меты: 6=5.1, 10=5.1.4, 12=7.1.4

--dsp-strength N

«Сила» DSP/рендера, 0..15, по умолчанию 12

--dsp-headroom-db X

Запас по уровню на всём выходе, 0..24 дБ, по умолчанию 0

Порядок каналов и экспорт

Параметр

Описание

--wav-standard

Переставить каналы в канонический WAVEFORMATEXTENSIBLE порядок и прописать dwChannelMask (удобно для плееров/DAW)

--clear-output-lsb [N]

Обнулить младшие N бит на экспорте (1..8, по умолчанию 4). Срезает остатки sync/ADOL с passthrough-каналов — для «чистого» PCM

--mono-tracks

Дополнительно выгрузить каждый канал отдельным моно-файлом: … (FL).wav и т.п.

Наушники / виртуализация

Параметр

Описание

--binaural

Свести декод в стерео через HRTF (принудительно 48 kHz)

--room-preset N

Помещение: 0 HOME, 1 CONCERT, 2 LOUNGE, 3 CINEMA

--hrtf-preset N

HRTF: 0 HPV2, 1..3 GENERIC_1..3

--virtualizer-mode N

0 ENABLED, 1 DISABLED

Экспериментальное

Параметр

Описание

--restore-lfe

Если LFE тихий/пустой — синтезировать из bed (mono sum + LPF 120 Hz, −10 dB). Не native-путь


Что декодер делает по шагам

  1. Demux через ffmpeg → PCM24

  2. Скан LSB → sync / ADOL → layout + carrier

  3. Codec-v3: DelayLine → Sync → Parser → Golomb-Rice → Extrapolate (unmix bed/height)

  4. (Опционально) binaural / restore-lfe / wav-standard / clear LSB

  5. WAV/FLAC + XML mapping

Ссылки

Вся официальная дока [10], которую только смог найти.

Мой получившийся декодер [9], можете использовать бесплатно, не для коммерческого использования, с указанием автора @almirus.

Автор: almirus

Источник [11]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/34144

URLs in this post:

[1] MediaInfo: https://mediaarea.net/MediaInfo

[2] статьи: #docs

[3] детектором layout: https://github.com/MediaArea/MediaInfoLib/pull/2531

[4] ошибки: http://www.braintools.ru/article/4192

[5] слух: http://www.braintools.ru/article/6251

[6] мозг: http://www.braintools.ru/parts-of-the-brain

[7] orig5.1.flac: https://touch-max.ru/wp-content/uploads/2026/07/orig5.1.flac

[8] auro2d.flac: https://touch-max.ru/wp-content/uploads/2026/07/auro2d.flac

[9] orua3d-decode: https://touch-max.ru/zvuk/auro-3d-kak-v-obychnom-pcm-pryachut-vysotnye-kanaly#download

[10] официальная дока: https://touch-max.ru/wp-content/uploads/2026/07/doc.zip

[11] Источник: https://habr.com/ru/articles/1068212/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1068212

www.BrainTools.ru

Rambler's Top100