adx: calculate correct LPC coeffs
authorJustin Ruggles <justin.ruggles@gmail.com>
Sun, 20 Nov 2011 19:21:32 +0000 (14:21 -0500)
committerJustin Ruggles <justin.ruggles@gmail.com>
Sat, 26 Nov 2011 21:25:06 +0000 (16:25 -0500)
Instead of using fixed coefficients, the correct way is to calculate the
coefficients using the highpass cutoff frequency from the ADX stream header
and the sample rate.

libavcodec/Makefile
libavcodec/adx.c [new file with mode: 0644]
libavcodec/adx.h
libavcodec/adxdec.c
libavcodec/adxenc.c

index 37aa8ee..2cdcca2 100644 (file)
@@ -489,7 +489,7 @@ OBJS-$(CONFIG_PCM_U32LE_ENCODER)          += pcm.o
 OBJS-$(CONFIG_PCM_ZORK_DECODER)           += pcm.o
 
 OBJS-$(CONFIG_ADPCM_4XM_DECODER)          += adpcm.o adpcm_data.o
-OBJS-$(CONFIG_ADPCM_ADX_DECODER)          += adxdec.o
+OBJS-$(CONFIG_ADPCM_ADX_DECODER)          += adxdec.o adx.o
 OBJS-$(CONFIG_ADPCM_ADX_ENCODER)          += adxenc.o
 OBJS-$(CONFIG_ADPCM_CT_DECODER)           += adpcm.o adpcm_data.o
 OBJS-$(CONFIG_ADPCM_EA_DECODER)           += adpcm.o
diff --git a/libavcodec/adx.c b/libavcodec/adx.c
new file mode 100644 (file)
index 0000000..bc3e882
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011  Justin Ruggles
+ *
+ * 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/mathematics.h"
+#include "adx.h"
+
+void ff_adx_calculate_coeffs(int cutoff, int sample_rate, int bits, int *coeff)
+{
+    double a, b, c;
+
+    a = M_SQRT2 - cos(2.0 * M_PI * cutoff / sample_rate);
+    b = M_SQRT2 - 1.0;
+    c = (a - sqrt((a + b) * (a - b))) / b;
+
+    coeff[0] = lrintf(c * 2.0  * (1 << bits));
+    coeff[1] = lrintf(-(c * c) * (1 << bits));
+}
index ae5eb6a..cd8c45b 100644 (file)
@@ -41,10 +41,20 @@ typedef struct {
     int header_parsed;
     unsigned char dec_temp[18*2];
     int in_temp;
+    int cutoff;
+    int coeff[2];
 } ADXContext;
 
 #define COEFF_BITS  12
-#define COEFF1      0x1CA6
-#define COEFF2      0x0CD4
+
+/**
+ * Calculate LPC coefficients based on cutoff frequency and sample rate.
+ *
+ * @param cutoff       cutoff frequency
+ * @param sample_rate  sample rate
+ * @param bits         number of bits used to quantize coefficients
+ * @param[out] coeff   2 quantized LPC coefficients
+ */
+void ff_adx_calculate_coeffs(int cutoff, int sample_rate, int bits, int *coeff);
 
 #endif /* AVCODEC_ADX_H */
