1
mirror of https://git.videolan.org/git/ffmpeg.git synced 2024-09-13 18:49:01 +02:00
ffmpeg/libavcodec/ra144enc.c
Anton Khirnov 2df0c32ea1 lavc: use a separate field for exporting audio encoder padding
Currently, the amount of padding inserted at the beginning by some audio
encoders, is exported through AVCodecContext.delay. However
- the term 'delay' is heavily overloaded and can have multiple different
  meanings even in the case of audio encoding.
- this field has entirely different meanings, depending on whether the
  codec context is used for encoding or decoding (and has yet another
  different meaning for video), preventing generic handling of the codec
  context.

Therefore, add a new field -- AVCodecContext.initial_padding. It could
conceivably be used for decoding as well at a later point.
2014-10-13 19:09:01 +00:00

560 lines
19 KiB
C

/*
* Real Audio 1.0 (14.4K) encoder
* Copyright (c) 2010 Francesco Lavra <francescolavra@interfree.it>
*
* This file is part of Libav.
*
* Libav is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Libav is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Real Audio 1.0 (14.4K) encoder
* @author Francesco Lavra <francescolavra@interfree.it>
*/
#include <float.h>
#include "avcodec.h"
#include "audio_frame_queue.h"
#include "celp_filters.h"
#include "internal.h"
#include "mathops.h"
#include "put_bits.h"
#include "ra144.h"
static av_cold int ra144_encode_close(AVCodecContext *avctx)
{
RA144Context *ractx = avctx->priv_data;
ff_lpc_end(&ractx->lpc_ctx);
ff_af_queue_close(&ractx->afq);
return 0;
}
static av_cold int ra144_encode_init(AVCodecContext * avctx)
{
RA144Context *ractx;
int ret;
if (avctx->channels != 1) {
av_log(avctx, AV_LOG_ERROR, "invalid number of channels: %d\n",
avctx->channels);
return -1;
}
avctx->frame_size = NBLOCKS * BLOCKSIZE;
avctx->initial_padding = avctx->frame_size;
avctx->bit_rate = 8000;
ractx = avctx->priv_data;
ractx->lpc_coef[0] = ractx->lpc_tables[0];
ractx->lpc_coef[1] = ractx->lpc_tables[1];
ractx->avctx = avctx;
ret = ff_lpc_init(&ractx->lpc_ctx, avctx->frame_size, LPC_ORDER,
FF_LPC_TYPE_LEVINSON);
if (ret < 0)
goto error;
ff_af_queue_init(avctx, &ractx->afq);
return 0;
error:
ra144_encode_close(avctx);
return ret;
}
/**
* Quantize a value by searching a sorted table for the element with the
* nearest value
*
* @param value value to quantize
* @param table array containing the quantization table
* @param size size of the quantization table
* @return index of the quantization table corresponding to the element with the
* nearest value
*/
static int quantize(int value, const int16_t *table, unsigned int size)
{
unsigned int low = 0, high = size - 1;
while (1) {
int index = (low + high) >> 1;
int error = table[index] - value;
if (index == low)
return table[high] + error > value ? low : high;
if (error > 0) {
high = index;
} else {
low = index;
}
}
}
/**
* Orthogonalize a vector to another vector
*
* @param v vector to orthogonalize
* @param u vector against which orthogonalization is performed
*/
static void orthogonalize(float *v, const float *u)
{
int i;
float num = 0, den = 0;
for (i = 0; i < BLOCKSIZE; i++) {
num += v[i] * u[i];
den += u[i] * u[i];
}
num /= den;
for (i = 0; i < BLOCKSIZE; i++)
v[i] -= num * u[i];
}
/**
* Calculate match score and gain of an LPC-filtered vector with respect to
* input data, possibly othogonalizing it to up to 2 other vectors
*
* @param work array used to calculate the filtered vector
* @param coefs coefficients of the LPC filter
* @param vect original vector
* @param ortho1 first vector against which orthogonalization is performed
* @param ortho2 second vector against which orthogonalization is performed
* @param data input data
* @param score pointer to variable where match score is returned
* @param gain pointer to variable where gain is returned
*/
static void get_match_score(float *work, const float *coefs, float *vect,
const float *ortho1, const float *ortho2,
const float *data, float *score, float *gain)
{
float c, g;
int i;
ff_celp_lp_synthesis_filterf(work, coefs, vect, BLOCKSIZE, LPC_ORDER);
if (ortho1)
orthogonalize(work, ortho1);
if (ortho2)
orthogonalize(work, ortho2);
c = g = 0;
for (i = 0; i < BLOCKSIZE; i++) {
g += work[i] * work[i];
c += data[i] * work[i];
}
if (c <= 0) {
*score = 0;
return;
}
*gain = c / g;
*score = *gain * c;
}
/**
* Create a vector from the adaptive codebook at a given lag value
*
* @param vect array where vector is stored
* @param cb adaptive codebook
* @param lag lag value
*/
static void create_adapt_vect(float *vect, const int16_t *cb, int lag)
{
int i;
cb += BUFFERSIZE - lag;
for (i = 0; i < FFMIN(BLOCKSIZE, lag); i++)
vect[i] = cb[i];
if (lag < BLOCKSIZE)
for (i = 0; i < BLOCKSIZE - lag; i++)
vect[lag + i] = cb[i];
}
/**
* Search the adaptive codebook for the best entry and gain and remove its
* contribution from input data
*
* @param adapt_cb array from which the adaptive codebook is extracted
* @param work array used to calculate LPC-filtered vectors
* @param coefs coefficients of the LPC filter
* @param data input data
* @return index of the best entry of the adaptive codebook
*/
static int adaptive_cb_search(const int16_t *adapt_cb, float *work,
const float *coefs, float *data)
{
int i, best_vect;
float score, gain, best_score, best_gain;
float exc[BLOCKSIZE];
gain = best_score = 0;
for (i = BLOCKSIZE / 2; i <= BUFFERSIZE; i++) {
create_adapt_vect(exc, adapt_cb, i);
get_match_score(work, coefs, exc, NULL, NULL, data, &score, &gain);
if (score > best_score) {
best_score = score;
best_vect = i;
best_gain = gain;
}
}
if (!best_score)
return 0;
/**
* Re-calculate the filtered vector from the vector with maximum match score
* and remove its contribution from input data.
*/
create_adapt_vect(exc, adapt_cb, best_vect);
ff_celp_lp_synthesis_filterf(work, coefs, exc, BLOCKSIZE, LPC_ORDER);
for (i = 0; i < BLOCKSIZE; i++)
data[i] -= best_gain * work[i];
return best_vect - BLOCKSIZE / 2 + 1;
}
/**
* Find the best vector of a fixed codebook by applying an LPC filter to
* codebook entries, possibly othogonalizing them to up to 2 other vectors and
* matching the results with input data
*
* @param work array used to calculate the filtered vectors
* @param coefs coefficients of the LPC filter
* @param cb fixed codebook
* @param ortho1 first vector against which orthogonalization is performed
* @param ortho2 second vector against which orthogonalization is performed
* @param data input data
* @param idx pointer to variable where the index of the best codebook entry is
* returned
* @param gain pointer to variable where the gain of the best codebook entry is
* returned
*/
static void find_best_vect(float *work, const float *coefs,
const int8_t cb[][BLOCKSIZE], const float *ortho1,
const float *ortho2, float *data, int *idx,
float *gain)
{
int i, j;
float g, score, best_score;
float vect[BLOCKSIZE];
*idx = *gain = best_score = 0;
for (i = 0; i < FIXED_CB_SIZE; i++) {
for (j = 0; j < BLOCKSIZE; j++)
vect[j] = cb[i][j];
get_match_score(work, coefs, vect, ortho1, ortho2, data, &score, &g);
if (score > best_score) {
best_score = score;
*idx = i;
*gain = g;
}
}
}
/**
* Search the two fixed codebooks for the best entry and gain
*
* @param work array used to calculate LPC-filtered vectors
* @param coefs coefficients of the LPC filter
* @param data input data
* @param cba_idx index of the best entry of the adaptive codebook
* @param cb1_idx pointer to variable where the index of the best entry of the
* first fixed codebook is returned
* @param cb2_idx pointer to variable where the index of the best entry of the
* second fixed codebook is returned
*/
static void fixed_cb_search(float *work, const float *coefs, float *data,
int cba_idx, int *cb1_idx, int *cb2_idx)
{
int i, ortho_cb1;
float gain;
float cba_vect[BLOCKSIZE], cb1_vect[BLOCKSIZE];
float vect[BLOCKSIZE];
/**
* The filtered vector from the adaptive codebook can be retrieved from
* work, because this function is called just after adaptive_cb_search().
*/
if (cba_idx)
memcpy(cba_vect, work, sizeof(cba_vect));
find_best_vect(work, coefs, ff_cb1_vects, cba_idx ? cba_vect : NULL, NULL,
data, cb1_idx, &gain);
/**
* Re-calculate the filtered vector from the vector with maximum match score
* and remove its contribution from input data.
*/
if (gain) {
for (i = 0; i < BLOCKSIZE; i++)
vect[i] = ff_cb1_vects[*cb1_idx][i];
ff_celp_lp_synthesis_filterf(work, coefs, vect, BLOCKSIZE, LPC_ORDER);
if (cba_idx)
orthogonalize(work, cba_vect);
for (i = 0; i < BLOCKSIZE; i++)
data[i] -= gain * work[i];
memcpy(cb1_vect, work, sizeof(cb1_vect));
ortho_cb1 = 1;
} else
ortho_cb1 = 0;
find_best_vect(work, coefs, ff_cb2_vects, cba_idx ? cba_vect : NULL,
ortho_cb1 ? cb1_vect : NULL, data, cb2_idx, &gain);
}
/**
* Encode a subblock of the current frame
*
* @param ractx encoder context
* @param sblock_data input data of the subblock
* @param lpc_coefs coefficients of the LPC filter
* @param rms RMS of the reflection coefficients
* @param pb pointer to PutBitContext of the current frame
*/
static void ra144_encode_subblock(RA144Context *ractx,
const int16_t *sblock_data,
const int16_t *lpc_coefs, unsigned int rms,
PutBitContext *pb)
{
float data[BLOCKSIZE] = { 0 }, work[LPC_ORDER + BLOCKSIZE];
float coefs[LPC_ORDER];
float zero[BLOCKSIZE], cba[BLOCKSIZE], cb1[BLOCKSIZE], cb2[BLOCKSIZE];
int16_t cba_vect[BLOCKSIZE];
int cba_idx, cb1_idx, cb2_idx, gain;
int i, n, m[3];
float g[3];
float error, best_error;
for (i = 0; i < LPC_ORDER; i++) {
work[i] = ractx->curr_sblock[BLOCKSIZE + i];
coefs[i] = lpc_coefs[i] * (1/4096.0);
}
/**
* Calculate the zero-input response of the LPC filter and subtract it from
* input data.
*/
ff_celp_lp_synthesis_filterf(work + LPC_ORDER, coefs, data, BLOCKSIZE,
LPC_ORDER);
for (i = 0; i < BLOCKSIZE; i++) {
zero[i] = work[LPC_ORDER + i];
data[i] = sblock_data[i] - zero[i];
}
/**
* Codebook search is performed without taking into account the contribution
* of the previous subblock, since it has been just subtracted from input
* data.
*/
memset(work, 0, LPC_ORDER * sizeof(*work));
cba_idx = adaptive_cb_search(ractx->adapt_cb, work + LPC_ORDER, coefs,
data);
if (cba_idx) {
/**
* The filtered vector from the adaptive codebook can be retrieved from
* work, see implementation of adaptive_cb_search().
*/
memcpy(cba, work + LPC_ORDER, sizeof(cba));
ff_copy_and_dup(cba_vect, ractx->adapt_cb, cba_idx + BLOCKSIZE / 2 - 1);
m[0] = (ff_irms(cba_vect) * rms) >> 12;
}
fixed_cb_search(work + LPC_ORDER, coefs, data, cba_idx, &cb1_idx, &cb2_idx);
for (i = 0; i < BLOCKSIZE; i++) {
cb1[i] = ff_cb1_vects[cb1_idx][i];
cb2[i] = ff_cb2_vects[cb2_idx][i];
}
ff_celp_lp_synthesis_filterf(work + LPC_ORDER, coefs, cb1, BLOCKSIZE,
LPC_ORDER);
memcpy(cb1, work + LPC_ORDER, sizeof(cb1));
m[1] = (ff_cb1_base[cb1_idx] * rms) >> 8;
ff_celp_lp_synthesis_filterf(work + LPC_ORDER, coefs, cb2, BLOCKSIZE,
LPC_ORDER);
memcpy(cb2, work + LPC_ORDER, sizeof(cb2));
m[2] = (ff_cb2_base[cb2_idx] * rms) >> 8;
best_error = FLT_MAX;
gain = 0;
for (n = 0; n < 256; n++) {
g[1] = ((ff_gain_val_tab[n][1] * m[1]) >> ff_gain_exp_tab[n]) *
(1/4096.0);
g[2] = ((ff_gain_val_tab[n][2] * m[2]) >> ff_gain_exp_tab[n]) *
(1/4096.0);
error = 0;
if (cba_idx) {
g[0] = ((ff_gain_val_tab[n][0] * m[0]) >> ff_gain_exp_tab[n]) *
(1/4096.0);
for (i = 0; i < BLOCKSIZE; i++) {
data[i] = zero[i] + g[0] * cba[i] + g[1] * cb1[i] +
g[2] * cb2[i];
error += (data[i] - sblock_data[i]) *
(data[i] - sblock_data[i]);
}
} else {
for (i = 0; i < BLOCKSIZE; i++) {
data[i] = zero[i] + g[1] * cb1[i] + g[2] * cb2[i];
error += (data[i] - sblock_data[i]) *
(data[i] - sblock_data[i]);
}
}
if (error < best_error) {
best_error = error;
gain = n;
}
}
put_bits(pb, 7, cba_idx);
put_bits(pb, 8, gain);
put_bits(pb, 7, cb1_idx);
put_bits(pb, 7, cb2_idx);
ff_subblock_synthesis(ractx, lpc_coefs, cba_idx, cb1_idx, cb2_idx, rms,
gain);
}
static int ra144_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet_ptr)
{
static const uint8_t sizes[LPC_ORDER] = {64, 32, 32, 16, 16, 8, 8, 8, 8, 4};
static const uint8_t bit_sizes[LPC_ORDER] = {6, 5, 5, 4, 4, 3, 3, 3, 3, 2};
RA144Context *ractx = avctx->priv_data;
PutBitContext pb;
int32_t lpc_data[NBLOCKS * BLOCKSIZE];
int32_t lpc_coefs[LPC_ORDER][MAX_LPC_ORDER];
int shift[LPC_ORDER];
int16_t block_coefs[NBLOCKS][LPC_ORDER];
int lpc_refl[LPC_ORDER]; /**< reflection coefficients of the frame */
unsigned int refl_rms[NBLOCKS]; /**< RMS of the reflection coefficients */
const int16_t *samples = frame ? (const int16_t *)frame->data[0] : NULL;
int energy = 0;
int i, idx, ret;
if (ractx->last_frame)
return 0;
if ((ret = ff_alloc_packet(avpkt, FRAMESIZE))) {
av_log(avctx, AV_LOG_ERROR, "Error getting output packet\n");
return ret;
}
/**
* Since the LPC coefficients are calculated on a frame centered over the
* fourth subframe, to encode a given frame, data from the next frame is
* needed. In each call to this function, the previous frame (whose data are
* saved in the encoder context) is encoded, and data from the current frame
* are saved in the encoder context to be used in the next function call.
*/
for (i = 0; i < (2 * BLOCKSIZE + BLOCKSIZE / 2); i++) {
lpc_data[i] = ractx->curr_block[BLOCKSIZE + BLOCKSIZE / 2 + i];
energy += (lpc_data[i] * lpc_data[i]) >> 4;
}
if (frame) {
int j;
for (j = 0; j < frame->nb_samples && i < NBLOCKS * BLOCKSIZE; i++, j++) {
lpc_data[i] = samples[j] >> 2;
energy += (lpc_data[i] * lpc_data[i]) >> 4;
}
}
if (i < NBLOCKS * BLOCKSIZE)
memset(&lpc_data[i], 0, (NBLOCKS * BLOCKSIZE - i) * sizeof(*lpc_data));
energy = ff_energy_tab[quantize(ff_t_sqrt(energy >> 5) >> 10, ff_energy_tab,
32)];
ff_lpc_calc_coefs(&ractx->lpc_ctx, lpc_data, NBLOCKS * BLOCKSIZE, LPC_ORDER,
LPC_ORDER, 16, lpc_coefs, shift, FF_LPC_TYPE_LEVINSON,
0, ORDER_METHOD_EST, 12, 0);
for (i = 0; i < LPC_ORDER; i++)
block_coefs[NBLOCKS - 1][i] = -(lpc_coefs[LPC_ORDER - 1][i] <<
(12 - shift[LPC_ORDER - 1]));
/**
* TODO: apply perceptual weighting of the input speech through bandwidth
* expansion of the LPC filter.
*/
if (ff_eval_refl(lpc_refl, block_coefs[NBLOCKS - 1], avctx)) {
/**
* The filter is unstable: use the coefficients of the previous frame.
*/
ff_int_to_int16(block_coefs[NBLOCKS - 1], ractx->lpc_coef[1]);
if (ff_eval_refl(lpc_refl, block_coefs[NBLOCKS - 1], avctx)) {
/* the filter is still unstable. set reflection coeffs to zero. */
memset(lpc_refl, 0, sizeof(lpc_refl));
}
}
init_put_bits(&pb, avpkt->data, avpkt->size);
for (i = 0; i < LPC_ORDER; i++) {
idx = quantize(lpc_refl[i], ff_lpc_refl_cb[i], sizes[i]);
put_bits(&pb, bit_sizes[i], idx);
lpc_refl[i] = ff_lpc_refl_cb[i][idx];
}
ractx->lpc_refl_rms[0] = ff_rms(lpc_refl);
ff_eval_coefs(ractx->lpc_coef[0], lpc_refl);
refl_rms[0] = ff_interp(ractx, block_coefs[0], 1, 1, ractx->old_energy);
refl_rms[1] = ff_interp(ractx, block_coefs[1], 2,
energy <= ractx->old_energy,
ff_t_sqrt(energy * ractx->old_energy) >> 12);
refl_rms[2] = ff_interp(ractx, block_coefs[2], 3, 0, energy);
refl_rms[3] = ff_rescale_rms(ractx->lpc_refl_rms[0], energy);
ff_int_to_int16(block_coefs[NBLOCKS - 1], ractx->lpc_coef[0]);
put_bits(&pb, 5, quantize(energy, ff_energy_tab, 32));
for (i = 0; i < NBLOCKS; i++)
ra144_encode_subblock(ractx, ractx->curr_block + i * BLOCKSIZE,
block_coefs[i], refl_rms[i], &pb);
flush_put_bits(&pb);
ractx->old_energy = energy;
ractx->lpc_refl_rms[1] = ractx->lpc_refl_rms[0];
FFSWAP(unsigned int *, ractx->lpc_coef[0], ractx->lpc_coef[1]);
/* copy input samples to current block for processing in next call */
i = 0;
if (frame) {
for (; i < frame->nb_samples; i++)
ractx->curr_block[i] = samples[i] >> 2;
if ((ret = ff_af_queue_add(&ractx->afq, frame)) < 0)
return ret;
} else
ractx->last_frame = 1;
memset(&ractx->curr_block[i], 0,
(NBLOCKS * BLOCKSIZE - i) * sizeof(*ractx->curr_block));
/* Get the next frame pts/duration */
ff_af_queue_remove(&ractx->afq, avctx->frame_size, &avpkt->pts,
&avpkt->duration);
avpkt->size = FRAMESIZE;
*got_packet_ptr = 1;
return 0;
}
AVCodec ff_ra_144_encoder = {
.name = "real_144",
.long_name = NULL_IF_CONFIG_SMALL("RealAudio 1.0 (14.4K)"),
.type = AVMEDIA_TYPE_AUDIO,
.id = AV_CODEC_ID_RA_144,
.priv_data_size = sizeof(RA144Context),
.init = ra144_encode_init,
.encode2 = ra144_encode_frame,
.close = ra144_encode_close,
.capabilities = CODEC_CAP_DELAY | CODEC_CAP_SMALL_LAST_FRAME,
.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE },
};