Язык Си в примерах/Декодирование звукозаписи в формате ADX

Материал из Викиучебника — открытых книг для открытого мира

Язык Си в примерах


  1. Компиляция программ
  2. Простейшая программа «Hello World»
  3. Учимся складывать
  4. Максимум
  5. Таблица умножения
  6. ASCII-коды символов
  7. Верхний регистр
  8. Скобочки
  9. Факториал
  10. Степень числа
  11. Треугольник Паскаля
  12. Корень уравнения
  13. Система счисления
  14. Сортировка
  15. Библиотека complex
  16. Сортировка на основе qsort
  17. RPN-калькулятор
  18. RPN-калькулятор на Bison
  19. Простая грамматика
  20. Задача «Расчёт сопротивления схемы»
  21. Простая реализация конечного автомата
  22. Использование аргументов командной строки
  23. Чтение и печать без использования stdio
  24. Декодирование звукозаписи в формате ADX
  25. Другие примеры

ADX — формат сжатия звукозаписи с потерями, разработанный для игровых приставок Sega Dreamcast, Sony PlayStation и GameCube.

Заголовок файла[править]

Формат диска ADX определён в порядке байт от старшего к младшему. Опознанные разделы главного заголовка

0 1 2 3 4 5 6 7 8 9 A B C D E F
0x0 0x80 0x00 Смещение данных об авторских правах Тип кодирования Размер блока Глубина образца в битах Счётчик каналов Частота выдачи образцов Общее количество образцов
0x10 Частота среза верхних частот Версия Флаги Неизвестно Зацикливание включено (v3) Указатель на образец в начале цикла (v3)
0x20 Указатель на байт в начале цикла (v3) Зацикливание включено (v4)

Указатель на образец в конце цикла (v3)

Указатель на образец в начале цикла (v4)

Указатель на байт в конце цикла (v3)

Указатель на байт в начале цикла (v4)
0x30 Указатель на образец в конце цикла (v4) Указатель на байт в конце цикла (v4) Ноль или больше байт пустого пространства
??? [Смещение данных об авторских правах (CopyrightOffset) - 2] строка ASCII (unterminated): "(c)CRI"
... [Смещение данных об авторских правах (CopyrightOffset) + 4] Здесь начинаются данные звукозаписи

Поля, помеченные как «неизвестно» содержат либо неизвестные данные, либо просто зарезервированы (например, заполнены нулевыми байтами). Поля, помеченные «v3» или «v4», но не обеими версиями, равнозначны «неизвестным» в той версии, к которой они не относятся. Также следует обратить внимание на то, что этот заголовок может быть укорочен до 20 байт (0x14), что при задании авторских прав неявно удаляет поддержку цикла, так как эти поля не представлены («затираются»).

Поле «тип кодирования» должно содержать одно из значений:

  • 0x03 для стандартного ADX
  • 0x04 для ADX с экспоненциальной шкалой (масштабом)
  • 0x10 или 0x11 для AHX

Поле «версия» должно содержать одно из значений:

  • 0x02 для одной из 'версии 3' с различными чётко указанными декодировщиками
  • 0x03 для ADX 'версии 3'
  • 0x04 для ADX 'версии 4'
  • 0x05 для версии ADX 4 без внутренней поддержки цикла

При декодировании звукозаписи в формате AHX , считывание значения в поле «версия» не представлено и может быть безопасно пропущено.

Формат образца[править]

Звукозапись, кодированная в ADX разбита на несколько последовательностей 'блоков', каждая из которых содержит данные только для одного канала. Блоки затем раскладываются в 'кадры', каждый из которых состоит из набора по одному блоку из каждого канала в порядке возрастания. Например, в стерео (2 канала) поток будет состоять из: кадра 1: блок левого канала, блок правого канала; кадра 2: левый канал, правый канал; и т.д. Обычный размер блока: 18 бит, содержащих образцы по 4 бита или других технически возможных размеров. Образец блока выглядит так:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Масштаб 32 4-битных образца

Масштаб — это 16-ти битовая беззнаковая целочисленная переменная [integer] (с байтами идущими от старшего к младшему, как и в заголовке), которая по существу значением является усиления всех образцов в этом блоке. Каждый образец должен быть декодирован в порядке потока бит, так что в начале идёт сам бит знак переменной. Например, когда размер образца составляет 4 бита:

7 6 5 4 3 2 1 0
Первый образец Второй образец

Сами по себе образцы не обращены, так что нет необходимости дополнительно работать с ними после извлечения. Каждый образец имеет знак, так что для этого примера значение может быть в диапазоне между -8 и +7 (который может расширяться масштабом во время декодирования). С другой стороны, благодаря заголовку, допустима любая глубина между 1 и 255. Маловероятно, что когда-либо появятся однобитовые образцы, так как они могут представлять только значения {0, 1}, {-1, 0} или {-1, 1}, каждое из которых малопригодно для кодирования музыки — если такие всё-таки появятся, неизвестно какой из трёх вариантов набора значений верен для интерпретации.

Декодирование ADX[править]

Этот раздел посвящён декодированию ADX версии 3 или 4, в котором указатель «тип кодирования» установлен в «стандартный ADX» (0x03). Кодировщик можно построить, «перевернув» алгоритм, указанный в дальнейшем коде. Все примеры кода написаны с использованием C99.

Перед тем, как кодировать/декодировать стандартный ADX, необходимо вычислить коэффициенты предсказания. В основном это лучше делать на начальном этапе:

 #define M_PI acos(-1.0)
 double a, b, c;
 a = sqrt(2.0) - cos(2.0 * M_PI * ((double)adx_header->highpass_frequency / adx_header->sample_rate));
 b = sqrt(2.0) - 1.0;
 c = (a - sqrt((a + b) * (a - b))) / b; //(a+b)*(a-b) = a*a-b*b, в любом случае, более простая формула теряет в точности числа с плавающей запятой
 
 // удвоение coefficient[2];
 coefficient[0] = c * 2.0;
 coefficient[1] = -(c * c);

