Сжатие звука в IMA ADPCM

Как это работает?
Возьмём обычный файл звук.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'ом прижать).

- +7
- 16 марта 2011, 06:42
- Lifelover
полезно-полезно, спасибо!
Лично я в реализациях для PC сижу на тяжеленном speex :) Для передачи узкополосных сигналов без искажения пакетов он хорош
Лично я в реализациях для PC сижу на тяжеленном speex :) Для передачи узкополосных сигналов без искажения пакетов он хорош
Если есть потери, стоит порезать поток на фреймы. В начале каждого фрейма записать индекс и текущее значение сэмпла. Каждый фрейм — в отдельный пакет. Потеряной инфы, конечно, не восстановишь, но искажений не будет. Насчёт помех — не знаю, но явно результат будет хуже, чем тупо PCM. Потом, IMA лучше прижать zlib'ом, так то он для передачи по сети в чистом виде не оч подходит. А вот zlib искажений не терпит.
А где ты нашёл канал с помехами? Юзаешь UDP без проверки контрольной суммы?
А где ты нашёл канал с помехами? Юзаешь UDP без проверки контрольной суммы?
нет, канал с помехами — это радиоканал ;) а с потерями, но без искажения — udp/tcp. Лично я много и часто использую передачу речи по сети, и для этого выбрал speex. Но подобные, легкие для контроллеров, алгоритмы меня интересовали уже давно. Пока что нашел melp, но он несколько сложен… :) А в IMA можно быстро перекодировать на лету, и это очень хорошо!
декодер/кодер:
#include "ul_audio.h"
// 4->16 bit IMA ADPCM tables of index changes and quantizer step size lookup
#ifdef __AVR
S8 PROGMEM adpcmima_0416_index_tab[16] = {-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
U16 PROGMEM adpcmima_0416_stepsize_tab[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
#else
S8 adpcmima_0416_index_tab[16] = {-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8};
U16 adpcmima_0416_stepsize_tab[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
#endif
U16 adpcmima_0416_dec(U8 sample_in, U16 sample_prev, U8 *index_prev) {
U16 diffq, step;
S8 index;
sample_prev += 32768; /*convert to unsigned*/
index = *index_prev;
/* Find quantizer step size from lookup table using index */
#ifdef __AVR
step = pgm_read_word(&adpcmima_0416_stepsize_tab[index]);
#else
step = adpcmima_0416_stepsize_tab[index];
#endif
/* Inverse quantize the ADPCM code into a difference using the quantizer step size */
if (sample_in & 4) diffq = step; else diffq = 0; step >>= 1;
if (sample_in & 2) diffq += step; step >>= 1;
if (sample_in & 1) diffq += step; step >>= 1;
diffq += step;
/* Add the difference to the predicted sample */
if (sample_in & 8) {
if (sample_prev > diffq) sample_prev -= diffq;
else sample_prev = 0;
} else {
if ((0xffff - sample_prev) > diffq) sample_prev += diffq;
else sample_prev = 0xffff;
}
sample_prev -= 32768;
/* Find new quantizer step size by adding the old index and a table lookup using the ADPCM code */
#ifdef __AVR
index += pgm_read_byte(&adpcmima_0416_index_tab[sample_in & 0xf]);
#else
index += adpcmima_0416_index_tab[sample_in & 0xf];
#endif
/* Check for overflow of the new quantizer step size index */
if (index < 0) index = 0;
else if (index > 88) index = 88;
*index_prev = index;
return(sample_prev);
}
U8 adpcmima_0416_enc(U16 sample_in, U16 *sample_prev, S8 *index_prev) {
S32 diff; /* Difference between sample and the predicted sample */
U16 step; /* Quantizer step size */
S16 sample_pred; /* Output of ADPCM predictor */
S32 diffq; /* Dequantized predicted difference */
S8 index; /* Index into step size table */
U8 sample_out; /* Result of encoding */
/*restore previous values of predicted sample and quantizer step size index*/
sample_pred = *sample_prev; /*convert predicted sample to unsigned format*/
index = *index_prev;
#ifdef __AVR
step = pgm_read_word(&adpcmima_0416_stepsize_tab[index]);
#else
step = adpcmima_0416_stepsize_tab[index];
#endif
/*compute the difference between the input sample (sample_in) and the the predicted sample (sample_pred)*/
diff = (S32)sample_in - (S32)sample_pred;
if (diff >= 0) sample_out = 0; else {sample_out = 8; diff = -diff;}
/* Quantize the difference into the 4-bit ADPCM code using the the quantizer step size */
/* Inverse quantize the ADPCM code into a predicted difference using the quantizer step size */
diffq = 0;
if (diff >= step) {sample_out |= 4; diff -= step; diffq += step;}
step >>= 1;
if (diff >= step) {sample_out |= 2; diff -= step; diffq += step;}
step >>= 1;
if (diff >= step) {sample_out |= 1; diffq += step;}
step >>= 1;
diffq += step;
/* Fixed predictor computes new predicted sample by adding the old predicted sample to predicted difference */
if (sample_out & 8) sample_pred -= diffq; else sample_pred += diffq;
/* Check for overflow of the new predicted sample */
if (sample_pred > 32767) sample_pred = 32767; else if (sample_pred < -32767) sample_pred = -32767;
/* Find new quantizer stepsize index by adding the old index to a table lookup using the ADPCM code */
#ifdef __AVR
index += pgm_read_byte(&adpcmima_0416_index_tab[sample_out]);
#else
index += adpcmima_0416_index_tab[sample_out];
#endif
/* Check for overflow of the new quantizer step size index */
if (index < 0) index = 0; else if (index > 88) index = 88;
/* Save the predicted sample and quantizer step size index for next iteration */
*sample_prev = sample_pred;
*index_prev = index;
return(sample_out);
}
Всё равно вот что выдаёт: http://pastebin.com/pYCMCFXr
Что у вас за среда? Скорее всего, у меня в GCC нет заголовочных файлов каких-то…
Что у вас за среда? Скорее всего, у меня в GCC нет заголовочных файлов каких-то…
U8,S8,U16,U32 определить не забыли?
#include <stdint.h>
#define U8 uint8_t
#define S8 int8_t
#define U16 uint16_t
#define S16 int16_t
#define S32 int32_t
для коллекции uLaw:
// uLaw encoder sample bias value
#define ULAW_0816_BIAS 0x84
// uLaw encoder sample clipping value
#define ULAW_0816_CLIP 32635
#ifdef __AVR
U16 PROGMEM ulaw_0816_expdec[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
U8 PROGMEM ulaw_0816_expenc[] = {
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};
#else
const U16 ulaw_0816_expdec[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
const U8 ulaw_0816_expenc[] = {
0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};
#endif
// uLaw decoder (8->16 bit)
U16 ulaw_0816_dec(U8 sample_in) {
U8 exponent;
U16 sample_out;
sample_in = ~sample_in;
exponent = (sample_in >> 4) & 7;
#ifdef __AVR
sample_out = pgm_read_word(&ulaw_0816_expdec[exponent]);
#else
sample_out = ulaw_0816_expdec[exponent];
#endif
sample_out += (((U16)((sample_in) & 0xf)) << (exponent + 3));
if (sample_in & 0x80) sample_out = -((S16)sample_out);
return sample_out;
}
// uLaw encoder (16->8 bit)
U8 ulaw_0816_enc(U16 sample_in) {
U8 sign, exponent, mantissa, sample_out;
if ((S16)sample_in < 0) {
sign = 0x80;
sample_in = -(S16)sample_in;
} else sign = 0;
if (sample_in > ulaw_0816_clip) sample_in = ulaw_0816_clip;
sample_in += ulaw_0816_bias;
#ifdef __AVR
exponent = pgm_read_word(&ulaw_0816_expenc[((U16)sample_in >> 7) & 0xff]);
#else
exponent = ulaw_0816_expenc[((U16)sample_in >> 7) & 0xff];
#endif
mantissa = (sample_in >> (exponent + 3)) & 0xf;
sample_out = (sign | (exponent << 4) | mantissa);
if (sample_out == 0) sample_out = 2;
return sample_out;
}
Добрый день!
Я разбираю формат карт для автомагнитол MITSUBISHI
Столкнулся с файлами, которые отвечают за голосовые фразы
С помщью программы Audacity — расжал код фразы, но все равно не то.
Прошу вашей помощи/консультации:
— понять какой алгоритм расжатия/сжатия звука
— или если у Вас есть готовый кодек под мой вариант:
— подскажите: с какими параметрами и как мне получить хорошее качество звучания фраз с поомощью Вашего кодека
Вот пример:
— исходный сжатый код (RAW)data_30_0. Это, первая фраза, что находится в файле voice001.me. Звучит так «расчет маршрута завершен»
— Если его не расжимать а в нем тупо сделать шапку для Wav => 11_00_8_30_0_dec.wav то фразу слышно, но много шумов.
— если этот RAW data_30_0 пропустить через Audacity -> import->звуковой файл без загловка: с параметрами=>
— кодировка: Signed 8 bit PCM
— порядок байт: litle-endian
— каналов: 1
— весь файл (100%)
— частота дискредитации: 11025
То получим более четкую фразу, но все равно с шумами
Когда я этот файл (11_00_8_30_2_dec.wav) уже из под Audacity сохраняю, то получаю:
— исходный расжатый код (RAW) data_30_2 -> размер увеличился в 2 раз
— чатота дискредитации: 11025
— Количество байт, переданных за секунду воспроизведения, byteRate: 22050
— формат PCM (код =1)
— глубина звука: моно (1), 16 бит (Hambit)
Файлы можно скачать отсюда solk.org.ua/voice.rar
Заранее благодарен за помощь, просьба не пинать сильно, если что-то не грамотно описал. Я с этим раньше вообще не работал
Я разбираю формат карт для автомагнитол MITSUBISHI
Столкнулся с файлами, которые отвечают за голосовые фразы
С помщью программы Audacity — расжал код фразы, но все равно не то.
Прошу вашей помощи/консультации:
— понять какой алгоритм расжатия/сжатия звука
— или если у Вас есть готовый кодек под мой вариант:
— подскажите: с какими параметрами и как мне получить хорошее качество звучания фраз с поомощью Вашего кодека
Вот пример:
— исходный сжатый код (RAW)data_30_0. Это, первая фраза, что находится в файле voice001.me. Звучит так «расчет маршрута завершен»
— Если его не расжимать а в нем тупо сделать шапку для Wav => 11_00_8_30_0_dec.wav то фразу слышно, но много шумов.
— если этот RAW data_30_0 пропустить через Audacity -> import->звуковой файл без загловка: с параметрами=>
— кодировка: Signed 8 bit PCM
— порядок байт: litle-endian
— каналов: 1
— весь файл (100%)
— частота дискредитации: 11025
То получим более четкую фразу, но все равно с шумами
Когда я этот файл (11_00_8_30_2_dec.wav) уже из под Audacity сохраняю, то получаю:
— исходный расжатый код (RAW) data_30_2 -> размер увеличился в 2 раз
— чатота дискредитации: 11025
— Количество байт, переданных за секунду воспроизведения, byteRate: 22050
— формат PCM (код =1)
— глубина звука: моно (1), 16 бит (Hambit)
Файлы можно скачать отсюда solk.org.ua/voice.rar
Заранее благодарен за помощь, просьба не пинать сильно, если что-то не грамотно описал. Я с этим раньше вообще не работал
Комментарии (31)
RSS свернуть / развернуть