Newer
Older
/*
* Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
*
* 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
*/
#include "libavutil/libm.h"
#include "libavutil/log.h"
#include "internal.h"
#include "audio_data.h"
struct ResampleContext {
AVAudioResampleContext *avr;
AudioData *buffer;
Justin Ruggles
committed
uint8_t *filter_bank;
int filter_length;
int ideal_dst_incr;
int dst_incr;
int index;
int frac;
int src_incr;
int compensation_distance;
int phase_shift;
int phase_mask;
int linear;
enum AVResampleFilterType filter_type;
int kaiser_beta;
Justin Ruggles
committed
void (*set_filter)(void *filter, double *tab, int phase, int tap_count);
void (*resample_one)(struct ResampleContext *c, int no_filter, void *dst0,
int dst_index, const void *src0, int src_size,
int index, int frac);
Justin Ruggles
committed
/* double template */
#define CONFIG_RESAMPLE_DBL
#include "resample_template.c"
#undef CONFIG_RESAMPLE_DBL
/* float template */
#define CONFIG_RESAMPLE_FLT
#include "resample_template.c"
#undef CONFIG_RESAMPLE_FLT
/* s32 template */
#define CONFIG_RESAMPLE_S32
#include "resample_template.c"
#undef CONFIG_RESAMPLE_S32
/* s16 template */
#include "resample_template.c"
/* 0th order modified bessel function of the first kind. */
static double bessel(double x)
{
double v = 1;
double lastv = 0;
double t = 1;
int i;
x = x * x / 4;
for (i = 1; v != lastv; i++) {
lastv = v;
t *= x / (i * i);
v += t;
}
return v;
}
/* Build a polyphase filterbank. */
Justin Ruggles
committed
static int build_filter(ResampleContext *c)
Justin Ruggles
committed
double x, y, w, factor;
Justin Ruggles
committed
int tap_count = c->filter_length;
int phase_count = 1 << c->phase_shift;
const int center = (tap_count - 1) / 2;
tab = av_malloc(tap_count * sizeof(*tab));
if (!tab)
return AVERROR(ENOMEM);
/* if upsampling, only need to interpolate, no filter */
Justin Ruggles
committed
factor = FFMIN(c->factor, 1.0);
for (ph = 0; ph < phase_count; ph++) {
double norm = 0;
for (i = 0; i < tap_count; i++) {
x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
if (x == 0) y = 1.0;
else y = sin(x) / x;
Justin Ruggles
committed
switch (c->filter_type) {
case AV_RESAMPLE_FILTER_TYPE_CUBIC: {
const float d = -0.5; //first order derivative = -0.5
x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
if (x < 1.0) y = 1 - 3 * x*x + 2 * x*x*x + d * ( -x*x + x*x*x);
else y = d * (-4 + 8 * x - 5 * x*x + x*x*x);
break;
}
case AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL:
w = 2.0 * x / (factor * tap_count) + M_PI;
y *= 0.3635819 - 0.4891775 * cos( w) +
0.1365995 * cos(2 * w) -
0.0106411 * cos(3 * w);
break;
case AV_RESAMPLE_FILTER_TYPE_KAISER:
Justin Ruggles
committed
y *= bessel(c->kaiser_beta * sqrt(FFMAX(1 - w * w, 0)));
break;
}
tab[i] = y;
norm += y;
}
/* normalize so that an uniform color remains the same */
Justin Ruggles
committed
for (i = 0; i < tap_count; i++)
tab[i] = tab[i] / norm;
c->set_filter(c->filter_bank, tab, ph, tap_count);
}
av_free(tab);
return 0;
}
ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr)
{
ResampleContext *c;
int out_rate = avr->out_sample_rate;
int in_rate = avr->in_sample_rate;
double factor = FFMIN(out_rate * avr->cutoff / in_rate, 1.0);
int phase_count = 1 << avr->phase_shift;
Justin Ruggles
committed
int felem_size;
Justin Ruggles
committed
if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P &&
avr->internal_sample_fmt != AV_SAMPLE_FMT_S32P &&
avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP &&
avr->internal_sample_fmt != AV_SAMPLE_FMT_DBLP) {
av_log(avr, AV_LOG_ERROR, "Unsupported internal format for "
"resampling: %s\n",
av_get_sample_fmt_name(avr->internal_sample_fmt));
return NULL;
}
c = av_mallocz(sizeof(*c));
if (!c)
return NULL;
c->avr = avr;
c->phase_shift = avr->phase_shift;
c->phase_mask = phase_count - 1;
c->linear = avr->linear_interp;
c->factor = factor;
c->filter_length = FFMAX((int)ceil(avr->filter_size / factor), 1);
c->filter_type = avr->filter_type;
c->kaiser_beta = avr->kaiser_beta;
Justin Ruggles
committed
switch (avr->internal_sample_fmt) {
case AV_SAMPLE_FMT_DBLP:
c->resample_one = resample_one_dbl;
c->set_filter = set_filter_dbl;
break;
case AV_SAMPLE_FMT_FLTP:
c->resample_one = resample_one_flt;
c->set_filter = set_filter_flt;
break;
case AV_SAMPLE_FMT_S32P:
c->resample_one = resample_one_s32;
c->set_filter = set_filter_s32;
break;
case AV_SAMPLE_FMT_S16P:
c->resample_one = resample_one_s16;
c->set_filter = set_filter_s16;
break;
}
felem_size = av_get_bytes_per_sample(avr->internal_sample_fmt);
c->filter_bank = av_mallocz(c->filter_length * (phase_count + 1) * felem_size);
Justin Ruggles
committed
if (build_filter(c) < 0)
Justin Ruggles
committed
memcpy(&c->filter_bank[(c->filter_length * phase_count + 1) * felem_size],
c->filter_bank, (c->filter_length - 1) * felem_size);
memcpy(&c->filter_bank[c->filter_length * phase_count * felem_size],
&c->filter_bank[(c->filter_length - 1) * felem_size], felem_size);
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
c->compensation_distance = 0;
if (!av_reduce(&c->src_incr, &c->dst_incr, out_rate,
in_rate * (int64_t)phase_count, INT32_MAX / 2))
goto error;
c->ideal_dst_incr = c->dst_incr;
c->index = -phase_count * ((c->filter_length - 1) / 2);
c->frac = 0;
/* allocate internal buffer */
c->buffer = ff_audio_data_alloc(avr->resample_channels, 0,
avr->internal_sample_fmt,
"resample buffer");
if (!c->buffer)
goto error;
av_log(avr, AV_LOG_DEBUG, "resample: %s from %d Hz to %d Hz\n",
av_get_sample_fmt_name(avr->internal_sample_fmt),
avr->in_sample_rate, avr->out_sample_rate);
return c;
error:
ff_audio_data_free(&c->buffer);
av_free(c->filter_bank);
av_free(c);
return NULL;
}
void ff_audio_resample_free(ResampleContext **c)
{
if (!*c)
return;
ff_audio_data_free(&(*c)->buffer);
av_free((*c)->filter_bank);
av_freep(c);
}
int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta,
int compensation_distance)
{
ResampleContext *c;
AudioData *fifo_buf = NULL;
int ret = 0;
if (compensation_distance < 0)
return AVERROR(EINVAL);
if (!compensation_distance && sample_delta)
return AVERROR(EINVAL);
if (!avr->resample_needed) {
#if FF_API_RESAMPLE_CLOSE_OPEN
/* if resampling was not enabled previously, re-initialize the
AVAudioResampleContext and force resampling */
int restore_matrix = 0;
double matrix[AVRESAMPLE_MAX_CHANNELS * AVRESAMPLE_MAX_CHANNELS] = { 0 };
/* buffer any remaining samples in the output FIFO before closing */
fifo_samples = av_audio_fifo_size(avr->out_fifo);
if (fifo_samples > 0) {
fifo_buf = ff_audio_data_alloc(avr->out_channels, fifo_samples,
avr->out_sample_fmt, NULL);
if (!fifo_buf)
return AVERROR(EINVAL);
ret = ff_audio_data_read_from_fifo(avr->out_fifo, fifo_buf,
fifo_samples);
if (ret < 0)
goto reinit_fail;
}
/* save the channel mixing matrix */
if (avr->am) {
ret = avresample_get_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS);
if (ret < 0)
goto reinit_fail;
restore_matrix = 1;
}
/* close the AVAudioResampleContext */
avresample_close(avr);
avr->force_resampling = 1;
/* restore the channel mixing matrix */
if (restore_matrix) {
ret = avresample_set_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS);
if (ret < 0)
goto reinit_fail;
}
/* re-open the AVAudioResampleContext */
ret = avresample_open(avr);
if (ret < 0)
goto reinit_fail;
/* restore buffered samples to the output FIFO */
if (fifo_samples > 0) {
ret = ff_audio_data_add_to_fifo(avr->out_fifo, fifo_buf, 0,
fifo_samples);
if (ret < 0)
goto reinit_fail;
ff_audio_data_free(&fifo_buf);
}
#else
av_log(avr, AV_LOG_ERROR, "Unable to set resampling compensation\n");
return AVERROR(EINVAL);
#endif
}
c = avr->resample;
c->compensation_distance = compensation_distance;
if (compensation_distance) {
c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr *
(int64_t)sample_delta / compensation_distance;
} else {
c->dst_incr = c->ideal_dst_incr;
}
return 0;
reinit_fail:
ff_audio_data_free(&fifo_buf);
return ret;
}
Justin Ruggles
committed
static int resample(ResampleContext *c, void *dst, const void *src,
int *consumed, int src_size, int dst_size, int update_ctx)
{
Justin Ruggles
committed
int dst_index;
int index = c->index;
int frac = c->frac;
int dst_incr_frac = c->dst_incr % c->src_incr;
int dst_incr = c->dst_incr / c->src_incr;
int compensation_distance = c->compensation_distance;
if (!dst != !src)
return AVERROR(EINVAL);
if (compensation_distance == 0 && c->filter_length == 1 &&
c->phase_shift == 0) {
int64_t index2 = ((int64_t)index) << 32;
int64_t incr = (1LL << 32) * c->dst_incr / c->src_incr;
dst_size = FFMIN(dst_size,
(src_size-1-index) * (int64_t)c->src_incr /
c->dst_incr);
if (dst) {
for(dst_index = 0; dst_index < dst_size; dst_index++) {
Justin Ruggles
committed
c->resample_one(c, 1, dst, dst_index, src, 0, index2 >> 32, 0);
index2 += incr;
}
} else {
dst_index = dst_size;
}
index += dst_index * dst_incr;
index += (frac + dst_index * (int64_t)dst_incr_frac) / c->src_incr;
frac = (frac + dst_index * (int64_t)dst_incr_frac) % c->src_incr;
} else {
for (dst_index = 0; dst_index < dst_size; dst_index++) {
int sample_index = index >> c->phase_shift;
Justin Ruggles
committed
if (sample_index + c->filter_length > src_size ||
-sample_index >= src_size)
Justin Ruggles
committed
if (dst)
c->resample_one(c, 0, dst, dst_index, src, src_size, index, frac);
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
frac += dst_incr_frac;
index += dst_incr;
if (frac >= c->src_incr) {
frac -= c->src_incr;
index++;
}
if (dst_index + 1 == compensation_distance) {
compensation_distance = 0;
dst_incr_frac = c->ideal_dst_incr % c->src_incr;
dst_incr = c->ideal_dst_incr / c->src_incr;
}
}
}
if (consumed)
*consumed = FFMAX(index, 0) >> c->phase_shift;
if (update_ctx) {
if (index >= 0)
index &= c->phase_mask;
if (compensation_distance) {
compensation_distance -= dst_index;
if (compensation_distance <= 0)
return AVERROR_BUG;
}
c->frac = frac;
c->index = index;
c->dst_incr = dst_incr_frac + c->src_incr*dst_incr;
c->compensation_distance = compensation_distance;
}
return dst_index;
}
int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src)
int ch, in_samples, in_leftover, consumed = 0, out_samples = 0;
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
int ret = AVERROR(EINVAL);
in_samples = src ? src->nb_samples : 0;
in_leftover = c->buffer->nb_samples;
/* add input samples to the internal buffer */
if (src) {
ret = ff_audio_data_combine(c->buffer, in_leftover, src, 0, in_samples);
if (ret < 0)
return ret;
} else if (!in_leftover) {
/* no remaining samples to flush */
return 0;
} else {
/* TODO: pad buffer to flush completely */
}
/* calculate output size and reallocate output buffer if needed */
/* TODO: try to calculate this without the dummy resample() run */
if (!dst->read_only && dst->allow_realloc) {
out_samples = resample(c, NULL, NULL, NULL, c->buffer->nb_samples,
INT_MAX, 0);
ret = ff_audio_data_realloc(dst, out_samples);
if (ret < 0) {
av_log(c->avr, AV_LOG_ERROR, "error reallocating output\n");
return ret;
}
}
/* resample each channel plane */
for (ch = 0; ch < c->buffer->channels; ch++) {
Justin Ruggles
committed
out_samples = resample(c, (void *)dst->data[ch],
(const void *)c->buffer->data[ch], &consumed,
c->buffer->nb_samples, dst->allocated_samples,
ch + 1 == c->buffer->channels);
}
if (out_samples < 0) {
av_log(c->avr, AV_LOG_ERROR, "error during resampling\n");
return out_samples;
}
/* drain consumed samples from the internal buffer */
ff_audio_data_drain(c->buffer, consumed);
av_dlog(c->avr, "resampled %d in + %d leftover to %d out + %d leftover\n",
in_samples, in_leftover, out_samples, c->buffer->nb_samples);
dst->nb_samples = out_samples;
return 0;
}
int avresample_get_delay(AVAudioResampleContext *avr)
{
if (!avr->resample_needed || !avr->resample)
return 0;
return avr->resample->buffer->nb_samples;
}