Этот код рассчитывает коэффициенты предсказания для предсказания текущего образца из 2 предыдущих образцов.

Когда мы знаем коэффициенты декодирования, мы можем начинать декодировать поток:

 static int32_t*      past_samples; // В начале обнуляются ранее декодированные образцы каждого канала (size = 2*channel_count)
 static uint_fast32_t sample_index = 0; // sample_index это указатель последовательности образцов, которую в дальнейшем необходимо декодировать
 static ADX_header*   adx_header;
 
 // buffer (буфер) это область памяти, в которую мы будем помещать декодированные образцы
 // samples_needed устанавливает, как много 'последовательностей' образцов (один образец из каждого канала), чтобы заполнить буфер (buffer)
 // looping_enabled это логический флаг для управления использованием встроенного цикла
 // Возвращает количество 'последовательностей' образцов в буфер (buffer), которое не может его заполнить (EOS)
 unsigned decode_adx_standard( int16_t* buffer, unsigned samples_needed, bool looping_enabled )
 {
  unsigned const samples_per_block = (adx_header->block_size - 2) * 8 / adx_header->sample_bitdepth;
  int16_t scale[ adx_header->channel_count ];
 
  if (looping_enabled && !adx_header->loop_enabled)
     looping_enabled = false;
 
  // Повторяется до тех пор, пока не будет декодировано достаточное количество образцов, или не будет достигнут конец файла
  while (samples_needed > 0 && sample_index < adx_header->total_samples)
  {
     // Рассчитывает количество образцов, которое осталось декодировать в текущем блоке
     unsigned sample_offset = sample_index % samples_per_block;
     unsigned samples_can_get = samples_per_block - sample_offset;

     // Сокращает количество образцов, которые мы можем получить в этом проходе, если их количество не помещается в буфер (buffer)
     if (samples_can_get > samples_needed)
        samples_can_get = samples_needed;
 
     // Сокращает количество забираемых образцов, если поток недостаточно длинный, или рядом расположен переключатель цикла
     if (looping_enabled && sample_index + samples_can_get > adx_header->loop_end_index)
        samples_can_get = adx_header->loop_end_index - sample_index;
     else if (sample_index + samples_can_get > adx_header->total_samples)
        samples_can_get = adx_header->total_samples - sample_index;
 
     // Рассчитывает битовый адрес начала кадра, в котором расположен sample_index, и записывает это расположение
     unsigned long started_at = (adx_header->copyright_offset + 4 + \
                     sample_index / samples_per_block * adx_header->block_size * adx_header->channel_count) * 8;
 
     // Читает значения масштаба от начала каждого блока в этом кадре
     for (unsigned i = 0 ; i < adx_header->channel_count ; ++i)
     {
        bitstream_seek( started_at + adx_header->block_size * i * 8 );
        scale[i] = ntohs( bitstream_read( 16 ) );
     }
 
     // Предварительно рассчитывает значение останова для sample_offset
     unsigned sample_endoffset = sample_offset + samples_can_get;
 
     // Сохраняет адрес первого кадра в потоке бит сразу после масштаба в первом блоке кадра
     started_at += 16;
     while ( sample_offset < sample_endoffset )
     {
        for (unsigned i = 0 ; i < adx_header->channel_count ; ++i)
        {
           // Предсказывает следующий образец
           double sample_prediction = coefficient[0] * past_samples[i*2 + 0] + coefficient[1] * past_samples[i*2 + 1];
 
           // Переходит до смещения образца, читает и переписывает его расширяя до целочисленной переменной 32-х бит
           // Реализация знакового расширения остаётся в качестве упражнения для читателя
           // Знаковое расширение также необходимо для включения конечной подгонки, если в значении более 8 бит
           bitstream_seek( started_at + adx_header->sample_bitdepth * sample_offset + \
                           adx_header->block_size * 8 * i );
           int_fast32_t sample_error = bitstream_read( adx_header->sample_bitdepth );
           sample_error = sign_extend( sample_error, adx_header->sample_bitdepth );
 
           // Масштабирование значения коррекции ошибок
           sample_error *= scale[i];
 
           // Расчёт образца путём совмещения предсказания и коррекции ошибок
           int_fast32_t sample = sample_error + (int_fast32_t)sample_prediction;
 
           // Обновление последних образцов более новым образцом
           past_samples[i*2 + 1] = past_samples[i*2 + 0];
           past_samples[i*2 + 0] = sample;
 
           // Сокращение декодированного образца до верного диапазона для целочисленной переменной 16 -бит
           if (sample > 32767)
              sample = 32767;
           else if (sample < -32768)
              sample = -32768;
 
           // Сохранение образца в буфер (buffer) и увеличение переход на одну позицию
           *buffer++ = sample;
        }
        ++sample_offset;  // Мы декодировали по образцу из каждого блока, переводим указатель смещения блоков на 1
        ++sample_index;   // Это также означает, что у нас есть ещё один образец в потоке
        --samples_needed; // Также это означает, что осталось на одну меньше последовательностей образцов, которые нужно декодировать
    }
 
    // Проверяем, не 'захватили' ли мы указатель окончания цикла, если да - переходим на начало цикла
    if (looping_enabled && sample_index == adx_header->loop_end_index)
       sample_index = adx_header->loop_start_index;
  }
 
  return samples_needed;
 }

Большей части этого кода должно быть достаточно для любого, кто разбирается в языке программирования Си.


Дятел