[FFmpeg-devel] [PATCH 02/10] swresample/audioconvert: integrate DSD to PCM conversion

Peter Ross pross at xvid.org
Tue May 6 14:37:56 CEST 2014


Signed-off-by: Peter Ross <pross at xvid.org>
---
 doc/APIchanges               |  3 ++
 libswresample/Makefile       | 18 +++++++++
 libswresample/audioconvert.c | 61 +++++++++++++++++++++++++++-
 libswresample/audioconvert.h | 11 ++++-
 libswresample/dsd_tablegen.h | 95 ++++++++++++++++++++++++++++++++++++++++++++
 libswresample/swresample.c   | 24 ++++++++++-
 libswresample/swresample.h   |  7 ++++
 7 files changed, 214 insertions(+), 5 deletions(-)
 create mode 100644 libswresample/dsd_tablegen.h

diff --git a/doc/APIchanges b/doc/APIchanges
index 4f16323..004d279 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -15,6 +15,9 @@ libavutil:     2012-10-22
 
 API changes, most recent first:
 
+2014-xx-xx - xxxxxxxx - lsws x.x.x
+  Add swr_get_dsd2pcm_sr_factor() function.
+
 2014-xx-xx - xxxxxxxx - libavu xx.xx.x
   Add AV_SAMPLE_FMT_DSD and AV_SAMPLE_FMT_DSDP.
 
diff --git a/libswresample/Makefile b/libswresample/Makefile
index 953c945..c5b4511 100644
--- a/libswresample/Makefile
+++ b/libswresample/Makefile
@@ -18,4 +18,22 @@ OBJS-$(CONFIG_SHARED)  += log2_tab.o
 # Windows resource file
 SLIBOBJS-$(HAVE_GNU_WINDRES) += swresampleres.o
 
+SKIPHEADERS                            += %_tablegen.h                  \
+                                          %_tables.h                    \
+
 TESTPROGS = swresample
