[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