---
 libavresample/Makefile          |    1 +
 libavresample/audio_convert.c   |   33 +++-
 libavresample/audio_convert.h   |   22 ++-
 libavresample/avresample.h      |    9 +
 libavresample/dither.c          |  475 +++++++++++++++++++++++++++++++++++++++
 libavresample/dither.h          |   71 ++++++
 libavresample/internal.h        |    1 +
 libavresample/options.c         |    6 +
 libavresample/utils.c           |   10 +-
 libavresample/x86/Makefile      |    2 +
 libavresample/x86/dither.asm    |   74 ++++++
 libavresample/x86/dither_init.c |   67 ++++++
 12 files changed, 760 insertions(+), 11 deletions(-)
 create mode 100644 libavresample/dither.c
 create mode 100644 libavresample/dither.h
 create mode 100644 libavresample/x86/dither.asm
 create mode 100644 libavresample/x86/dither_init.c

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 affeeeb..fc7f138 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..0000098
--- /dev/null
+++ b/libavresample/dither.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <[email protected]>
+ *
+ * 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/float_dsp.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;
+    AVLFG lfg;
+    float dither_a[4];
+    float dither_b[4];
+} DitherState;
+
+struct DitherContext {
+    AVFloatDSPContext fdsp;
+    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
+
+    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_simple)(int16_t *dst, float *src, int *dither, int len);
+    int (*quantize)(DitherContext *c, int16_t **dst, float * const *src,
+                    int channels, int nb_samples);
+};
+
+/* mute threshold, in seconds */
+#define MUTE_THRESHOLD_SEC 0.000333
+
+/* 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 quantize_simple_rectangular_c(int16_t *dst, float *src, int 
*dither,
+                                          int len)
+{
+    int i;
+    for (i = 0; i < len; i++)
+        dst[i] = av_clip_int16(lrintf(src[i] + dither[i] / (2.0f * 
INT32_MAX)));
+}
+
+static void quantize_simple_triangular_c(int16_t *dst, float *src, int 
*dither0,
+                                         int len)
+{
+    int i;
+    int *dither1 = dither0 + len;
+
+    for (i = 0; i < len; i++) {
+        float r = dither0[i] / (2.0f * INT32_MAX);
+        r      += dither1[i] / (2.0f * INT32_MAX);
+        dst[i] = av_clip_int16(lrintf(src[i] + r));
+    }
+}
+
+static int quantize_simple(DitherContext *c, int16_t **dst, float * const *src,
+                           int channels, int nb_samples)
+{
+    int ch, i, j;
+    unsigned int *dither_buf;
+    int aligned_samples, lfg_per_sample;
+
+    aligned_samples = FFALIGN(nb_samples, c->ddsp.samples_align);
+    lfg_per_sample  = c->method == AV_RESAMPLE_DITHER_TRIANGULAR ? 2 : 1;
+
+    dither_buf = av_mallocz(aligned_samples * lfg_per_sample *
+                            sizeof(*dither_buf));
+    if (!dither_buf)
+        return AVERROR(ENOMEM);
+
+    for (ch = 0; ch < channels; ch++) {
+        for (j = 0; j < lfg_per_sample; j++) {
+            for (i = 0; i < nb_samples; i++)
+                dither_buf[j * aligned_samples + i] = 
av_lfg_get(&c->state[ch].lfg);
+        }
+        c->quantize_simple(dst[ch], src[ch], (int *)dither_buf,
+                           aligned_samples);
+    }
+    av_free(dither_buf);
+
+    return 0;
+}
+
+#define SQRT_1_6 0.40824829046386301723f
+
+static int quantize_triangular_hp(DitherContext *c, int16_t **dst,
+                                  float * const *src, int channels,
+                                  int nb_samples)
+{
+    int ch, i;
+
+    for (ch = 0; ch < channels; ch++) {
+        DitherState *state = &c->state[ch];
+
+        if (state->mute > c->mute_reset_threshold)
+            memset(state->dither_a, 0, sizeof(state->dither_a));
+
+        for (i = 0; i < nb_samples; i++) {
+            float tmp, r;
+            float sample = src[ch][i];
+
+            if (sample == 0) {
+                state->mute++;
+                if (state->mute > c->mute_dither_threshold)
+                    continue;
+            } else {
+                state->mute = 0;
+            }
+
+            r  = av_lfg_get_flt(&state->lfg, 0.5f);
+            r += av_lfg_get_flt(&state->lfg, 0.5f);
+
+            /* filter is from libswresample in FFmpeg */
+            tmp = r;
+            r   = (   -state->dither_a[0] +
+                   2 * state->dither_a[1] - r) * SQRT_1_6;
+
+            state->dither_a[0] = state->dither_a[1];
+            state->dither_a[1] = tmp;
+
+            dst[ch][i] = av_clip_int16(lrintf(sample + r));
+        }
+    }
+
+    return 0;
+}
+
+static int quantize_triangular_ns(DitherContext *c, int16_t **dst,
+                                  float * const *src, int channels,
+                                  int nb_samples)
+{
+    int ch, i, j;
+
+    for (ch = 0; ch < channels; ch++) {
+        DitherState *state = &c->state[ch];
+
+        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[ch][i];
+
+            if (sample == 0)
+                state->mute++;
+            else
+                state->mute = 0;
+
+            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[ch][i] = sample;
+
+                state->dither_b[0] = 0;
+            } else {
+                float r    = av_lfg_get_flt(&state->lfg, 0.5f);
+                r         += av_lfg_get_flt(&state->lfg, 0.5f);
+                dst[ch][i] = av_clip_int16(lrintf(sample + r));
+
+                state->dither_b[0] = av_clipf(dst[ch][i] - sample, -1.5f, 
1.5f);
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Scale the samples to s16 range.
+ * The signal is attenuated slightly to avoid clipping.
+ */
+static void scale_flt_data(AVFloatDSPContext *fdsp, AudioData *flt_data)
+{
+    int ch;
+    int aligned_len = FFALIGN(flt_data->nb_samples, 4);
+
+    if (flt_data->ptr_align & 15 || flt_data->samples_align < aligned_len) {
+        int i;
+        for (ch = 0; ch < flt_data->channels; ch++) {
+            float *samples = (float *)flt_data->data[ch];
+            for (i = 0; i < flt_data->nb_samples; i++)
+                samples[i] *= 32753.0f;
+        }
+    } else {
+        for (ch = 0; ch < flt_data->channels; ch++) {
+            fdsp->vector_fmul_scalar((float *)flt_data->data[ch],
+                                     (float *)flt_data->data[ch], 32753.0f,
+                                     aligned_len);
+        }
+    }
+
+}
+
+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;
+    }
+
+    /* make sure flt_data is large enough for the input */
+    if (src->sample_fmt != AV_SAMPLE_FMT_FLTP || src->read_only) {
+        ret = ff_audio_data_realloc(c->flt_data, src->nb_samples);
+        if (ret < 0)
+            return ret;
+        flt_data = c->flt_data;
+    } else {
+        flt_data = src;
+    }
+
+    /* convert input samples to fltp and scale to s16 range */
+    if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) {
+        ret = ff_audio_convert(c->ac_in, flt_data, src);
+        if (ret < 0)
+            return ret;
+    } else if (src->read_only) {
+        ret = ff_audio_data_copy(flt_data, src);
+        if (ret < 0)
+            return ret;
+    }
+    scale_flt_data(&c->fdsp, flt_data);
+
+    /* check alignment and padding constraints */
+    if (c->method == AV_RESAMPLE_DITHER_RECTANGULAR ||
+        c->method == AV_RESAMPLE_DITHER_TRIANGULAR) {
+        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_simple = c->ddsp.quantize_simple;
+        } else {
+            if (c->method == AV_RESAMPLE_DITHER_RECTANGULAR)
+                c->quantize_simple = quantize_simple_rectangular_c;
+            else
+                c->quantize_simple = quantize_simple_triangular_c;
+        }
+    }
+
+    ret = c->quantize(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 **c)
+{
+    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);
+    av_free((*c)->state);
+    av_freep(c);
+}
+
+static void dither_init_simple(DitherDSPContext *ddsp,
+                               enum AVResampleDitherMethod method)
+{
+    switch (method) {
+    case AV_RESAMPLE_DITHER_RECTANGULAR:
+        ddsp->quantize_simple = quantize_simple_rectangular_c;
+        break;
+    case AV_RESAMPLE_DITHER_TRIANGULAR:
+        ddsp->quantize_simple = quantize_simple_triangular_c;
+        break;
+    }
+    ddsp->ptr_align     = 1;
+    ddsp->samples_align = 1;
+
+    if (ARCH_X86)
+        ff_dither_init_simple_x86(ddsp, method);
+}
+
+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, i;
+
+    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;
+    }
+    switch (avr->dither_method) {
+    case AV_RESAMPLE_DITHER_RECTANGULAR:
+    case AV_RESAMPLE_DITHER_TRIANGULAR:
+        c->quantize = quantize_simple;
+        dither_init_simple(&c->ddsp, avr->dither_method);
+        break;
+    case AV_RESAMPLE_DITHER_TRIANGULAR_HP:
+        c->quantize = quantize_triangular_hp;
+        break;
+    case AV_RESAMPLE_DITHER_TRIANGULAR_NS:
+        c->quantize = quantize_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;
+        }
+        break;
+    default:
+        goto fail;
+    }
+    c->method = avr->dither_method;
+
+    c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP,
+                                      "dither flt buffer");
+    if (!c->flt_data)
+        goto fail;
+
+    /* 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->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) {
+        av_free(c);
+        return NULL;
+    }
+
+    /* 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;
+        av_lfg_init(&state->lfg, av_lfg_get(&seed_gen));
+        /* prime the noise buffer for triangular high pass */
+        if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_HP) {
+            for (i = 0; i < 2; i++) {
+                float r = av_lfg_get_flt(&state->lfg, 0.5f);
+                r      += av_lfg_get_flt(&state->lfg, 0.5f);
+                state->dither_a[i] = r;
+            }
+        }
+    }
+
+    avpriv_float_dsp_init(&c->fdsp, 0);
+
+    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..37cfe55
--- /dev/null
+++ b/libavresample/dither.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <[email protected]>
+ *
+ * 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 {
+    void (*quantize_simple)(int16_t *dst, float *src, int *dither, int len);
+    int ptr_align;
+    int samples_align;
+} 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);
+
+/* arch-specific initialization functions */
+
+void ff_dither_init_simple_x86(DitherDSPContext *ddsp,
+                               enum AVResampleDitherMethod method);
+
+#endif /* AVRESAMPLE_DITHER_H */
diff --git a/libavresample/internal.h b/libavresample/internal.h
index 006b6fd..e76240c 100644
--- a/libavresample/internal.h
+++ b/libavresample/internal.h
@@ -74,6 +74,7 @@ struct AVAudioResampleContext {
     ResampleContext *resample;  /**< resampling context                      */
     AudioMix *am;               /**< channel mixing context                  */
     enum AVMatrixEncoding matrix_encoding;      /**< matrixed stereo encoding 
*/
+    enum AVResampleDitherMethod dither_method;  /**< dither method           */
 };
 
 #endif /* AVRESAMPLE_INTERNAL_H */
