К основному контенту

Как декодировать звуковые файлы *.xa в PC версии игры "Bugs Bunny - Lost in Time"?


Не смотря на то, что эта игра портирована с консоли Sony PlayStation, расширение XA вовсе не означает, что это плейстейшновский XA, а Exe-файл - эмулятор, как может показаться на первый взгляд.
Во-первых, если сравнивать версию для PS1 и PC, вторая имеет другую структуру папок, другое расширение файлов ресурсов (BZE вместо BZZ), сами данные - не сжаты, как в PS1-версии (хотя периодически они прерываются "FF" байтом, который выглядит как буква "я"); то что в PS1 версии было Vag-файлами, здесь имеет формат AIFF (только в заголовке почему-то для всех файлов прописана частота 44100 khz в виде байт "AC 44" - это Big Endian), а XA-файлы закодированы в так называемый IMA ADPCM.

Чем декодировать?

Для этого уже существует инструмент "ImaEnc" от автора "Redsh". Скачать его можно здесь.
Умеет не только декодировать, но и кодировать обратно. Приложение - консольное, но необходимые батники уже созданы. Работа проверена на Windows 10 x64. Можете сразу протестировать: распакуйте файлы в папку speech (в каталоге с игрой) и запустите Decode.bat. Можно внести изменения в Wav файл и закодировать его обратно с помощью Encode.bat.

Что это за формат такой?

Следующий текст взят отсюда:
http://we.easyelectronics.ru/Soft/szhatie-zvuka-v-ima-adpcm.html
Автор: Lifelover
------------------------------------------------------------------------------------------------------------
Есть такой аудиокодек — IMA ADPCM. Сжимает аудио в 4 бита на сэмпл (250 кбит/с при частоте дискретизации 32 кГц, стерео), после чего, достаточно достоверно восстанавливает. А главное, очень быстро. Около 100 тактов на сэмпл на ядре AVR.

Как это работает?

Возьмём обычный файл звук.wav, закодированный в самом распространённом формате — PCM, 16 бит на сэмпл. Амплитуда каждого сэмпла представлена в нём числом от -32768 до 32767. Возьмём и заменим каждый сэмпл на разницу (дельта) между ним и предыдущим сэмлом. Получим формат DPCM Differential PCM. Но тут мы ничего не выиграли, полученная дельта уже и в 16 бит не влезает. Получается число от -65535 до 65535.

Но что, если уменьшить разрядность дельты? Если сигнал нарастает или убывает незначительно, всё будет хорошо. Но на крутых подъёмах и спусках изменение будет превышать разрядность дельты, возникнут искажения.

А если вместо дельты использовать дельту, умноженную на некоторый шаг? Например, если шаг равен 16, при изменении амплитуды сигнала на 64, дельта будет равна 4. Теперь, если сигнал изменяется значительно, процент искажений будет незначительным, однако при небольших изменениях сигнала, опять же, возникнут искажения, небольшие изменения могут просто пропасть.

Но что, если сделать шаг изменяемым. При небольших изменениях сигнала — маленький, при значительных — большой. Учитывая, что амплитуда сигнала — не случайная величина, а более-менее гладкая функция, мы сможем корректно изменять шаг, и минимизировать искажения.

Данный формат называется ADPCM Adaptive Differential PCM. Осталось решить как рассчитывать изменение шага. Существует несколько алгоритмов. Здесь мы поговорим о IMA ADPCM. Это быстрый и простой алгоритм, оптимизированный для музыки.

Шаг берётся из специальной таблицы (ima_step_table), заполненной волшебными числами. Номер (step_index) шага в этой таблице изменяется после кодирования каждого сэмпла, в зависимости от скорости нарастания или убывания сигнала, по другой таблице (ima_index_table). Сжатый сэмпл занимает 4 бита (nibble) — 1 бит для знака и 3 для абсолютного значения (т.о. сжатый сэмпл может принимать значения от -7 до 7).

Сжатие:

step = step_table[step_index]
diff = sample - prev_sample
nibble = diff / step
step_index = step_index + index_table[nibble]

Восстановление:

step = step_table[step_index]
diff = nibble * step
sample = prev_sample + diff
step_index = step_index + index_table[nibble]
При сжатии и восстановлении обязательно нужно учитывать возможные переполнения sample и step_index. Они случаются достаточно редко, но если случаются, то могут привести к плачевным последствиям. sample должен быть ограничен пределами от -32768 до 32767, step_index — от 0 до 88.

Кстати, этот кодек — отличный пример, показывающий превосходства программирования на ассемблере. Взять те же переполнения. При программировании на языке высокого уровня информация о переполнениях теряется, приходится использовать более длинный тип данных, и делать медленные сравнения. Что ужасно сказывается на производительности.