index 93bbc6b..f9f17cd 100644 (file)
@@ -59,7 +59,7 @@ static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch)
     s2 = prev->s2;
     for (i = 0; i < 32; i++) {
         d  = get_sbits(&gb, 4);
-        s0 = ((d << COEFF_BITS) * scale + COEFF1 * s1 - COEFF2 * s2) >> COEFF_BITS;
+        s0 = ((d << COEFF_BITS) * scale + c->coeff[0] * s1 + c->coeff[1] * s2) >> COEFF_BITS;
         s2 = s1;
         s1 = av_clip_int16(s0);
         *out = s1;
@@ -81,7 +81,7 @@ static int adx_decode_header(AVCodecContext *avctx, const uint8_t *buf,
                              int bufsize)
 {
     ADXContext *c = avctx->priv_data;
-    int offset;
+    int offset, cutoff;
 
     if (AV_RB16(buf) != 0x8000)
         return AVERROR_INVALIDDATA;
@@ -98,6 +98,9 @@ static int adx_decode_header(AVCodecContext *avctx, const uint8_t *buf,
         return AVERROR_INVALIDDATA;
     avctx->bit_rate    = avctx->sample_rate * avctx->channels * 18 * 8 / 32;
 
+    cutoff = AV_RB16(buf + 16);
+    ff_adx_calculate_coeffs(cutoff, avctx->sample_rate, COEFF_BITS, c->coeff);
+
     return offset;
 }
 
index 7225c31..b85a70d 100644 (file)
@@ -34,7 +34,7 @@
 
 /* 18 bytes <-> 32 samples */
 
-static void adx_encode(unsigned char *adx,const short *wav,
+static void adx_encode(ADXContext *c, unsigned char *adx, const short *wav,
                        ADXChannelState *prev)
 {
     int scale;
@@ -48,7 +48,7 @@ static void adx_encode(unsigned char *adx,const short *wav,
     s2 = prev->s2;
     for(i=0;i<32;i++) {
         s0 = wav[i];
-        d = ((s0 << COEFF_BITS) - COEFF1 * s1 + COEFF2 * s2) >> COEFF_BITS;
+        d = ((s0 << COEFF_BITS) - c->coeff[0] * s1 - c->coeff[1] * s2) >> COEFF_BITS;
         data[i]=d;
         if (max<d) max=d;
         if (min>d) min=d;
@@ -102,19 +102,24 @@ static int adx_encode_header(AVCodecContext *avctx,unsigned char *buf,size_t buf
     } adxhdr; /* big endian */
     /* offset-6 "(c)CRI" */
 #endif
+    ADXContext *c = avctx->priv_data;
+
     AV_WB32(buf+0x00,0x80000000|0x20);
     AV_WB32(buf+0x04,0x03120400|avctx->channels);
     AV_WB32(buf+0x08,avctx->sample_rate);
     AV_WB32(buf+0x0c,0); /* FIXME: set after */
-    AV_WB32(buf+0x10,0x01040300);
-    AV_WB32(buf+0x14,0x00000000);
-    AV_WB32(buf+0x18,0x00000000);
-    memcpy(buf+0x1c,"\0\0(c)CRI",8);
+    AV_WB16(buf + 0x10, c->cutoff);
+    AV_WB32(buf + 0x12, 0x03000000);
+    AV_WB32(buf + 0x16, 0x00000000);
+    AV_WB32(buf + 0x1a, 0x00000000);
+    memcpy (buf + 0x1e, "(c)CRI", 6);
     return 0x20+4;
 }
 
 static av_cold int adx_encode_init(AVCodecContext *avctx)
 {
+    ADXContext *c = avctx->priv_data;
+
     if (avctx->channels > 2)
         return -1; /* only stereo or mono =) */
     avctx->frame_size = 32;
@@ -124,6 +129,10 @@ static av_cold int adx_encode_init(AVCodecContext *avctx)
 
 //    avctx->bit_rate = avctx->sample_rate*avctx->channels*18*8/32;
 
+    /* the cutoff can be adjusted, but this seems to work pretty well */
+    c->cutoff = 500;
+    ff_adx_calculate_coeffs(c->cutoff, avctx->sample_rate, COEFF_BITS, c->coeff);
+
     av_log(avctx, AV_LOG_DEBUG, "adx encode init\n");
 
     return 0;
@@ -159,7 +168,7 @@ static int adx_encode_frame(AVCodecContext *avctx,
 
     if (avctx->channels==1) {
         while(rest>=32) {
-            adx_encode(dst,samples,c->prev);
+            adx_encode(c, dst, samples, c->prev);
             dst+=18;
             samples+=32;
             rest-=32;
@@ -174,8 +183,8 @@ static int adx_encode_frame(AVCodecContext *avctx,
                 tmpbuf[i+32] = samples[i*2+1];
             }
 
-            adx_encode(dst,tmpbuf,c->prev);
-            adx_encode(dst+18,tmpbuf+32,c->prev+1);
+            adx_encode(c, dst,      tmpbuf,      c->prev);
+            adx_encode(c, dst + 18, tmpbuf + 32, c->prev + 1);
             dst+=18*2;
             samples+=32*2;
             rest-=32*2;