diff --git a/libavresample/options.c b/libavresample/options.c
index 8f64370..1097f12 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 3fdeeb8..e46029f 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;
@@ -188,8 +190,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_close(avr->am);
     return;
diff --git a/libavresample/x86/Makefile b/libavresample/x86/Makefile
index 65bed89..2e8786f 100644
--- a/libavresample/x86/Makefile
+++ b/libavresample/x86/Makefile
@@ -1,5 +1,7 @@
 OBJS      += x86/audio_convert_init.o                                   \
              x86/audio_mix_init.o                                       \
+             x86/dither_init.o                                          \
 
 YASM-OBJS += x86/audio_convert.o                                        \
              x86/audio_mix.o                                            \
+             x86/dither.o                                               \
diff --git a/libavresample/x86/dither.asm b/libavresample/x86/dither.asm
new file mode 100644
index 0000000..0a8fbd3
--- /dev/null
+++ b/libavresample/x86/dither.asm
@@ -0,0 +1,74 @@
+;******************************************************************************
+;* x86 optimized dithering format conversion
+;* Copyright (c) 2012 Justin Ruggles <[email protected]>
+;*
+;* 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/x86/x86util.asm"
+
+SECTION_RODATA
+
+; 1.0f / (2.0f * INT32_MAX)
+pf_dither_scale:  times 4 dd 2.32830643762e-10
+
+SECTION_TEXT
+
+;------------------------------------------------------------------------------
+; void ff_quantize_simple_rectangular/triangular(int16_t *dst, float *src,
+;                                                int *dither, int len);
+;------------------------------------------------------------------------------
+
+%macro QUANTIZE_SIMPLE 1
+cglobal quantize_simple_%1, 4,5,5, dst, src, dither0, len, dither1
+    lea         lenq, [2*lend]
+    add         dstq, lenq
+    lea         srcq, [srcq+2*lenq]
+%ifidn %1, triangular
+    lea     dither1q, [dither0q+4*lenq]
+%endif
+    lea     dither0q, [dither0q+2*lenq]
+    neg         lenq
+    mova          m0, [pf_dither_scale]
+.loop
+    cvtdq2ps      m1, [dither0q+2*lenq]
+    cvtdq2ps      m2, [dither0q+2*lenq+mmsize]
+%ifidn %1, triangular
+    cvtdq2ps      m3, [dither1q+2*lenq]
+    cvtdq2ps      m4, [dither1q+2*lenq+mmsize]
+    addps         m1, m3
+    addps         m2, m4
+%endif
+    fmaddps       m1, m1, m0, [srcq+2*lenq], m3
+    fmaddps       m2, m2, m0, [srcq+2*lenq+mmsize], m3
+    cvtps2dq      m1, m1
+    cvtps2dq      m2, m2
+    packssdw      m1, m2
+    mova     [dstq+lenq], m1
+    add         lenq, mmsize
+    jl .loop
+    REP_RET
+%endmacro
+
+INIT_XMM sse2
+QUANTIZE_SIMPLE rectangular
+QUANTIZE_SIMPLE triangular
+%if HAVE_FMA4_EXTERNAL
+INIT_XMM fma4
+QUANTIZE_SIMPLE rectangular
+QUANTIZE_SIMPLE triangular
+%endif
diff --git a/libavresample/x86/dither_init.c b/libavresample/x86/dither_init.c
new file mode 100644
index 0000000..65eebcf
--- /dev/null
+++ b/libavresample/x86/dither_init.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <[email protected]>
+ *
+ * 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 "config.h"
+#include "libavutil/cpu.h"
+#include "libavutil/x86/cpu.h"
+#include "libavresample/dither.h"
+
+extern void ff_quantize_simple_rectangular_sse2(int16_t *dst, float *src,
+                                                int *dither, int len);
+extern void ff_quantize_simple_rectangular_fma4(int16_t *dst, float *src,
+                                                int *dither, int len);
+
+extern void ff_quantize_simple_triangular_sse2(int16_t *dst, float *src,
+                                               int *dither, int len);
+extern void ff_quantize_simple_triangular_fma4(int16_t *dst, float *src,
+                                               int *dither, int len);
+
+av_cold void ff_dither_init_simple_x86(DitherDSPContext *ddsp,
+                                       enum AVResampleDitherMethod method)
+{
+    int mm_flags = av_get_cpu_flags();
+
+    switch (method) {
+    case AV_RESAMPLE_DITHER_RECTANGULAR:
+        if (EXTERNAL_SSE2(mm_flags)) {
+            ddsp->quantize_simple = ff_quantize_simple_rectangular_sse2;
+            ddsp->ptr_align       = 16;
+            ddsp->samples_align   = 8;
+        }
+        if (EXTERNAL_FMA4(mm_flags)) {
+            ddsp->quantize_simple = ff_quantize_simple_rectangular_fma4;
+            ddsp->ptr_align       = 16;
+            ddsp->samples_align   = 8;
+        }
+        break;
+    case AV_RESAMPLE_DITHER_TRIANGULAR:
+        if (EXTERNAL_SSE2(mm_flags)) {
+            ddsp->quantize_simple = ff_quantize_simple_triangular_sse2;
+            ddsp->ptr_align       = 16;
+            ddsp->samples_align   = 8;
+        }
+        if (EXTERNAL_FMA4(mm_flags)) {
+            ddsp->quantize_simple = ff_quantize_simple_triangular_fma4;
+            ddsp->ptr_align       = 16;
+            ddsp->samples_align   = 8;
+        }
+        break;
+    }
+}
-- 
1.7.1

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to