Ладно, вот моя реализация декодера.

/* -------------------------------------------------------------------------- */
#pragma once
/* -------------------------------------------------------------------------- */
typedef struct ima_state {
    int16_t sample;
    int8_t index;
} ima_state;
/* -------------------------------------------------------------------------- */
void imadec(ima_state *state, uint8_t value);
/* -------------------------------------------------------------------------- */
;------------------------------------------------------------------------------
    .global imadec
; ------------------------------------------------------------------------------
#define XL        R26
#define XH        R27
#define ZL        R30
#define ZH        R31
; ------------------------------------------------------------------------------
/*
    R0    *
    R1    zero
    R18    sample
    R19    sample
    R20    step
    R21    step
    R22    value
    R23    index
    R24    state
    R25    state
    R26    XL
    R27    XH
    R30    ZL
    R31    ZH
*/
;------------------------------------------------------------------------------
imadec:
    movw        XL,R24 ; state
   
    ld            R18,X+    ; sample
    ld            R19,X+
    ld            R23,X    ; index
    ; load step
    ldi            ZL,lo8(step_table)
    ldi            ZH,hi8(step_table)
    mov            XL,R23
    lsl            XL
    add            ZL,XL
    adc            ZH,R1
    lpm            R20,Z+            ; step = step_table[index]
    lpm            R21,Z
    ; diff = (value + 0.5) * step / 4
    clr            XL                ; diff = 0
    clr            XH
    sbrs        R22,2            ; if(value & 4) diff += step
    rjmp        __1
    add            XL,R20
    adc            XH,R21
__1:
    lsr            R21                ; step >>= 1
    ror            R20
   
    sbrs        R22,1            ; if(value & 2) diff += step
    rjmp        __2
    add            XL,R20
    adc            XH,R21       
__2:
    lsr            R21                ; step >>= 1
    ror            R20
    sbrs        R22,0            ; if(value & 1) diff += step
    rjmp        __3
    add            XL,R20
    adc            XH,R21
__3:
    lsr            R21                ; step >>= 1
    ror            R20
   
    add            XL,R20            ; diff += step
    adc            XH,R21
    ; update sample
    ldi            R21,0x80
    add            R19,R21            ; sample += 0x8000
    sbrs        R22,3            ; if(value & 7)
    rjmp        __4
    sub            R18,XL            ;         sample -= diff
    sbc            R19,XH
    brcc        __5
   
    clr            R18                ;         if(sample < 0) sample = 0
    clr            R19
    rjmp        __5
__4:                            ; else
    add            R18,XL            ;        sample += diff
    adc            R19,XH
    brcc        __5
    ser            R18                ;        if(sample > 0xffff) sample = 0xffff
    ser            R19
__5:
    sub            R19,R21            ; sample -= 0x8000
    ; update index
    ldi            ZL, lo8(index_table)
    ldi            ZH, hi8(index_table)
    andi        R22,7            ; value &= 7
    add            ZL,R22
    adc            ZH,R1
    lpm            R20,Z
    add            R23,R20            ; index += index_table[value]
    brpl        __6                ; if(index < 0)
    clr            R23                ;     index = 0
    rjmp        __7
__6:
    cpi            R23,89            ; if(index >= 89)
    brlo        __7
    ldi            R23,88            ;     index = 88
__7:
    movw        XL,R24
    st            X+,R18
    st            X+,R19
    st            X,R23
    ret
; ------------------------------------------------------------------------------
index_table:
    .byte        -1, -1, -1, -1, 2, 4, 6, 8
; ------------------------------------------------------------------------------
step_table:
    .word        7,     8,     9,    10,    11,    12,    13,    14
    .word       16,    17,    19,    21,    23,    25,    28,    31
    .word       34,    37,    41,    45,    50,    55,    60,    66
    .word       73,    80,    88,    97,   107,   118,   130,   143
    .word      157,   173,   190,   209,   230,   253,   279,   307
    .word      337,   371,   408,   449,   494,   544,   598,   658
    .word      724,   796,   876,   963,  1060,  1166,  1282,  1411
    .word     1552,  1707,  1878,  2066,  2272,  2499,  2749,  3024
    .word     3327,  3660,  4026,  4428,  4871,  5358,  5894,  6484
    .word     7132,  7845,  8630,  9493, 10442, 11487, 12635, 13899
    .word    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794
    .word    32767
; ------------------------------------------------------------------------------
А вот пример использования

