Commit b2fe6756 authored by Justin Ruggles's avatar Justin Ruggles

lavr: add option for dithering during sample format conversion to s16

parent 58236862
...@@ -8,6 +8,7 @@ OBJS = audio_convert.o \ ...@@ -8,6 +8,7 @@ OBJS = audio_convert.o \
audio_data.o \ audio_data.o \
audio_mix.o \ audio_mix.o \
audio_mix_matrix.o \ audio_mix_matrix.o \
dither.o \
options.o \ options.o \
resample.o \ resample.o \
utils.o \ utils.o \
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "libavutil/samplefmt.h" #include "libavutil/samplefmt.h"
#include "audio_convert.h" #include "audio_convert.h"
#include "audio_data.h" #include "audio_data.h"
#include "dither.h"
#include "internal.h"
enum ConvFuncType { enum ConvFuncType {
CONV_FUNC_TYPE_FLAT, CONV_FUNC_TYPE_FLAT,
...@@ -46,6 +48,7 @@ typedef void (conv_func_deinterleave)(uint8_t **out, const uint8_t *in, int len, ...@@ -46,6 +48,7 @@ typedef void (conv_func_deinterleave)(uint8_t **out, const uint8_t *in, int len,
struct AudioConvert { struct AudioConvert {
AVAudioResampleContext *avr; AVAudioResampleContext *avr;
DitherContext *dc;
enum AVSampleFormat in_fmt; enum AVSampleFormat in_fmt;
enum AVSampleFormat out_fmt; enum AVSampleFormat out_fmt;
int channels; int channels;
...@@ -246,10 +249,18 @@ static void set_generic_function(AudioConvert *ac) ...@@ -246,10 +249,18 @@ static void set_generic_function(AudioConvert *ac)
SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL) 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, AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
enum AVSampleFormat out_fmt, enum AVSampleFormat out_fmt,
enum AVSampleFormat in_fmt, enum AVSampleFormat in_fmt,
int channels) int channels, int sample_rate)
{ {
AudioConvert *ac; AudioConvert *ac;
int in_planar, out_planar; int in_planar, out_planar;
...@@ -263,6 +274,17 @@ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, ...@@ -263,6 +274,17 @@ AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
ac->in_fmt = in_fmt; ac->in_fmt = in_fmt;
ac->channels = channels; 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); in_planar = av_sample_fmt_is_planar(in_fmt);
out_planar = av_sample_fmt_is_planar(out_fmt); out_planar = av_sample_fmt_is_planar(out_fmt);
...@@ -289,6 +311,15 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) ...@@ -289,6 +311,15 @@ int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in)
int use_generic = 1; int use_generic = 1;
int len = in->nb_samples; 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 /* determine whether to use the optimized function based on pointer and
samples alignment in both the input and output */ samples alignment in both the input and output */
if (ac->has_optimized_func) { if (ac->has_optimized_func) {
......
...@@ -54,16 +54,26 @@ void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, ...@@ -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. * Allocate and initialize AudioConvert context for sample format conversion.
* *
* @param avr AVAudioResampleContext * @param avr AVAudioResampleContext
* @param out_fmt output sample format * @param out_fmt output sample format
* @param in_fmt input sample format * @param in_fmt input sample format
* @param channels number of channels * @param channels number of channels
* @return newly-allocated AudioConvert context * @param sample_rate sample rate (used for dithering)
* @return newly-allocated AudioConvert context
*/ */
AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
enum AVSampleFormat out_fmt, enum AVSampleFormat out_fmt,
enum AVSampleFormat in_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. * Convert audio data from one sample format to another.
......
...@@ -119,6 +119,15 @@ enum AVResampleFilterType { ...@@ -119,6 +119,15 @@ enum AVResampleFilterType {
AV_RESAMPLE_FILTER_TYPE_KAISER, /**< Kaiser Windowed Sinc */ 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. * Return the LIBAVRESAMPLE_VERSION_INT constant.
*/ */
......
This diff is collapsed.
/*
* 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 */
...@@ -53,6 +53,7 @@ struct AVAudioResampleContext { ...@@ -53,6 +53,7 @@ struct AVAudioResampleContext {
double cutoff; /**< resampling cutoff frequency. 1.0 corresponds to half the output sample rate */ double cutoff; /**< resampling cutoff frequency. 1.0 corresponds to half the output sample rate */
enum AVResampleFilterType filter_type; /**< resampling filter type */ enum AVResampleFilterType filter_type; /**< resampling filter type */
int kaiser_beta; /**< beta value for Kaiser window (only applicable if filter_type == AV_FILTER_TYPE_KAISER) */ 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 in_channels; /**< number of input channels */
int out_channels; /**< number of output channels */ int out_channels; /**< number of output channels */
......
...@@ -63,6 +63,12 @@ static const AVOption options[] = { ...@@ -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" }, { "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", "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 }, { "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 }, { NULL },
}; };
......
...@@ -142,7 +142,8 @@ int avresample_open(AVAudioResampleContext *avr) ...@@ -142,7 +142,8 @@ int avresample_open(AVAudioResampleContext *avr)
/* setup contexts */ /* setup contexts */
if (avr->in_convert_needed) { if (avr->in_convert_needed) {
avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, 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) { if (!avr->ac_in) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);
goto error; goto error;
...@@ -155,7 +156,8 @@ int avresample_open(AVAudioResampleContext *avr) ...@@ -155,7 +156,8 @@ int avresample_open(AVAudioResampleContext *avr)
else else
src_fmt = avr->in_sample_fmt; src_fmt = avr->in_sample_fmt;
avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_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) { if (!avr->ac_out) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);
goto error; goto error;
...@@ -190,8 +192,8 @@ void avresample_close(AVAudioResampleContext *avr) ...@@ -190,8 +192,8 @@ void avresample_close(AVAudioResampleContext *avr)
ff_audio_data_free(&avr->out_buffer); ff_audio_data_free(&avr->out_buffer);
av_audio_fifo_free(avr->out_fifo); av_audio_fifo_free(avr->out_fifo);
avr->out_fifo = NULL; avr->out_fifo = NULL;
av_freep(&avr->ac_in); ff_audio_convert_free(&avr->ac_in);
av_freep(&avr->ac_out); ff_audio_convert_free(&avr->ac_out);
ff_audio_resample_free(&avr->resample); ff_audio_resample_free(&avr->resample);
ff_audio_mix_free(&avr->am); ff_audio_mix_free(&avr->am);
av_freep(&avr->mix_matrix); av_freep(&avr->mix_matrix);
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#define LIBAVRESAMPLE_VERSION_MAJOR 1 #define LIBAVRESAMPLE_VERSION_MAJOR 1
#define LIBAVRESAMPLE_VERSION_MINOR 0 #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, \ #define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \
LIBAVRESAMPLE_VERSION_MINOR, \ LIBAVRESAMPLE_VERSION_MINOR, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment