diff options
author | Justin Ruggles <justin.ruggles@gmail.com> | 2012-10-31 15:40:12 -0400 |
---|---|---|
committer | Justin Ruggles <justin.ruggles@gmail.com> | 2012-12-19 18:52:54 -0500 |
commit | b2fe6756e34d1316d0fa799e8a5ace993059c407 (patch) | |
tree | 0fc8dea25140a8af90cdfb96af5b5d8f97560ab7 /libavresample | |
parent | 582368626188c070d4300913c6da5efa4c24cfb2 (diff) | |
download | ffmpeg-streaming-b2fe6756e34d1316d0fa799e8a5ace993059c407.zip ffmpeg-streaming-b2fe6756e34d1316d0fa799e8a5ace993059c407.tar.gz |
lavr: add option for dithering during sample format conversion to s16
Diffstat (limited to 'libavresample')
-rw-r--r-- | libavresample/Makefile | 1 | ||||
-rw-r--r-- | libavresample/audio_convert.c | 33 | ||||
-rw-r--r-- | libavresample/audio_convert.h | 22 | ||||
-rw-r--r-- | libavresample/avresample.h | 9 | ||||
-rw-r--r-- | libavresample/dither.c | 423 | ||||
-rw-r--r-- | libavresample/dither.h | 88 | ||||
-rw-r--r-- | libavresample/internal.h | 1 | ||||
-rw-r--r-- | libavresample/options.c | 6 | ||||
-rw-r--r-- | libavresample/utils.c | 10 | ||||
-rw-r--r-- | libavresample/version.h | 2 |
10 files changed, 583 insertions, 12 deletions
diff --git a/libavresample/Makefile b/libavresample/Makefile index c0c20a9..6805280 100644 --- a/libavresample/Makefile +++ b/libavresample/Makefile @@ -8,6 +8,7 @@ OBJS = audio_convert.o \ audio_data.o \ audio_mix.o \ audio_mix_matrix.o \ + dither.o \ options.o \ resample.o \ utils.o \ diff --git a/libavresample/audio_convert.c b/libavresample/audio_convert.c index dcf8a39..eb3bc1f 100644 --- a/libavresample/audio_convert.c +++ b/libavresample/audio_convert.c @@ -29,6 +29,8 @@ #include "libavutil/samplefmt.h" #include "audio_convert.h" #include "audio_data.h" +#include "dither.h" +#include "internal.h" enum ConvFuncType { CONV_FUNC_TYPE_FLAT, @@ -46,6 +48,7 @@ typedef void (conv_func_deinterleave)(uint8_t **out, const uint8_t *in, int len, struct AudioConvert { AVAudioResampleContext *avr; + DitherContext *dc; enum AVSampleFormat in_fmt; enum AVSampleFormat out_fmt; int channels; @@ -246,10 +249,18 @@ static void set_generic_function(AudioConvert *ac) SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL) } +void ff_audio_convert_free(AudioConvert **ac) +{ + if (!*ac) + return; + ff_dither_free(&(*ac)->dc); + av_freep(ac); +} + AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, - int channels) + int channels, int sample_rate) { AudioConvert *ac; int in_planar, out_planar; @@ -263,6 +274,17 @@ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, ac->in_fmt = in_fmt; ac->channels = channels; + if (avr->dither_method != AV_RESAMPLE_DITHER_NONE && + av_get_packed_sample_fmt(out_fmt) == AV_SAMPLE_FMT_S16 && + av_get_bytes_per_sample(in_fmt) > 2) { + ac->dc = ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate); + if (!ac->dc) { + av_free(ac); + return NULL; + } + return ac; + } + in_planar = av_sample_fmt_is_planar(in_fmt); out_planar = av_sample_fmt_is_planar(out_fmt); @@ -289,6 +311,15 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) int use_generic = 1; int len = in->nb_samples; + if (ac->dc) { + /* dithered conversion */ + av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (dithered)\n", + len, av_get_sample_fmt_name(ac->in_fmt), + av_get_sample_fmt_name(ac->out_fmt)); + + return ff_convert_dither(ac->dc, out, in); + } + /* determine whether to use the optimized function based on pointer and samples alignment in both the input and output */ if (ac->has_optimized_func) { diff --git a/libavresample/audio_convert.h b/libavresample/audio_convert.h index bc27223..b8808f1 100644 --- a/libavresample/audio_convert.h +++ b/libavresample/audio_convert.h @@ -54,16 +54,26 @@ void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, /** * Allocate and initialize AudioConvert context for sample format conversion. * - * @param avr AVAudioResampleContext - * @param out_fmt output sample format - * @param in_fmt input sample format - * @param channels number of channels - * @return newly-allocated AudioConvert context + * @param avr AVAudioResampleContext + * @param out_fmt output sample format + * @param in_fmt input sample format + * @param channels number of channels + * @param sample_rate sample rate (used for dithering) + * @return newly-allocated AudioConvert context */ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, - int channels); + int channels, int sample_rate); + +/** + * Free AudioConvert. + * + * The AudioConvert must have been previously allocated with ff_audio_convert_alloc(). + * + * @param ac AudioConvert struct + */ +void ff_audio_convert_free(AudioConvert **ac); /** * Convert audio data from one sample format to another. diff --git a/libavresample/avresample.h b/libavresample/avresample.h index 4841d26..34998aa 100644 --- a/libavresample/avresample.h +++ b/libavresample/avresample.h @@ -119,6 +119,15 @@ enum AVResampleFilterType { AV_RESAMPLE_FILTER_TYPE_KAISER, /**< Kaiser Windowed Sinc */ }; +enum AVResampleDitherMethod { + AV_RESAMPLE_DITHER_NONE, /**< Do not use dithering */ + AV_RESAMPLE_DITHER_RECTANGULAR, /**< Rectangular Dither */ + AV_RESAMPLE_DITHER_TRIANGULAR, /**< Triangular Dither*/ + AV_RESAMPLE_DITHER_TRIANGULAR_HP, /**< Triangular Dither with High Pass */ + AV_RESAMPLE_DITHER_TRIANGULAR_NS, /**< Triangular Dither with Noise Shaping */ + AV_RESAMPLE_DITHER_NB, /**< Number of dither types. Not part of ABI. */ +}; + /** * Return the LIBAVRESAMPLE_VERSION_INT constant. */ diff --git a/libavresample/dither.c b/libavresample/dither.c new file mode 100644 index 0000000..9c1e1c1 --- /dev/null +++ b/libavresample/dither.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * Triangular with Noise Shaping is based on opusfile. + * Copyright (c) 1994-2012 by the Xiph.Org Foundation and contributors + * + * 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 + * Dithered Audio Sample Quantization + * + * Converts from dbl, flt, or s32 to s16 using dithering. + */ + +#include <math.h> +#include <stdint.h> + +#include "libavutil/common.h" +#include "libavutil/lfg.h" +#include "libavutil/mem.h" +#include "libavutil/samplefmt.h" +#include "audio_convert.h" +#include "dither.h" +#include "internal.h" + +typedef struct DitherState { + int mute; + unsigned int seed; + AVLFG lfg; + float *noise_buf; + int noise_buf_size; + int noise_buf_ptr; + float dither_a[4]; + float dither_b[4]; +} DitherState; + +struct DitherContext { + DitherDSPContext ddsp; + enum AVResampleDitherMethod method; + + int mute_dither_threshold; // threshold for disabling dither + int mute_reset_threshold; // threshold for resetting noise shaping + const float *ns_coef_b; // noise shaping coeffs + const float *ns_coef_a; // noise shaping coeffs + + int channels; + DitherState *state; // dither states for each channel + + AudioData *flt_data; // input data in fltp + AudioData *s16_data; // dithered output in s16p + AudioConvert *ac_in; // converter for input to fltp + AudioConvert *ac_out; // converter for s16p to s16 (if needed) + + void (*quantize)(int16_t *dst, const float *src, float *dither, int len); + int samples_align; +}; + +/* mute threshold, in seconds */ +#define MUTE_THRESHOLD_SEC 0.000333 + +/* scale factor for 16-bit output. + The signal is attenuated slightly to avoid clipping */ +#define S16_SCALE 32753.0f + +/* scale to convert lfg from INT_MIN/INT_MAX to -0.5/0.5 */ +#define LFG_SCALE (1.0f / (2.0f * INT32_MAX)) + +/* noise shaping coefficients */ + +static const float ns_48_coef_b[4] = { + 2.2374f, -0.7339f, -0.1251f, -0.6033f +}; + +static const float ns_48_coef_a[4] = { + 0.9030f, 0.0116f, -0.5853f, -0.2571f +}; + +static const float ns_44_coef_b[4] = { + 2.2061f, -0.4707f, -0.2534f, -0.6213f +}; + +static const float ns_44_coef_a[4] = { + 1.0587f, 0.0676f, -0.6054f, -0.2738f +}; + +static void dither_int_to_float_rectangular_c(float *dst, int *src, int len) +{ + int i; + for (i = 0; i < len; i++) + dst[i] = src[i] * LFG_SCALE; +} + +static void dither_int_to_float_triangular_c(float *dst, int *src0, int len) +{ + int i; + int *src1 = src0 + len; + + for (i = 0; i < len; i++) { + float r = src0[i] * LFG_SCALE; + r += src1[i] * LFG_SCALE; + dst[i] = r; + } +} + +static void quantize_c(int16_t *dst, const float *src, float *dither, int len) +{ + int i; + for (i = 0; i < len; i++) + dst[i] = av_clip_int16(lrintf(src[i] * S16_SCALE + dither[i])); +} + +#define SQRT_1_6 0.40824829046386301723f + +static void dither_highpass_filter(float *src, int len) +{ + int i; + + /* filter is from libswresample in FFmpeg */ + for (i = 0; i < len - 2; i++) + src[i] = (-src[i] + 2 * src[i + 1] - src[i + 2]) * SQRT_1_6; +} + +static int generate_dither_noise(DitherContext *c, DitherState *state, + int min_samples) +{ + int i; + int nb_samples = FFALIGN(min_samples, 16) + 16; + int buf_samples = nb_samples * + (c->method == AV_RESAMPLE_DITHER_RECTANGULAR ? 1 : 2); + unsigned int *noise_buf_ui; + + av_freep(&state->noise_buf); + state->noise_buf_size = state->noise_buf_ptr = 0; + + state->noise_buf = av_malloc(buf_samples * sizeof(*state->noise_buf)); + if (!state->noise_buf) + return AVERROR(ENOMEM); + state->noise_buf_size = FFALIGN(min_samples, 16); + noise_buf_ui = (unsigned int *)state->noise_buf; + + av_lfg_init(&state->lfg, state->seed); + for (i = 0; i < buf_samples; i++) + noise_buf_ui[i] = av_lfg_get(&state->lfg); + + c->ddsp.dither_int_to_float(state->noise_buf, noise_buf_ui, nb_samples); + + if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_HP) + dither_highpass_filter(state->noise_buf, nb_samples); + + return 0; +} + +static void quantize_triangular_ns(DitherContext *c, DitherState *state, + int16_t *dst, const float *src, + int nb_samples) +{ + int i, j; + float *dither = &state->noise_buf[state->noise_buf_ptr]; + + if (state->mute > c->mute_reset_threshold) + memset(state->dither_a, 0, sizeof(state->dither_a)); + + for (i = 0; i < nb_samples; i++) { + float err = 0; + float sample = src[i] * S16_SCALE; + + for (j = 0; j < 4; j++) { + err += c->ns_coef_b[j] * state->dither_b[j] - + c->ns_coef_a[j] * state->dither_a[j]; + } + for (j = 3; j > 0; j--) { + state->dither_a[j] = state->dither_a[j - 1]; + state->dither_b[j] = state->dither_b[j - 1]; + } + state->dither_a[0] = err; + sample -= err; + + if (state->mute > c->mute_dither_threshold) { + dst[i] = av_clip_int16(lrintf(sample)); + state->dither_b[0] = 0; + } else { + dst[i] = av_clip_int16(lrintf(sample + dither[i])); + state->dither_b[0] = av_clipf(dst[i] - sample, -1.5f, 1.5f); + } + + state->mute++; + if (src[i]) + state->mute = 0; + } +} + +static int convert_samples(DitherContext *c, int16_t **dst, float * const *src, + int channels, int nb_samples) +{ + int ch, ret; + int aligned_samples = FFALIGN(nb_samples, 16); + + for (ch = 0; ch < channels; ch++) { + DitherState *state = &c->state[ch]; + + if (state->noise_buf_size < aligned_samples) { + ret = generate_dither_noise(c, state, nb_samples); + if (ret < 0) + return ret; + } else if (state->noise_buf_size - state->noise_buf_ptr < aligned_samples) { + state->noise_buf_ptr = 0; + } + + if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_NS) { + quantize_triangular_ns(c, state, dst[ch], src[ch], nb_samples); + } else { + c->quantize(dst[ch], src[ch], + &state->noise_buf[state->noise_buf_ptr], + FFALIGN(nb_samples, c->samples_align)); + } + + state->noise_buf_ptr += aligned_samples; + } + + return 0; +} + +int ff_convert_dither(DitherContext *c, AudioData *dst, AudioData *src) +{ + int ret; + AudioData *flt_data; + + /* output directly to dst if it is planar */ + if (dst->sample_fmt == AV_SAMPLE_FMT_S16P) + c->s16_data = dst; + else { + /* make sure s16_data is large enough for the output */ + ret = ff_audio_data_realloc(c->s16_data, src->nb_samples); + if (ret < 0) + return ret; + } + + if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) { + /* make sure flt_data is large enough for the input */ + ret = ff_audio_data_realloc(c->flt_data, src->nb_samples); + if (ret < 0) + return ret; + flt_data = c->flt_data; + + /* convert input samples to fltp and scale to s16 range */ + ret = ff_audio_convert(c->ac_in, flt_data, src); + if (ret < 0) + return ret; + } else { + flt_data = src; + } + + /* check alignment and padding constraints */ + if (c->method != AV_RESAMPLE_DITHER_TRIANGULAR_NS) { + int ptr_align = FFMIN(flt_data->ptr_align, c->s16_data->ptr_align); + int samples_align = FFMIN(flt_data->samples_align, c->s16_data->samples_align); + int aligned_len = FFALIGN(src->nb_samples, c->ddsp.samples_align); + + if (!(ptr_align % c->ddsp.ptr_align) && samples_align >= aligned_len) { + c->quantize = c->ddsp.quantize; + c->samples_align = c->ddsp.samples_align; + } else { + c->quantize = quantize_c; + c->samples_align = 1; + } + } + + ret = convert_samples(c, (int16_t **)c->s16_data->data, + (float * const *)flt_data->data, src->channels, + src->nb_samples); + if (ret < 0) + return ret; + + c->s16_data->nb_samples = src->nb_samples; + + /* interleave output to dst if needed */ + if (dst->sample_fmt == AV_SAMPLE_FMT_S16) { + ret = ff_audio_convert(c->ac_out, dst, c->s16_data); + if (ret < 0) + return ret; + } else + c->s16_data = NULL; + + return 0; +} + +void ff_dither_free(DitherContext **cp) +{ + DitherContext *c = *cp; + int ch; + + if (!c) + return; + ff_audio_data_free(&c->flt_data); + ff_audio_data_free(&c->s16_data); + ff_audio_convert_free(&c->ac_in); + ff_audio_convert_free(&c->ac_out); + for (ch = 0; ch < c->channels; ch++) + av_free(c->state[ch].noise_buf); + av_free(c->state); + av_freep(cp); +} + +static void dither_init(DitherDSPContext *ddsp, + enum AVResampleDitherMethod method) +{ + ddsp->quantize = quantize_c; + ddsp->ptr_align = 1; + ddsp->samples_align = 1; + + if (method == AV_RESAMPLE_DITHER_RECTANGULAR) + ddsp->dither_int_to_float = dither_int_to_float_rectangular_c; + else + ddsp->dither_int_to_float = dither_int_to_float_triangular_c; +} + +DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int sample_rate) +{ + AVLFG seed_gen; + DitherContext *c; + int ch; + + if (av_get_packed_sample_fmt(out_fmt) != AV_SAMPLE_FMT_S16 || + av_get_bytes_per_sample(in_fmt) <= 2) { + av_log(avr, AV_LOG_ERROR, "dithering %s to %s is not supported\n", + av_get_sample_fmt_name(in_fmt), av_get_sample_fmt_name(out_fmt)); + return NULL; + } + + c = av_mallocz(sizeof(*c)); + if (!c) + return NULL; + + if (avr->dither_method == AV_RESAMPLE_DITHER_TRIANGULAR_NS && + sample_rate != 48000 && sample_rate != 44100) { + av_log(avr, AV_LOG_WARNING, "sample rate must be 48000 or 44100 Hz " + "for triangular_ns dither. using triangular_hp instead.\n"); + avr->dither_method = AV_RESAMPLE_DITHER_TRIANGULAR_HP; + } + c->method = avr->dither_method; + dither_init(&c->ddsp, c->method); + + if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_NS) { + if (sample_rate == 48000) { + c->ns_coef_b = ns_48_coef_b; + c->ns_coef_a = ns_48_coef_a; + } else { + c->ns_coef_b = ns_44_coef_b; + c->ns_coef_a = ns_44_coef_a; + } + } + + /* Either s16 or s16p output format is allowed, but s16p is used + internally, so we need to use a temp buffer and interleave if the output + format is s16 */ + if (out_fmt != AV_SAMPLE_FMT_S16P) { + c->s16_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_S16P, + "dither s16 buffer"); + if (!c->s16_data) + goto fail; + + c->ac_out = ff_audio_convert_alloc(avr, out_fmt, AV_SAMPLE_FMT_S16P, + channels, sample_rate); + if (!c->ac_out) + goto fail; + } + + if (in_fmt != AV_SAMPLE_FMT_FLTP) { + c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP, + "dither flt buffer"); + if (!c->flt_data) + goto fail; + + c->ac_in = ff_audio_convert_alloc(avr, AV_SAMPLE_FMT_FLTP, in_fmt, + channels, sample_rate); + if (!c->ac_in) + goto fail; + } + + c->state = av_mallocz(channels * sizeof(*c->state)); + if (!c->state) + goto fail; + c->channels = channels; + + /* calculate thresholds for turning off dithering during periods of + silence to avoid replacing digital silence with quiet dither noise */ + c->mute_dither_threshold = lrintf(sample_rate * MUTE_THRESHOLD_SEC); + c->mute_reset_threshold = c->mute_dither_threshold * 4; + + /* initialize dither states */ + av_lfg_init(&seed_gen, 0xC0FFEE); + for (ch = 0; ch < channels; ch++) { + DitherState *state = &c->state[ch]; + state->mute = c->mute_reset_threshold + 1; + state->seed = av_lfg_get(&seed_gen); + generate_dither_noise(c, state, FFMAX(32768, sample_rate / 2)); + } + + return c; + +fail: + ff_dither_free(&c); + return NULL; +} diff --git a/libavresample/dither.h b/libavresample/dither.h new file mode 100644 index 0000000..8b30dd2 --- /dev/null +++ b/libavresample/dither.h @@ -0,0 +1,88 @@ +/* + * 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 + */ + +#ifndef AVRESAMPLE_DITHER_H +#define AVRESAMPLE_DITHER_H + +#include "avresample.h" +#include "audio_data.h" + +typedef struct DitherContext DitherContext; + +typedef struct DitherDSPContext { + /** + * Convert samples from flt to s16 with added dither noise. + * + * @param dst destination float array, range -0.5 to 0.5 + * @param src source int array, range INT_MIN to INT_MAX. + * @param dither float dither noise array + * @param len number of samples + */ + void (*quantize)(int16_t *dst, const float *src, float *dither, int len); + + int ptr_align; ///< src and dst constraits for quantize() + int samples_align; ///< len constraits for quantize() + + /** + * Convert dither noise from int to float with triangular distribution. + * + * @param dst destination float array, range -0.5 to 0.5 + * constraints: 32-byte aligned + * @param src0 source int array, range INT_MIN to INT_MAX. + * the array size is len * 2 + * constraints: 32-byte aligned + * @param len number of output noise samples + * constraints: multiple of 16 + */ + void (*dither_int_to_float)(float *dst, int *src0, int len); +} DitherDSPContext; + +/** + * Allocate and initialize a DitherContext. + * + * The parameters in the AVAudioResampleContext are used to initialize the + * DitherContext. + * + * @param avr AVAudioResampleContext + * @return newly-allocated DitherContext + */ +DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int sample_rate); + +/** + * Free a DitherContext. + * + * @param c DitherContext + */ +void ff_dither_free(DitherContext **c); + +/** + * Convert audio sample format with dithering. + * + * @param c DitherContext + * @param dst destination audio data + * @param src source audio data + * @return 0 if ok, negative AVERROR code on failure + */ +int ff_convert_dither(DitherContext *c, AudioData *dst, AudioData *src); + +#endif /* AVRESAMPLE_DITHER_H */ diff --git a/libavresample/internal.h b/libavresample/internal.h index 3fd33fe..2e139ab 100644 --- a/libavresample/internal.h +++ b/libavresample/internal.h @@ -53,6 +53,7 @@ struct AVAudioResampleContext { double cutoff; /**< resampling cutoff frequency. 1.0 corresponds to half the output sample rate */ enum AVResampleFilterType filter_type; /**< resampling filter type */ int kaiser_beta; /**< beta value for Kaiser window (only applicable if filter_type == AV_FILTER_TYPE_KAISER) */ + enum AVResampleDitherMethod dither_method; /**< dither method */ int in_channels; /**< number of input channels */ int out_channels; /**< number of output channels */ diff --git a/libavresample/options.c b/libavresample/options.c index 824f5e3..68548f0 100644 --- a/libavresample/options.c +++ b/libavresample/options.c @@ -63,6 +63,12 @@ static const AVOption options[] = { { "blackman_nuttall", "Blackman Nuttall Windowed Sinc", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL }, INT_MIN, INT_MAX, PARAM, "filter_type" }, { "kaiser", "Kaiser Windowed Sinc", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_FILTER_TYPE_KAISER }, INT_MIN, INT_MAX, PARAM, "filter_type" }, { "kaiser_beta", "Kaiser Window Beta", OFFSET(kaiser_beta), AV_OPT_TYPE_INT, { .i64 = 9 }, 2, 16, PARAM }, + { "dither_method", "Dither Method", OFFSET(dither_method), AV_OPT_TYPE_INT, { .i64 = AV_RESAMPLE_DITHER_NONE }, 0, AV_RESAMPLE_DITHER_NB-1, PARAM, "dither_method"}, + {"none", "No Dithering", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_NONE }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"rectangular", "Rectangular Dither", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_RECTANGULAR }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"triangular", "Triangular Dither", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_TRIANGULAR }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"triangular_hp", "Triangular Dither With High Pass", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_TRIANGULAR_HP }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"triangular_ns", "Triangular Dither With Noise Shaping", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_TRIANGULAR_NS }, INT_MIN, INT_MAX, PARAM, "dither_method"}, { NULL }, }; diff --git a/libavresample/utils.c b/libavresample/utils.c index fe2e1c2..ed7f470 100644 --- a/libavresample/utils.c +++ b/libavresample/utils.c @@ -142,7 +142,8 @@ int avresample_open(AVAudioResampleContext *avr) /* setup contexts */ if (avr->in_convert_needed) { avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, - avr->in_sample_fmt, avr->in_channels); + avr->in_sample_fmt, avr->in_channels, + avr->in_sample_rate); if (!avr->ac_in) { ret = AVERROR(ENOMEM); goto error; @@ -155,7 +156,8 @@ int avresample_open(AVAudioResampleContext *avr) else src_fmt = avr->in_sample_fmt; avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, - avr->out_channels); + avr->out_channels, + avr->out_sample_rate); if (!avr->ac_out) { ret = AVERROR(ENOMEM); goto error; @@ -190,8 +192,8 @@ void avresample_close(AVAudioResampleContext *avr) ff_audio_data_free(&avr->out_buffer); av_audio_fifo_free(avr->out_fifo); avr->out_fifo = NULL; - av_freep(&avr->ac_in); - av_freep(&avr->ac_out); + ff_audio_convert_free(&avr->ac_in); + ff_audio_convert_free(&avr->ac_out); ff_audio_resample_free(&avr->resample); ff_audio_mix_free(&avr->am); av_freep(&avr->mix_matrix); diff --git a/libavresample/version.h b/libavresample/version.h index 834c942..ebcd07f 100644 --- a/libavresample/version.h +++ b/libavresample/version.h @@ -21,7 +21,7 @@ #define LIBAVRESAMPLE_VERSION_MAJOR 1 #define LIBAVRESAMPLE_VERSION_MINOR 0 -#define LIBAVRESAMPLE_VERSION_MICRO 0 +#define LIBAVRESAMPLE_VERSION_MICRO 1 #define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ LIBAVRESAMPLE_VERSION_MINOR, \ |