+
+HOSTPROGS = dsd_tablegen                                                \
+
+CLEANFILES = *_tables.c *_tables.h *_tablegen$(HOSTEXESUF)
+
+GEN_HEADERS = dsd_tables.h dv_tables.h
+
+GEN_HEADERS := $(addprefix $(SUBDIR), $(GEN_HEADERS))
+
+$(GEN_HEADERS): $(SUBDIR)%_tables.h: $(SUBDIR)%_tablegen$(HOSTEXESUF)
+	$(M)./$< > $@
+
+ifdef CONFIG_HARDCODED_TABLES
+$(SUBDIR)audioconvert.o: $(SUBDIR)dsd_tables.h
+endif
diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c
index 4ba0ff1..2df11f5 100644
--- a/libswresample/audioconvert.c
+++ b/libswresample/audioconvert.c
@@ -1,6 +1,8 @@
 /*
  * audio conversion
  * Copyright (c) 2006 Michael Niedermayer <michaelni at gmx.at>
+ * based on BSD licensed dsd2pcm by Sebastian Gesemann
+ * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
  *
  * This file is part of FFmpeg.
  *
@@ -29,6 +31,7 @@
 #include "libavutil/avassert.h"
 #include "libavutil/libm.h"
 #include "libavutil/samplefmt.h"
+#include "libavcodec/mathops.h" // ff_reverse
 #include "audioconvert.h"
 
 
@@ -36,7 +39,7 @@
 
 //FIXME rounding ?
 #define CONV_FUNC(ofmt, otype, ifmt, expr)\
-static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end)\
+static void CONV_FUNC_NAME(ofmt, ifmt)(ChannelState *s, uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end)\
 {\
     uint8_t *end2 = end - 3*os;\
     while(po < end2){\
@@ -77,6 +80,41 @@ CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, av_clipl_int32(llrint(*
 CONV_FUNC(AV_SAMPLE_FMT_FLT, float  , AV_SAMPLE_FMT_DBL, *(const double*)pi)
 CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_DBL, *(const double*)pi)
 
+#include "dsd_tablegen.h"
+#define FIFOMASK (DSD_FIFOSIZE - 1)  /** bit mask for FIFO offsets */
+#if DSD_FIFOSIZE * 8 < HTAPS * 2
+#error "DSD_FIFOSIZE too small"
+#endif
+
+#define DSD2PCM_CONV_FUNC(ofmt, otype, expr)\
+static void CONV_FUNC_NAME(ofmt, AV_SAMPLE_FMT_DSD)(ChannelState *s, uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end)\
+{\
+    unsigned pos, i;\
+    unsigned char* p;\
+    double sum;\
+    pos = s->pos;\
+    while (po < end) {\
+        s->buf[pos] = *pi; pi += is;\
+        p = s->buf + ((pos - CTABLES) & FIFOMASK);\
+        *p = ff_reverse[*p];\
+        sum = 0.0;\
+        for (i = 0; i < CTABLES; i++) {\
+            unsigned char a = s->buf[(pos                   - i) & FIFOMASK];\
+            unsigned char b = s->buf[(pos - (CTABLES*2 - 1) + i) & FIFOMASK];\
+            sum += ctables[i][a] + ctables[i][b];\
+        }\
+        *(otype*)po = expr; po += os;\
+        pos = (pos + 1) & FIFOMASK;\
+    }\
+    s->pos = pos;\
+}
+
+DSD2PCM_CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, av_clip_uint8(  lrint(sum * (1<<7)) + 0x80))
+DSD2PCM_CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, av_clip_int16(  lrint(sum * (1<<15))))
+DSD2PCM_CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, av_clipl_int32(llrint(sum * (1U<<31))))
+DSD2PCM_CONV_FUNC(AV_SAMPLE_FMT_FLT, float  , sum)
+DSD2PCM_CONV_FUNC(AV_SAMPLE_FMT_DBL, double , sum)
+
 #define FMT_PAIR_FUNC(out, in) [out + AV_SAMPLE_FMT_NB*in] = CONV_FUNC_NAME(out, in)
 
 static conv_func_type * const fmt_pair_to_conv_functions[AV_SAMPLE_FMT_NB*AV_SAMPLE_FMT_NB] = {
@@ -105,6 +143,11 @@ static conv_func_type * const fmt_pair_to_conv_functions[AV_SAMPLE_FMT_NB*AV_SAM
     FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DBL),
     FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL),
     FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8,  AV_SAMPLE_FMT_DSD),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_DSD),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DSD),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DSD),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DSD),
 };
 
 static void cpy1(uint8_t **dst, const uint8_t **src, int len){
@@ -134,6 +177,20 @@ AudioConvert *swri_audio_convert_alloc(enum AVSampleFormat out_fmt,
     if (!ctx)
         return NULL;
 
+    if (av_get_packed_sample_fmt(in_fmt) == AV_SAMPLE_FMT_DSD) {
+        int ch;
+        dsd_ctables_tableinit();
+        for (ch = 0; ch < channels; ch++) {
+            ctx->channel[ch].pos = 0;
+            memset(ctx->channel[ch].buf, 0x69, sizeof(ctx->channel[ch].buf));
+            /* 0x69 = 01101001
+            * This pattern "on repeat" makes a low energy 352.8 kHz tone
+            * and a high energy 1.0584 MHz tone which should be filtered
+            * out completely by any playback system --> silence
+            */
+        }
+    }
+
     if(channels == 1){
          in_fmt = av_get_planar_sample_fmt( in_fmt);
         out_fmt = av_get_planar_sample_fmt(out_fmt);
@@ -218,7 +275,7 @@ int swri_audio_convert(AudioConvert *ctx, AudioData *out, AudioData *in, int len
         uint8_t *end= po + os*len;
         if(!po)
             continue;
-        ctx->conv_f(po+off*os, pi+off*is, is, os, end);
+        ctx->conv_f(&ctx->channel[ch], po+off*os, pi+off*is, is, os, end);
     }
     return 0;
 }
diff --git a/libswresample/audioconvert.h b/libswresample/audioconvert.h
index 2e983df..38f5c70 100644
--- a/libswresample/audioconvert.h
+++ b/libswresample/audioconvert.h
@@ -32,8 +32,16 @@
 #include "swresample_internal.h"
 #include "libavutil/cpu.h"
 
+/**
+ * Per-channel context for DSD->PCM conversion
+ */
+typedef struct {
+#define DSD_FIFOSIZE 16  /** must be a power of two */
+    unsigned char buf[DSD_FIFOSIZE];
+    unsigned pos;
+} ChannelState;
 
-typedef void (conv_func_type)(uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end);
+typedef void (conv_func_type)(ChannelState *s, uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end);
 typedef void (simd_func_type)(uint8_t **dst, const uint8_t **src, int len);
 
 typedef struct AudioConvert {
@@ -44,6 +52,7 @@ typedef struct AudioConvert {
     simd_func_type *simd_f;
     const int *ch_map;
     uint8_t silence[8]; ///< silence input sample
+    ChannelState channel[SWR_CH_MAX];
 }AudioConvert;
 
 /**
diff --git a/libswresample/dsd_tablegen.h b/libswresample/dsd_tablegen.h
new file mode 100644
index 0000000..1eecb67
--- /dev/null
+++ b/libswresample/dsd_tablegen.h
@@ -0,0 +1,95 @@
+/*
+ * Header file for hardcoded DSD tables
+ * based on BSD licensed dsd2pcm by Sebastian Gesemann
+ * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SWRESAMPLE_DSD_TABLEGEN_H
+#define SWRESAMPLE_DSD_TABLEGEN_H
+
+#include <stdint.h>
+#include "libavutil/attributes.h"
+
+#define HTAPS   48                /** number of FIR constants */
+#define CTABLES ((HTAPS + 7) / 8) /** number of "8 MACs" lookup tables */
+
+#if CONFIG_HARDCODED_TABLES
+#define dsd_ctables_tableinit()
+#include "libswresample/dsd_tables.h"
+#else
+#include "libavutil/common.h"
+
+/*
+ * Properties of this 96-tap lowpass filter when applied on a signal
+ * with sampling rate of 44100*64 Hz:
+ *
+ * () has a delay of 17 microseconds.
+ *
+ * () flat response up to 48 kHz
+ *
+ * () if you downsample afterwards by a factor of 8, the
+ *    spectrum below 70 kHz is practically alias-free.
+ *
+ * () stopband rejection is about 160 dB
+ *
+ * The coefficient tables ("ctables") take only 6 Kibi Bytes and
+ * should fit into a modern processor's fast cache.
+ */
+
+/**
+ * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter
+ */
+static const double htaps[HTAPS] = {
+     0.09950731974056658,    0.09562845727714668,    0.08819647126516944,
+     0.07782552527068175,    0.06534876523171299,    0.05172629311427257,
+     0.0379429484910187,     0.02490921351762261,    0.0133774746265897,
+     0.003883043418804416,  -0.003284703416210726,  -0.008080250212687497,
+    -0.01067241812471033,   -0.01139427235000863,   -0.0106813877974587,
+    -0.009007905078766049,  -0.006828859761015335,  -0.004535184322001496,
+    -0.002425035959059578,  -0.0006922187080790708,  0.0005700762133516592,
+     0.001353838005269448,   0.001713709169690937,   0.001742046839472948,
+     0.001545601648013235,   0.001226696225277855,   0.0008704322683580222,
+     0.0005381636200535649,  0.000266446345425276,   7.002968738383528e-05,
+    -5.279407053811266e-05, -0.0001140625650874684, -0.0001304796361231895,
+    -0.0001189970287491285, -9.396247155265073e-05, -6.577634378272832e-05,
+    -4.07492895872535e-05,  -2.17407957554587e-05,  -9.163058931391722e-06,
+    -2.017460145032201e-06,  1.249721855219005e-06,  2.166655190537392e-06,
+     1.930520892991082e-06,  1.319400334374195e-06,  7.410039764949091e-07,
+     3.423230509967409e-07,  1.244182214744588e-07,  3.130441005359396e-08
+};
+
+static float ctables[CTABLES][256];
+
+static av_cold void dsd_ctables_tableinit(void)
+{
+    int t, e, m, k;
+    double acc;
+    for (t = 0; t < CTABLES; ++t) {
+        k = FFMIN(HTAPS - t * 8, 8);
+        for (e = 0; e < 256; ++e) {
+            acc = 0.0;
+            for (m = 0; m < k; ++m)
+                acc += (((e >> (7 - m)) & 1) * 2 - 1) * htaps[t * 8 + m];
+            ctables[CTABLES - 1 - t][e] = (float)acc;
+        }
+    }
+}
+#endif /* CONFIG_HARDCODED_TABLES */
+
+#endif /* SWRESAMPLE_DSD_TABLEGEN_H */
diff --git a/libswresample/swresample.c b/libswresample/swresample.c
index 54e06e1..20eb8e1 100644
--- a/libswresample/swresample.c
+++ b/libswresample/swresample.c
@@ -251,6 +251,16 @@ av_cold void swr_free(SwrContext **ss){
     av_freep(ss);
 }
 
+static int is_sample_rate_different(struct SwrContext *s)
+{
+    if (s->out_sample_fmt != AV_SAMPLE_FMT_DSD && s->in_sample_fmt == AV_SAMPLE_FMT_DSD)
+        return s->out_sample_rate != s->in_sample_rate / 8;
+    else if (s->out_sample_fmt == AV_SAMPLE_FMT_DSD && s->in_sample_fmt != AV_SAMPLE_FMT_DSD)
+        return s->out_sample_rate != s->in_sample_rate * 8;
+    else
+        return s->out_sample_rate != s->in_sample_rate;
+}
+
 av_cold int swr_init(struct SwrContext *s){
     int ret;
 
@@ -345,8 +355,13 @@ av_cold int swr_init(struct SwrContext *s){
         }
     }
 
-    if (s->out_sample_rate!=s->in_sample_rate || (s->flags & SWR_FLAG_RESAMPLE)){
-        s->resample = s->resampler->init(s->resample, s->out_sample_rate, s->in_sample_rate, s->filter_size, s->phase_shift, s->linear_interp, s->cutoff, s->int_sample_fmt, s->filter_type, s->kaiser_beta, s->precision, s->cheby);
+    if (is_sample_rate_different(s) || (s->flags & SWR_FLAG_RESAMPLE)){
+        int int_sample_rate = s->in_sample_rate;
+        if (s->in_sample_fmt == AV_SAMPLE_FMT_DSD || s->in_sample_fmt == AV_SAMPLE_FMT_DSDP) {
+            int_sample_rate /= swr_get_dsd2pcm_sr_factor(s);
+            av_log(s, AV_LOG_DEBUG, "performing dsd2pcm conversion and %s resample (%i -> %i -> %i Hz)\n", av_get_sample_fmt_name(s->int_sample_fmt), s->in_sample_rate, int_sample_rate, s->out_sample_rate);
+        }
+        s->resample = s->resampler->init(s->resample, s->out_sample_rate, int_sample_rate, s->filter_size, s->phase_shift, s->linear_interp, s->cutoff, s->int_sample_fmt, s->filter_type, s->kaiser_beta, s->precision, s->cheby);
     }else
         s->resampler->free(&s->resample);
     if(    s->int_sample_fmt != AV_SAMPLE_FMT_S16P
@@ -940,3 +955,8 @@ int64_t swr_next_pts(struct SwrContext *s, int64_t pts){
         return s->outpts;
     }
 }
+
+int swr_get_dsd2pcm_sr_factor(struct SwrContext *s)
+{
+    return 8;
+}
diff --git a/libswresample/swresample.h b/libswresample/swresample.h
index 0525289..3c3baf5 100644
--- a/libswresample/swresample.h
+++ b/libswresample/swresample.h
@@ -297,6 +297,13 @@ int swr_inject_silence(struct SwrContext *s, int count);
 int64_t swr_get_delay(struct SwrContext *s, int64_t base);
 
 /**
+ * Return DSD to PCM sample rate conversion factor
+ *
+ * When converting DSD to PCM the sample rate is divided by a factor.
+ */
+int swr_get_dsd2pcm_sr_factor(struct SwrContext *s);
+
+/**
  * Return the LIBSWRESAMPLE_VERSION_INT constant.
  */
 unsigned swresample_version(void);
-- 
1.8.3.2

-- Peter
(A907 E02F A6E5 0CD2 34CD 20D2 6760 79C5 AC40 DD6B)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20140506/aa53163f/attachment.asc>


More information about the ffmpeg-devel mailing list