void playima(char *name)
{
    uint8_t buff[512];
    WORD cb;
    uint16_t n;
    ima_state state[2];
    uint8_t data;
    if(!pf_open(name))
    {
        snd_freq(44100);
        state[0].sample = 0;
        state[0].index = 0;
        state[1].sample = 0;
        state[1].index = 0;
        while(!pf_read(buff, sizeof(buff), &cb))
        {
            for(n = 0; n < cb; ++n)
            {
                data = buff[n];
                imadec(state+0, data);
                imadec(state+1, data>>4);
                snd_out( (state[0].sample>>8)+0x80, (state[1].sample>>8)+0x80 );
            }
            if(cb != sizeof(buff))
                break;
        }
    }
snd_freq устанавливает частоту загрузки данных из кольцевого буфера в регистр PWM
snd_out кладет данные в кольцевой буфер

А здесь можно скачать прогу для перекодирования WAV файлов в IMA и пример прошивки, воспроизводящей файл, сжатый IMA с SD-карточки.

зы. Данный кодек я очень люблю, и юзаю как в девайсах на МК, так и в прогах для компа, для передачи звука по сети (в этом случае можно сверху ещё deflate'ом прижать).
-------------------------------------------------------------------------------------------------------------------------

Пример PC-версии игры с измененным XA звуком:


Комментарии

Самое просматриваемое

Где Vegas Pro хранит автосохраненные проекты?

12 -ая версия, например, хранит их здесь: %USERPROFILE%\AppData\Local\Sony\Vegas Pro\12.0 16 -ая хранит проекты по тому же пути, только немного в другой папке: %USERPROFILE%\AppData\Local\VEGAS Pro\16.0 

Нет доступа к рабочей группе "WORKGROUP". Ошибка "Нет доступа к Workgroup. Возможно, у вас нет прав на использование этого сетевого ресурса".

Ситуация: есть рабочая группа " WORKGROUP " и три компьютера в сети, объединенные через роутер. На одном из них установлена Windows XP Professional SP3 , на другом - Windows 10 SL . В один прекрасный день, попытавшись получить с XP-шного компьютера доступ к компьютеру с Windows 10 , я даже не смог отобразить компьютеры рабочей группы (т. е. увидеть все компьютеры в сети). Вместо этого красовалось сообщение об ошибке, гласящее: "Нет доступа к Workgroup. Возможно у вас нет прав на использование этого сетевого ресурса...Обратитесь к системному администратору. Служба не запущена". Оказалось, что проблема главным образом заключалась в компьютере с Windows 10 : ошибка выскакивала при попытке отобразить в списке именно его, и как следствие - не открывалась рабочая группа. РЕШЕНИЕ (во всяком случае, мне это помогло) На компьютере с  Windows 10 заходим в Службы ( Win + R > services.msc ) Находим службу "Браузер компьютеров" и останавливаем

Сеть отсутствует или не запущена. Решение

Запустить командную строку от имени администратора: Пуск > в строке поиска вписать cmd , нажать на иконку правой кнопкой мыши выбрать « Запустить от имени администратора ». В командную строку скопируйте sc config mrxsmb10 start= demand и нажмите Enter . Нажимаем Win+R и вводим services.msc Находим службу “ Рабочая станция ” и нажав правой кнопкой мыши выбираем пункт « Запустить ». Примечание: служба не запустится , если не выполнить пункт 2 . Запуск службы лучше поставить на " Автоматически ". Вот и все. Источник:  http://studenthelper.hol.es/%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0/%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D1%8B-%D1%81%D0%B5%D1%82%D1%8C-%D0%BE%D1%82%D1%81%D1%83%D1%82%D1%81%D1%82%D0%B2%D1%83%D0%B5%D1%82-%D0%B8/

Как удалить виртуальный дисковод, созданный UltraISO из "Проводника"?

Не смотря на то, что в Windows 10 уже давно есть поддержка открытия образов дисков ( Открыть с помощью > Проводник ), некоторые типы образов она не открывает (например, образы с играми для  PS1 ). Поэтому приходится прибегать к использованию сторонних программ, популярной из которых считается UltraISO (благодаря простому интерфейсу, малому размеру и нетребовательности к ресурсам ПК). Но даже у таких простых простых программ порой случаются глюки. И проблема чаще всего связана с виртуальным приводом. Проблема В моем же случае, у меня на компьютере "застрял" виртуальный дисковод, который занял букву временно отключенного внешнего HDD . Все бы ничего, но на нем ( HDD ) располагались видеофайлы материала, который я в данный момент монтирую, и на которые (с учетом исходной буквы диска) прописаны ссылки в файле проекта монтажки. Поэтому, самым простым вариантом на мой взгляд было вернуть внешнему диску исходную букву (которую занял виртуальный привод), а виртуальному просто на

FL Studio. Не перетаскиваются файлы

Зайти в Свойства > Совместимость > Изменить параметры для всех пользователей , снять галочку "Выполнять от имени Администратора" > OK , снять еще одну (уже разблокированную) галочку "Выполнять от имени Администратора" > OK .