ref: da6bd76bcbdbad47ce45df93d3e000b91bc85673
parent: eb34a52b9b0c0b82b889cbef287c174a1344df36
author: cbagwell <cbagwell>
date: Mon Nov 22 11:09:01 EST 1999
IMA ADPCM write support in wav files, speed up of adpcm codecs, and speed improvements to raw.
--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,12 @@
sox-12.17
---------
+ o Sox can now write IMA_ADPCM compressed .wav,
+ MSADPCM writing should be available soon.
+ o General changes to wav.c for writing additional wav formats.
+ o Speedups to adpcm read routines, new codex versions are
+ now in ima_rw.c and adpcm.c.
+ o Speedups for raw.c, especially for gcc with glibc.
o Fixed a segfault problem with ulaw/alaw conversion, where
an out-of-range index into the tables could occur.
o More detailed output from the stat effect.
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -24,12 +24,12 @@
MANDIR = $(PREFIX)/man
INCDIR = $(PREFIX)/include
-SRCDIR = sox-12.16
+SRCDIR = sox-12.17
##############################################################################
-FOBJ = 8svx.o aiff.o alsa.o au.o auto.o avr.o cdr.o cvsd.o dat.o \
- g721.o g723_24.o g723_40.o g72x.o gsm.o hcom.o maud.o oss.o raw.o \
+FOBJ = 8svx.o adpcm.o aiff.o alsa.o au.o auto.o avr.o cdr.o cvsd.o dat.o \
+ g721.o g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.c maud.o oss.o raw.o \
sf.o smp.o sndrtool.o sunaudio.o tx16w.o voc.o wav.o wve.o
EOBJ = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
@@ -86,6 +86,14 @@
# its in stdlib.h.
GETOPT_DEFINES = -DHAVE_GETOPT_H
+# If your system has bswap_16(), bswap_32() in byteswap.h
+# (Such as the case with GNU libc 2.1)
+# then uncomment the following line to use these fast versions.
+BYTESWAP_DEFINES = -DHAVE_BYTESWAP_H
+
+# If your system has rand() then uncomment the following line.
+RAND_DEFINES = -DHAVE_RAND
+
# Uncomment the following if your system does not have a built in
# strerror(). This includes SunOS.
#
@@ -178,8 +186,8 @@
SOX_POST_LIBS = $(GSM_POST_LIBS) $(MATHLIB)
SOX_INCLUDES = $(GSM_INCLUDES)
SOX_DEFINES = $(GSM_DEFINES) $(PLAYER_DEFINES) \
- $(GETOPT_DEFINES) $(STRERR_DEFINES) $(MEMMOVE_DEFINES) \
- $(DOS_DEFINES) $(NEXT_DEFINES) $(MISC_DEFINES)
+ $(GETOPT_DEFINES) $(BYTESWAP_DEFINES) $(RAND_DEFINES) $(STRERR_DEFINES) \
+ $(MEMMOVE_DEFINES) $(DOS_DEFINES) $(NEXT_DEFINES) $(MISC_DEFINES)
CFLAGS = $O $(SOX_DEFINES) $(SOX_INCLUDES)
--- a/configure
+++ b/configure
@@ -1347,7 +1347,7 @@
CPPFLAGS="$ac_save_CPPFLAGS"
-for ac_hdr in getopt.h unistd.h malloc.h
+for ac_hdr in getopt.h unistd.h malloc.h byteswap.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
@@ -1389,7 +1389,7 @@
-for ac_func in getopt strerror memmove
+for ac_func in getopt strerror memmove rand
do
echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
echo "configure:1396: checking for $ac_func" >&5
@@ -1595,6 +1595,49 @@
fi
if test "$oss_dsp" = yes
then
+ if test "$ac_cv_dev_oss_dsp" = ""
+ then
+ for ac_hdr in sys/soundcard.h machine/soundcard.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1605: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1610 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1615: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+ fi
CFLAGS="$CFLAGS -DOSS_PLAYER"
NEED_OSS=1
PLAY_SUPPORT=1
@@ -1603,7 +1646,7 @@
if test "$sun_audio" = auto
then
echo $ac_n "checking whether /dev/audio is functional (SUN style)""... $ac_c" 1>&6
-echo "configure:1607: checking whether /dev/audio is functional (SUN style)" >&5
+echo "configure:1650: checking whether /dev/audio is functional (SUN style)" >&5
if eval "test \"`echo '$''{'ac_cv_dev_sun_audio'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
@@ -1611,7 +1654,7 @@
ac_cv_dev_sun_audio=no
else
cat > conftest.$ac_ext <<EOF
-#line 1615 "configure"
+#line 1658 "configure"
#include "confdefs.h"
int
@@ -1627,7 +1670,7 @@
}
EOF
-if { (eval echo configure:1631: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1674: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
then
ac_cv_dev_sun_audio=yes
else
@@ -1647,17 +1690,17 @@
then
ac_safe=`echo "sys/audioio.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for sys/audioio.h""... $ac_c" 1>&6
-echo "configure:1651: checking for sys/audioio.h" >&5
+echo "configure:1694: checking for sys/audioio.h" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 1656 "configure"
+#line 1699 "configure"
#include "confdefs.h"
#include <sys/audioio.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1661: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1704: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
@@ -1680,17 +1723,17 @@
ac_safe=`echo "sun/audioio.h" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for sun/audioio.h""... $ac_c" 1>&6
-echo "configure:1684: checking for sun/audioio.h" >&5
+echo "configure:1727: checking for sun/audioio.h" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 1689 "configure"
+#line 1732 "configure"
#include "confdefs.h"
#include <sun/audioio.h>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1694: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1737: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
@@ -1719,6 +1762,49 @@
fi
if test "$sun_audio" = yes
then
+ if test "$ac_cv_dev_sun_audio" = ""
+ then
+ for ac_hdr in sys/audioio.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1772: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1777 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1782: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ sun/audioio.h
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+ fi
CFLAGS="$CFLAGS -DSUNAUDIO_PLAYER"
NEED_SUNAU=1
PLAY_SUPPORT=1
--- a/configure.in
+++ b/configure.in
@@ -202,7 +202,7 @@
then
if test "$ac_cv_dev_oss_dsp" = ""
then
- AC_CHEKC_HEADERS(sys/soundcard.h machine/soundchare.h,)
+ AC_CHECK_HEADERS(sys/soundcard.h machine/soundcard.h,)
fi
CFLAGS="$CFLAGS -DOSS_PLAYER"
NEED_OSS=1
@@ -246,7 +246,7 @@
then
if test "$ac_cv_dev_sun_audio" = ""
then
- AC_CHEKC_HEADERS(sys/audioio.h, sun/audioio.h,)
+ AC_CHECK_HEADERS(sys/audioio.h, sun/audioio.h,)
fi
CFLAGS="$CFLAGS -DSUNAUDIO_PLAYER"
NEED_SUNAU=1
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -41,8 +41,8 @@
# Objects.
-FOBJ = 8svx.o aiff.o au.o auto.o avr.o cdr.o cvsd.o dat.o g721.o \
- g723_24.o g723_40.o g72x.o gsm.o hcom.o maud.o raw.o \
+FOBJ = 8svx.o adpcm.o aiff.o au.o auto.o avr.o cdr.o cvsd.o dat.o g721.o \
+ g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.o maud.o raw.o \
sf.o smp.o sndrtool.o tx16w.o voc.o wav.o wve.o
EOBJ = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
--- /dev/null
+++ b/src/adpcm.c
@@ -1,0 +1,165 @@
+/*
+ * adpcm.c codex functions for MS_ADPCM data
+ * (hopefully) provides interoperability with
+ * Microsoft's ADPCM format, but, as usual,
+ * see LACK-OF-WARRANTY information below.
+ *
+ * Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * November 20, 1999
+ * specs I've seen are unclear about ADPCM supporting more than 2 channels,
+ * but these routines support more channels in a manner which looks (IMHO)
+ * like the most natural extension.
+ *
+ * Remark: code still turbulent, encoding to be added RSN
+ */
+
+#include <sys/types.h>
+#include <math.h>
+#include <stdio.h>
+#include "adpcm.h"
+
+typedef struct MsState {
+ LONG step; /* step size */
+ short iCoef[2];
+} MsState_t;
+
+#define lsbshortldi(x,p) { (x)=((short)((int)(p)[0] + ((int)(p)[1]<<8))); (p) += 2; }
+
+/*
+ * Lookup tables for MS ADPCM format
+ */
+
+/* these are step-size adjust factors, where
+ * 1.0 is scaled to 0x100
+ */
+static LONG stepAdjustTable[] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+};
+
+/* TODO : The first 7 iCoef sets are always hardcoded and must
+ appear in the actual WAVE file. They should be read in
+ in case a sound program added extras to the list. */
+
+static short iCoef[7][2] = {
+ { 256, 0},
+ { 512,-256},
+ { 0, 0},
+ { 192, 64},
+ { 240, 0},
+ { 460,-208},
+ { 392,-232}
+};
+
+#if 0
+static LONG AdpcmDecode(LONG, MsState_t*, LONG, LONG)__attribute__((regparm(3)));
+#endif
+
+#ifdef __GNUC__
+inline
+#endif
+static LONG AdpcmDecode(c, state, sample1, sample2)
+LONG c, sample1, sample2;
+MsState_t *state;
+{
+ LONG predict;
+ LONG sample;
+ LONG step, step1;
+
+ /** Compute next step value **/
+ step = state->step;
+ step1 = (stepAdjustTable[c] * step) >> 8;
+ state->step = (step1 < 16)? 16:step1;
+
+ /** make linear prediction for next sample **/
+ predict =
+ ((sample1 * state->iCoef[0]) +
+ (sample2 * state->iCoef[1])) >> 8;
+ /** then add the code*step adjustment **/
+ c -= (c & 0x08) << 1;
+ sample = (c * step) + predict;
+
+ if (sample > 32767)
+ sample = 32767;
+ else if (sample < -32768)
+ sample = -32768;
+
+ return (sample);
+}
+
+/* AdpcmBlockExpandI() outputs interleaved samples into one output buffer */
+const char *AdpcmBlockExpandI(
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL *obuff, /* output samples, n*chans */
+ int n /* samples to decode PER channel */
+)
+{
+ const u_char *ip;
+ int ch;
+ const char *errmsg = NULL;
+ MsState_t state[4]; /* One decompressor state for each channel */
+
+ /* Read the four-byte header for each channel */
+ ip = ibuff;
+ for (ch = 0; ch < chans; ch++) {
+ u_char bpred = *ip++;
+ if (bpred >= 7) {
+ errmsg = "MSADPCM bpred >= 7, arbitrarily using 0\n";
+ bpred = 0;
+ }
+ state[ch].iCoef[0] = iCoef[(int)bpred][0];
+ state[ch].iCoef[1] = iCoef[(int)bpred][1];
+ }
+
+ for (ch = 0; ch < chans; ch++)
+ lsbshortldi(state[ch].step, ip);
+
+ /* sample1's directly into obuff */
+ for (ch = 0; ch < chans; ch++)
+ lsbshortldi(obuff[chans+ch], ip);
+
+ /* sample2's directly into obuff */
+ for (ch = 0; ch < chans; ch++)
+ lsbshortldi(obuff[ch], ip);
+
+ {
+ int ch;
+ u_char b;
+ short *op, *top;
+
+ /* already have 1st 2 samples from block-header */
+ op = obuff + 2*chans;
+ top = obuff + n*chans;
+
+ ch = 0;
+ while (op < top) {
+ b = *ip++;
+ *op++ = AdpcmDecode(b >> 4, state+ch, op[-chans], op[-2*chans]);
+ if (++ch == chans) ch = 0;
+ /* ch = ++ch % chans; */
+ *op++ = AdpcmDecode(b&0x0f, state+ch, op[-chans], op[-2*chans]);
+ if (++ch == chans) ch = 0;
+ /* ch = ++ch % chans; */
+ }
+ }
+ return errmsg;
+}
--- /dev/null
+++ b/src/adpcm.h
@@ -1,0 +1,21 @@
+/*
+ * adpcm.h
+ */
+#ifndef SAMPL
+#define SAMPL short
+#endif
+#ifndef LONG
+#define LONG long
+#endif
+#ifndef ULONG
+#define ULONG u_long
+#endif
+
+/* AdpcmBlockExpandI() outputs interleaved samples into one output buffer */
+extern const char *AdpcmBlockExpandI(
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL *obuff, /* output samples, n*chans */
+ int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
+);
+
--- a/src/filter.c
+++ b/src/filter.c
@@ -3,11 +3,36 @@
*/
/*
- * November 7, 1999
+ * November 18, 1999
+ * Copyright 1994 Julius O. Smith
+ * Copyright 1991 (?) Lance Norskog (?)
+ * Copyright 1999 Stan Brooks <stabro@megsinet.net>
*
+ * -------------------------------------------------------------------
* This source code 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.
+ * -------------------------------------------------------------------
+ *
+ * REMARKS: (Stan Brooks speaking)
+ * This code is heavily based on the resample.c code which was
+ * apparently itself a rewrite (by Lance Norskog?) of code originally
+ * by Julius O. Smith, and distributed under the GPL license...
+ * I am currently trying to contact Lance Norskog and Julius O. Smith
+ * to hopefully clear up the question of copyright, license, etc, of
+ * this code and resample.c. The LGPL license would be ideal IMHO.
+ *
+ * The following was in the README file accompanying resample-1.2 :
+ *
+ * COPYING
+ *
+ * This software package is Copyright 1994 by Julius O. Smith
+ * (jos@ccrma.stanford.edu), all rights reserved. Permission to use and copy
+ * is granted subject to the terms of the "GNU Software General Public
+ * License" (see ftp://prep.ai.mit.edu/pub/gnu/COPYING). In addition, we
+ * request that a copy of any modified files be sent by email to
+ * jos@ccrma.stanford.edu so that we may incorporate them in the CCRMA
+ * version.
*
*/
--- /dev/null
+++ b/src/ima_rw.c
@@ -1,0 +1,405 @@
+/*
+ ima_rw.c -- codex utilities for WAV_FORMAT_IMA_ADPCM
+
+ Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+#include <sys/types.h>
+#include <math.h>
+#include <stdio.h>
+#include "ima_rw.h"
+
+/*
+ *
+ * Lookup tables for IMA ADPCM format
+ *
+ */
+#define ISSTMAX 88
+
+static const int imaStepSizeTable[ISSTMAX + 1] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34,
+ 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494,
+ 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
+ 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026,
+ 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
+ 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623,
+ 27086, 29794, 32767
+};
+
+#define imaStateAdjust(c) (((c)<4)? -1:(2*(c)-6))
+/* +0 - +3, decrease step size */
+/* +4 - +7, increase step size */
+/* -0 - -3, decrease step size */
+/* -4 - -7, increase step size */
+
+static u_char imaStateAdjustTable[ISSTMAX+1][8];
+
+void initImaTable(void)
+{
+ int i,j,k;
+ for (i=0; i<=ISSTMAX; i++) {
+ for (j=0; j<8; j++) {
+ k = i + imaStateAdjust(j);
+ if (k<0) k=0;
+ else if (k>ISSTMAX) k=ISSTMAX;
+ imaStateAdjustTable[i][j] = k;
+ }
+ }
+}
+
+static void ImaExpandS(
+ int ch, /* channel number to decode, REQUIRE 0 <= ch < chans */
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL *obuff, /* obuff[n] will be output samples */
+ int n, /* samples to decode PER channel, REQUIRE n % 8 == 1 */
+ int o_inc /* index difference between successive output samples */
+)
+{
+ const u_char *ip;
+ int i_inc;
+ SAMPL *op;
+ int i, val, state;
+
+ ip = ibuff + 4*ch; /* input pointer to 4-byte block state-initializer */
+ /* fprintf(stderr, "IMA_ADPCM ip %p op %p\n", ip,obuff); */
+ i_inc = 4*(chans-1); /* amount by which to incr ip after each 4-byte read */
+ val = (short)(ip[0] + (ip[1]<<8)); /* need cast for sign-extend */
+ state = ip[2];
+ if (state > ISSTMAX) {
+ fprintf(stderr, "IMA_ADPCM block ch%d initial-state (%d) out of range\n", ch, state);
+ fflush(stderr);
+ state = 0;
+ }
+ /* specs say to ignore ip[3] , but write it as 0 */
+ ip += 4+i_inc;
+
+ op = obuff;
+ *op = val; /* 1st output sample for this channel */
+ op += o_inc;
+ /* fprintf(stderr, "ch%d val000 .... %.4x %2d\n", ch,val,state); */
+
+ for (i = 1; i < n; i++) {
+ int step,dp,c,cm;
+
+ if (i&1) { /* 1st of pair */
+ cm = *ip & 0x0f;
+ } else {
+ cm = (*ip++)>>4;
+ if ((i&7) == 0) /* ends the 8-sample input block for this channel */
+ ip += i_inc; /* skip ip for next group */
+ }
+
+ step = imaStepSizeTable[state];
+ /* Update the state for the next sample */
+ c = cm & 0x07;
+ state = imaStateAdjustTable[state][c];
+
+# ifdef STRICT_IMA
+ dp = 0;
+ if (c & 4) dp += step;
+ step = step >> 1;
+ if (c & 2) dp += step;
+ step = step >> 1;
+ if (c & 1) dp += step;
+ step = step >> 1;
+ dp += step;
+# else
+ dp = ((c+c+1) * step) >> 3; /* faster than bit-test & add on my cpu */
+# endif
+ if (c != cm) {
+ val -= dp;
+ if (val<-0x8000) val = -0x8000;
+ } else {
+ val += dp;
+ if (val>0x7fff) val = 0x7fff;
+ }
+ *op = val;
+ op += o_inc;
+ /* fprintf(stderr, "ch%d val%3d %.4x %.4x %2d\n", ch,i,dp,val,state); */
+ }
+ /* fprintf(stderr, " -> ip %p op %p\n", ip,op); */
+ return;
+}
+
+/* ImaBlockExpandI() outputs interleaved samples into one output buffer */
+void ImaBlockExpandI(
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL *obuff, /* output samples, n*chans */
+ int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
+)
+{
+ int ch;
+ for (ch=0; ch<chans; ch++)
+ ImaExpandS(ch, chans, ibuff, obuff+ch, n, chans);
+}
+
+/* ImaBlockExpandM() outputs non-interleaved samples into chan separate output buffers */
+void ImaBlockExpandM(
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL **obuffs, /* chan output sample buffers, each takes n samples */
+ int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
+)
+{
+ int ch;
+ for (ch=0; ch<chans; ch++)
+ ImaExpandS(ch, chans, ibuff, obuffs[ch], n, 1);
+}
+
+static int ImaMashS(
+ int ch, /* channel number to encode, REQUIRE 0 <= ch < chans */
+ int chans, /* total channels */
+ SAMPL v0, /* value to use as starting prediction0 */
+ const SAMPL *ibuff, /* ibuff[] is interleaved input samples */
+ int n, /* samples to encode PER channel, REQUIRE n % 8 == 1 */
+ int *st, /* input/output state, REQUIRE 0 <= *st <= ISSTMAX */
+ u_char *obuff, /* output buffer[blockAlign], or NULL for no output */
+ int sho /* nonzero for debug printout */
+)
+{
+ const SAMPL *ip, *itop;
+ u_char *op;
+ int o_inc = 0; /* set 0 only to shut up gcc's 'might be uninitialized' */
+ int i, val;
+ int state;
+ double d2; /* long long is okay also, speed abt the same */
+
+ ip = ibuff + ch; /* point ip to 1st input sample for this channel */
+ itop = ibuff + n*chans;
+ val = *ip - v0; ip += chans;/* 1st input sample for this channel */
+ d2 = val*val;/* d2 will be sum of squares of errors, given input v0 and *st */
+ val = v0;
+
+ op = obuff; /* output pointer (or NULL) */
+ if (op) { /* NULL means don't output, just compute the rms error */
+ op += 4*ch; /* where to put this channel's 4-byte block state-initializer */
+ o_inc = 4*(chans-1); /* amount by which to incr op after each 4-byte written */
+ *op++ = val; *op++ = val>>8;
+ *op++ = *st; *op++ = 0; /* they could have put a mid-block state-correction here */
+ op += o_inc; /* _sigh_ NEVER waste a byte. It's a rule! */
+ }
+
+ state = *st;
+
+ for (i = 0; ip < itop; ip+=chans) {
+ int step,d,dp,c;
+
+ d = *ip - val; /* difference between last prediction and current sample */
+
+ step = imaStepSizeTable[state];
+ c = (abs(d)<<2)/step;
+ if (c > 7) c = 7;
+ /* Update the state for the next sample */
+ state = imaStateAdjustTable[state][c];
+
+ if (op) { /* if we want output, put it in proper place */
+ int cm = c;
+ if (d<0) cm |= 8;
+ if (i&1) { /* odd numbered output */
+ *op++ |= (cm<<4);
+ if (i == 7) /* ends the 8-sample output block for this channel */
+ op += o_inc; /* skip op for next group */
+ } else {
+ *op = cm;
+ }
+ i = (i+1) & 0x07;
+ }
+
+# ifdef STRICT_IMA
+ dp = 0;
+ if (c & 4) dp += step;
+ step = step >> 1;
+ if (c & 2) dp += step;
+ step = step >> 1;
+ if (c & 1) dp += step;
+ step = step >> 1;
+ dp += step;
+# else
+ dp = ((c+c+1) * step) >> 3; /* faster than bit-test & add on my cpu */
+# endif
+ if (d<0) {
+ val -= dp;
+ if (val<-0x8000) val = -0x8000;
+ } else {
+ val += dp;
+ if (val>0x7fff) val = 0x7fff;
+ }
+
+ {
+ int x = *ip - val;
+ d2 += x*x;
+ }
+
+ }
+ d2 /= n; /* be sure it's non-negative */
+ if (sho) {
+ fprintf(stderr, "n %d, st %d->%d, d %.1f\n", n, *st, state, sqrt(d2));
+ fflush(stderr);
+ }
+ *st = state;
+ return (int) sqrt(d2);
+}
+
+#if 0
+static long AvgDelta(int ch, int chans, const SAMPL *ibuff, int n)
+{
+ const SAMPL *ip, *itop;
+ int v0;
+ long d1;
+
+ ip = ibuff + ch;
+ itop = ip + n*chans;
+ d1 = 0;
+ v0 = *ip;
+ ip += chans;
+ for ( ; ip < itop; ip+=chans) {
+ int v1;
+
+ v1 = *ip;
+ d1 += abs(v1-v0);
+ v0 = v1;
+ }
+ return (d1/(n-1));
+}
+#endif
+
+/* mash one channel... if you want to use opt>0, 9 is a reasonable value */
+void ImaMashChannel(
+ int ch, /* channel number to encode, REQUIRE 0 <= ch < chans */
+ int chans, /* total channels */
+ const SAMPL *ip, /* ip[] is interleaved input samples */
+ int n, /* samples to encode PER channel, REQUIRE n % 8 == 1 */
+ int *st, /* input/output state, REQUIRE 0 <= *st <= ISSTMAX */
+ u_char *obuff, /* output buffer[blockAlign] */
+ int opt /* non-zero allows some cpu-intensive code to improve output */
+)
+{
+ int snext,d;
+ int s0,d0;
+ int s32,d32;
+ int sho = 0;
+
+ s32 = s0 = *st;
+ if (opt>0) {
+ int low,hi,w;
+ int low0,hi0;
+ snext = s0;
+ d32 = d0 = ImaMashS(ch, chans, ip[0], ip,n,&snext, NULL, sho);
+
+ w = 0;
+ low=hi=s0;
+ low0 = low-opt; if (low0<0) low0=0;
+ hi0 = hi+opt; if (hi0>ISSTMAX) hi0=ISSTMAX;
+ while (low>low0 || hi<hi0) {
+ if (!w && low>low0) {
+ int d;
+ snext = --low;
+ d = ImaMashS(ch, chans, ip[0], ip,n,&snext, NULL, sho);
+ if (d<d0) {
+ d0=d; s0=low;
+ low0 = low-opt; if (low0<0) low0=0;
+ hi0 = low+opt; if (hi0>ISSTMAX) hi0=ISSTMAX;
+ }
+ }
+ if (w && hi<hi0) {
+ int d;
+ snext = ++hi;
+ d = ImaMashS(ch, chans, ip[0], ip,n,&snext, NULL, sho);
+ if (d<d0) {
+ d0=d; s0=hi;
+ low0 = hi-opt; if (low0<0) low0=0;
+ hi0 = hi+opt; if (hi0>ISSTMAX) hi0=ISSTMAX;
+ }
+ }
+ w=1-w;
+ }
+ *st = s0;
+ }
+ d = ImaMashS(ch, chans, ip[0], ip,n,st, obuff, 0);
+ /* printf("%4d %6d %6d\n", s0-s32, d0, d32-d0); */
+ /* printf("%5d %2d\n", AvgDelta(ch,O.chans,ip,32), s0); */
+}
+
+#if 0
+static void ImaMashChannel(int ch, const SAMPL *ip, int n, int *st)
+{
+ int s,snext,d;
+ int s0,d0;
+ int s32,d32;
+ int sho = 0;
+ int mx[ISSTMAX+1];
+
+#if 0
+ s32=-1; d32=0x10000;
+ for (s=3; s<=ISSTMAX; s += 9) {
+ snext = s;
+ d = ImaMashS(ch, O.chans, ip[0], ip,n,&snext, NULL, sho);
+ if (d<d32) {
+ d32=d; s32=s;
+ }
+ }
+#endif
+ s0=-1; d0=0x10000;
+ for (s=0; s<=ISSTMAX;s++) {
+ snext = s;
+ mx[s] = d = ImaMashS(ch, O.chans, ip[0], ip,n,&snext, NULL, sho);
+ /* if (s==s32) d32 = d; */
+ if (d<d0) {
+ d0=d; s0=s;
+ }
+ }
+ s32 = *st;
+ d32 = mx[s32];
+#if 1
+ {
+ int low,hi,w;
+ int low0,hi0;
+ w = 0;
+ low=hi=s32;
+ d32 = mx[s32];
+ low0 = low-9; if (low0<0) low0=0;
+ hi0 = hi+9; if (hi0>ISSTMAX) hi0=ISSTMAX;
+ while (low>low0 || hi<hi0) {
+ if (!w && low>low0) {
+ if (mx[--low]<d32) {
+ d32=mx[low]; s32=low;
+ low0 = low-9; if (low0<0) low0=0;
+ hi0 = low+9; if (hi0>ISSTMAX) hi0=ISSTMAX;
+ }
+ }
+ if (w && hi<hi0) {
+ if (mx[++hi]<d32) {
+ d32=mx[hi]; s32=hi;
+ low0 = hi-9; if (low0<0) low0=0;
+ hi0 = hi+9; if (hi0>ISSTMAX) hi0=ISSTMAX;
+ }
+ }
+ w=1-w;
+ }
+ }
+#endif
+ *st = s0;
+ d = ImaMashS(ch, O.chans, ip[0], ip,n,st, O.packet, 0);
+ printf("%4d %6d %6d\n", s0-s32, d0, d32-d0);
+ /* printf("%5d %2d\n", AvgDelta(ch,O.chans,ip,32), s0); */
+}
+#endif
+
--- /dev/null
+++ b/src/ima_rw.h
@@ -1,0 +1,66 @@
+/*
+ ima_rw.h -- codex utilities for WAV_FORMAT_IMA_ADPCM
+
+ Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+#ifndef SAMPL
+# define SAMPL short
+#endif
+
+/* #undef STRICT_IMA makes code a bit faster, but not
+ * strictly compatible with the real IMA spec, which
+ * defines an encoding on approximate multiply/divide
+ * using shift/adds instead of the (now) faster mult/div
+ * Probably the roundof inexactness is inaudibly small,
+ * so you might consider it worthwhile to #undef STRICT_IMA
+ */
+#define STRICT_IMA
+/* #undef STRICT_IMA */
+
+/*
+ * call initImaTable() before any other Ima* functions,
+ * to create the fast lookup tables
+ */
+extern void initImaTable(void);
+
+/* ImaBlockExpandI() outputs interleaved samples into one output buffer */
+extern void ImaBlockExpandI(
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL *obuff, /* output samples, n*chans */
+ int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
+);
+
+/* ImaBlockExpandM() outputs non-interleaved samples into chan separate output buffers */
+extern void ImaBlockExpandM(
+ int chans, /* total channels */
+ const u_char *ibuff,/* input buffer[blockAlign] */
+ SAMPL **obuffs, /* chan output sample buffers, each takes n samples */
+ int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
+);
+
+extern void ImaMashChannel(
+ int ch, /* channel number to encode, REQUIRE 0 <= ch < chans */
+ int chans, /* total channels */
+ const SAMPL *ip, /* ip[] is interleaved input samples */
+ int n, /* samples to encode PER channel, REQUIRE n % 8 == 1 */
+ int *st, /* input/output state, REQUIRE 0 <= *st <= ISSTMAX */
+ u_char *obuff, /* output buffer[blockAlign] */
+ int opt /* non-zero allows some cpu-intensive code to improve output */
+);
+
--- a/src/misc.c
+++ b/src/misc.c
@@ -15,6 +15,7 @@
#include "version.h"
#include "patchlvl.h"
#include <stdio.h>
+#include <stdlib.h>
#include <time.h>
char *sizes[] = {
@@ -170,8 +171,6 @@
return ((us >> 8) | (us << 8)) & 0xffff;
}
-#endif
-
ULONG
swapl(ul)
ULONG ul;
@@ -192,6 +191,8 @@
u.l= (u.l>>24) | ((u.l>>8)&0xff00) | ((u.l<<8)&0xff0000L) | (u.l<<24);
return u.f;
}
+
+#endif
double
swapd(df)
--- a/src/polyphas.c
+++ b/src/polyphas.c
@@ -2,19 +2,25 @@
* July 14, 1998
* Copyright 1998 K. Bradley, Carnegie Mellon University
*
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Lance Norskog And Sundry Contributors are not responsible for
- * the consequences of using this software.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
*/
/*
* October 29, 1999
* Various changes, bugfixes, speedups, by Stan Brooks.
- *
- * This source code 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.
*
*/
--- a/src/raw.c
+++ b/src/raw.c
@@ -33,6 +33,8 @@
#define memmove(dest, src, len) bcopy((src), (dest), (len))
#endif
+#define MAXWSPEED 1
+
void rawstartread(ft)
ft_t ft;
{
@@ -127,6 +129,24 @@
swapn(p,n);
}
+static int blockr_sw(p0, n, ft)
+LONG *p0,n;
+ft_t ft;
+{
+ LONG x, done;
+ short s;
+
+ for (done=0; done < n;) {
+ blockr(&s, sizeof(short), ft);
+ x = s;
+ if (ft->file.eof) break;
+ /* scale signed up to long's range */
+ *p0++ = LEFT(x, 16);
+ done++;
+ }
+ return done;
+}
+
LONG rawread(ft, buf, nsamp)
ft_t ft;
LONG *buf, nsamp;
@@ -189,28 +209,19 @@
switch(ft->info.style)
{
case SIGN2:
- while(done < nsamp) {
- short s;
- blockr(&s, sizeof(short), ft);
- datum = s;
- if (ft->file.eof)
- return done;
- /* scale signed up to long's range */
- *buf++ = LEFT(datum, 16);
- done++;
- }
- return done;
+ return blockr_sw(buf, nsamp, ft);
case UNSIGNED:
while(done < nsamp) {
+ LONG x;
unsigned short s;
blockr(&s, sizeof(short), ft);
- datum = s;
+ x = s;
if (ft->file.eof)
return done;
/* Convert to signed */
- datum ^= 0x8000;
+ x ^= 0x8000;
/* scale signed up to long's range */
- *buf++ = LEFT(datum, 16);
+ *buf++ = LEFT(x, 16);
done++;
}
return done;
@@ -296,6 +307,57 @@
ft->file.pos += n;
}
+static void blockw_sw(ft, buf, nsamp)
+ft_t ft;
+LONG *buf, nsamp;
+{
+ short *top;
+
+ top = (short*)(ft->file.buf + ft->file.size);
+ while (nsamp) {
+ short *p, *q;
+ p = (short*)(ft->file.buf + ft->file.pos);
+ if (p >= top) {
+ blockflush(ft);
+ continue;
+ }
+ q = p+nsamp; if (q>top) q = top;
+ ft->file.pos += (q-p)*sizeof(short);
+ nsamp -= (q-p);
+ if (ft->swap) {
+# ifdef MAXWSPEED
+ q -= 4;
+ while (p<q) {
+ p[0] = swapw((buf[0] + 0x8000) >> 16); /* round for 16 bit */
+ p[1] = swapw((buf[1] + 0x8000) >> 16); /* round for 16 bit */
+ p[2] = swapw((buf[2] + 0x8000) >> 16); /* round for 16 bit */
+ p[3] = swapw((buf[3] + 0x8000) >> 16); /* round for 16 bit */
+ p += 4; buf += 4;
+ }
+ q += 4;
+# endif
+ while (p<q) {
+ *p++ = swapw(((*buf++) + 0x8000) >> 16); /* round for 16 bit */
+ }
+ } else {
+# ifdef MAXWSPEED
+ q -= 4;
+ while (p<q) {
+ p[0] = (buf[0] + 0x8000) >> 16; /* round for 16 bit */
+ p[1] = (buf[1] + 0x8000) >> 16; /* round for 16 bit */
+ p[2] = (buf[2] + 0x8000) >> 16; /* round for 16 bit */
+ p[3] = (buf[3] + 0x8000) >> 16; /* round for 16 bit */
+ p += 4; buf += 4;
+ }
+ q += 4;
+# endif
+ while (p<q) {
+ *p++ = ((*buf++) + 0x8000) >> 16; /* round for 16 bit */
+ }
+ }
+ }
+}
+
/* Convert the sox internal signed long format */
/* to the raw file data, and write it. */
@@ -356,14 +418,7 @@
switch(ft->info.style)
{
case SIGN2:
- while(done < nsamp) {
- short s;
- /* scale signed up to long's range */
- datum = *buf++ + 0x8000; /* round for 16 bit */
- s = RIGHT(datum, 16);
- blockw(&s, sizeof(short),ft);
- done++;
- }
+ blockw_sw(ft,buf,nsamp);
return;
case UNSIGNED:
while(done < nsamp) {
--- a/src/resample.c
+++ b/src/resample.c
@@ -48,12 +48,14 @@
/* resample includes */
#include "resampl.h"
+/* this Float MUST match that in filter.c */
#define Float double/*float*/
#define ISCALE 0x10000
+/* largest factor for which exact-coefficients upsampling will be used */
#define NQMAX 511
-#define BUFFSIZE 8192 /*16384*/ /* Total buffer size */
-#define L64 long long
+
+#define BUFFSIZE 8192 /*16384*/ /* Total I/O buffer size */
/* Private data for Lerp via LCM file */
typedef struct resamplestuff {
--- a/src/sox.c
+++ b/src/sox.c
@@ -234,7 +234,13 @@
if (ft->filetype[0] == '.')
ft->filetype++;
break;
-
+#if 0
+ case 'X': /* extract a subinterval of samples as input */
+ if (! ft) usage("-X");
+ ft->fileextract++;
+ ft->filetype = optarg;
+ break;
+#endif
case 'r':
if (! ft) usage("-r");
str = optarg;
--- a/src/stat.c
+++ b/src/stat.c
@@ -22,12 +22,14 @@
/* Private data for STAT effect */
typedef struct statstuff {
- double min, max, asum, sum1, sum2; /* amplitudes */
- double dmin, dmax, dsum1, dsum2; /* deltas */
- double scale; /* scale-factor */
- double last; /* previous sample */
- double read;
- int first;
+ double min, max;
+ double asum;
+ double sum1, sum2; /* amplitudes */
+ double dmin, dmax;
+ double dsum1, dsum2; /* deltas */
+ double scale; /* scale-factor */
+ double last; /* previous sample */
+ ULONG read; /* samples processed */
int volume;
int srms;
ULONG bin[4];
@@ -94,7 +96,6 @@
stat_t stat = (stat_t) effp->priv;
int i;
- stat->first = 1;
stat->min = stat->max = 0;
stat->asum = 0;
stat->sum1 = stat->sum2 = 0;
@@ -102,6 +103,7 @@
stat->dmin = stat->dmax = 0;
stat->dsum1 = stat->dsum2 = 0;
+ stat->last = 0;
stat->read = 0;
for (i = 0; i < 4; i++)
@@ -121,16 +123,23 @@
{
stat_t stat = (stat_t) effp->priv;
int len, done;
- double samp, delta;
short count;
count = 0;
len = ((*isamp > *osamp) ? *osamp : *isamp);
+ if (len==0) return;
+
+ if (stat->read == 0) /* 1st sample */
+ stat->min = stat->max = stat->last = (*ibuf)/stat->scale;
+
for(done = 0; done < len; done++) {
- /* work in absolute levels for both sample and delta */
- samp = (*ibuf)/stat->scale;
- stat->bin[RIGHT(*ibuf,30)+2]++;
- *obuf++ = *ibuf++;
+ long lsamp;
+ double samp, delta;
+ /* work in scaled levels for both sample and delta */
+ lsamp = *ibuf++;
+ samp = (double)lsamp/stat->scale;
+ stat->bin[RIGHT(lsamp,30)+2]++;
+ *obuf++ = lsamp;
if (stat->volume == 2)
{
@@ -137,16 +146,12 @@
fprintf(stderr,"%f ",samp);
if (count++ == 5)
{
- fprintf(stderr,"\n");
- count = 0;
+ fprintf(stderr,"\n");
+ count = 0;
}
}
-
- if (stat->first) {
- stat->min = stat->max = samp;
- stat->first = 0;
- }
+ /* update min/max */
if (stat->min > samp)
stat->min = samp;
else if (stat->max < samp)
@@ -180,15 +185,15 @@
eff_t effp;
{
stat_t stat = (stat_t) effp->priv;
- double amp, scale, srms = 0, freq;
+ double amp, scale, rms = 0, freq;
double x, ct;
ct = stat->read;
- if (stat->srms) {
+ if (stat->srms) { /* adjust results to units of rms */
double f;
- srms = sqrt(stat->sum2/ct);
- f = 1.0/srms;
+ rms = sqrt(stat->sum2/ct);
+ f = 1.0/rms;
stat->max *= f;
stat->min *= f;
stat->asum *= f;
@@ -198,7 +203,7 @@
stat->dmin *= f;
stat->dsum1 *= f;
stat->dsum2 *= f*f;
- stat->scale *= srms;
+ stat->scale *= rms;
}
scale = stat->scale;
@@ -215,10 +220,11 @@
if (stat->volume == 2) {
fprintf(stderr, "\n");
}
- /* print them out */
- fprintf(stderr, "Samples read: %12lu\n", (unsigned long)ct);
+ /* print out the info */
+ fprintf(stderr, "Samples read: %12lu\n", stat->read);
+ fprintf(stderr, "Length (seconds): %12.6f\n", (double)stat->read/effp->ininfo.rate);
if (stat->srms)
- fprintf(stderr, "Scaled by rms: %12.6f\n", srms);
+ fprintf(stderr, "Scaled by rms: %12.6f\n", rms);
else
fprintf(stderr, "Scaled by: %12.1f\n", scale);
fprintf(stderr, "Maximum amplitude: %12.6f\n", stat->max);
@@ -229,8 +235,8 @@
fprintf(stderr, "Maximum delta: %12.6f\n", stat->dmax);
fprintf(stderr, "Minimum delta: %12.6f\n", stat->dmin);
- fprintf(stderr, "Mean delta: %12.6f\n", stat->dsum1/ct);
- fprintf(stderr, "RMS delta: %12.6f\n", sqrt(stat->dsum2/ct));
+ fprintf(stderr, "Mean delta: %12.6f\n", stat->dsum1/(ct-1));
+ fprintf(stderr, "RMS delta: %12.6f\n", sqrt(stat->dsum2/(ct-1)));
freq = sqrt(stat->dsum2/stat->sum2)*effp->ininfo.rate/(M_PI*2);
fprintf(stderr, "Rough frequency: %12d\n", (int)freq);
--- a/src/wav.c
+++ b/src/wav.c
@@ -8,6 +8,13 @@
*
* Change History:
*
+ * November 11, 1999 - Stan Brooks (stabro@megsinet.com)
+ * Mods for faster adpcm decoding and addition of ima_adpcm
+ * writing... low-level codex functions moved to external
+ * modules ima_rw.c and adpcm.c. Some general cleanup, consistent
+ * with writing *adpcm and other output formats.
+ * Headers for adpcm include the 'fact' subchunk.
+ *
* September 11, 1998 - Chris Bagwell (cbagwell@sprynet.com)
* Fixed length bug for IMA and MS ADPCM files.
*
@@ -70,11 +77,13 @@
#include "st.h"
#include "wav.h"
+#include "ima_rw.h"
+#include "adpcm.h"
/* Private data for .wav file */
typedef struct wavstuff {
LONG numSamples;
- int second_header; /* non-zero on second header write */
+ LONG dataLength; /* needed for ADPCM writing */
unsigned short formatTag; /* What type of encoding file is using */
/* The following are only needed for ADPCM wav files */
@@ -81,128 +90,39 @@
unsigned short samplesPerBlock;
unsigned short bytesPerBlock;
unsigned short blockAlign;
- short *samples[2]; /* Left and Right sample buffers */
- short *samplePtr[2]; /* Pointers to current samples */
- unsigned short blockSamplesRemaining;/* Samples remaining in each channel */
+ unsigned short nCoefs; /* ADPCM: number of coef sets */
+ short *iCoefs; /* ADPCM: coef sets */
unsigned char *packet; /* Temporary buffer for packets */
+ short *samples; /* interleaved samples buffer */
+ short *samplePtr; /* Pointer to current samples */
+ short *sampleTop; /* End of samples-buffer */
+ unsigned short blockSamplesRemaining;/* Samples remaining in each channel */
+ int state[16]; /* last, because maybe longer */
} *wav_t;
static char *wav_format_str();
-void wavwritehdr(P1(ft_t));
+LONG rawread(P3(ft_t, LONG *, LONG));
+void rawwrite(P3(ft_t, LONG *, LONG));
+void wavwritehdr(P2(ft_t, int));
-
-/*
- *
- * Lookup tables for MS ADPCM format
- *
- */
-
-static LONG gaiP4[] = { 230, 230, 230, 230, 307, 409, 512, 614,
- 768, 614, 512, 409, 307, 230, 230, 230 };
-
-/* TODO : The first 7 coef's are are always hardcode and must
- appear in the actual WAVE file. They should be read in
- in case a sound program added extras to the list. */
-
-static LONG gaiCoef1[] = { 256, 512, 0, 192, 240, 460, 392 };
-static LONG gaiCoef2[] = { 0, -256, 0, 64, 0,-208, -232};
-
-/*
- *
- * Lookup tables for IMA ADPCM format
- *
- */
-static int imaIndexAdjustTable[16] = {
- -1, -1, -1, -1, /* +0 - +3, decrease the step size */
- 2, 4, 6, 8, /* +4 - +7, increase the step size */
- -1, -1, -1, -1, /* -0 - -3, decrease the step size */
- 2, 4, 6, 8, /* -4 - -7, increase the step size */
-};
-
-static int imaStepSizeTable[89] = {
- 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34,
- 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
- 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494,
- 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
- 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026,
- 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
- 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623,
- 27086, 29794, 32767
-};
-
/****************************************************************************/
/* IMA ADPCM Support Functions Section */
/****************************************************************************/
-
-/*
- *
- * MsAdpcmDecode - Decode a given sample and update state tables
- *
- */
-
-short ImaAdpcmDecode(deltaCode, state)
-unsigned char deltaCode;
-ImaState_t *state;
-{
- /* Get the current step size */
- int step;
- int difference;
-
- step = imaStepSizeTable[state->index];
-
- /* Construct the difference by scaling the current step size */
- /* This is approximately: difference = (deltaCode+.5)*step/4 */
- difference = step>>3;
- if ( deltaCode & 1 ) difference += step>>2;
- if ( deltaCode & 2 ) difference += step>>1;
- if ( deltaCode & 4 ) difference += step;
-
- if ( deltaCode & 8 ) difference = -difference;
-
- /* Build the new sample */
- state->previousValue += difference;
-
- if (state->previousValue > 32767) state->previousValue = 32767;
- else if (state->previousValue < -32768) state->previousValue = -32768;
-
- /* Update the step for the next sample */
- state->index += imaIndexAdjustTable[deltaCode];
- if (state->index < 0) state->index = 0;
- else if (state->index > 88) state->index = 88;
-
- return state->previousValue;
-
-}
-
-/*
- *
- * ImaAdpcmNextBlock - Grab and decode complete block of samples
- *
- */
-unsigned short ImaAdpcmNextBlock(ft)
+unsigned short ImaAdpcmReadBlock(ft)
ft_t ft;
{
wav_t wav = (wav_t) ft->priv;
-
- /* Pull in the packet and check the header */
- unsigned short bytesRead;
- unsigned char *bytePtr;
+ int bytesRead;
+ int samplesThisBlock;
- ImaState_t state[2]; /* One decompressor state for each channel */
- int ch;
- unsigned short remaining;
- unsigned short samplesThisBlock;
-
- int i;
- unsigned char b;
-
+ /* Pull in the packet and check the header */
bytesRead = fread(wav->packet,1,wav->blockAlign,ft->fp);
if (bytesRead < wav->blockAlign)
{
/* If it looks like a valid header is around then try and */
/* work with partial blocks. Specs say it should be null */
- /* padded but I guess this is better then trailing quite. */
+ /* padded but I guess this is better then trailing quiet. */
if (bytesRead >= (4 * ft->info.channels))
{
samplesThisBlock = (wav->blockAlign - (3 * ft->info.channels));
@@ -216,123 +136,58 @@
else
samplesThisBlock = wav->samplesPerBlock;
- bytePtr = wav->packet;
+ wav->samplePtr = wav->samples;
+
- /* Read the four-byte header for each channel */
+ /* For a full block, the following should be true: */
+ /* wav->samplesPerBlock = blockAlign - 8byte header + 1 sample in header */
+ ImaBlockExpandI(ft->info.channels, wav->packet, wav->samples, samplesThisBlock);
+ return samplesThisBlock;
- /* Reset the decompressor */
- for(ch=0;ch < ft->info.channels; ch++) {
-
- /* Got this from xanim */
+}
- state[ch].previousValue = ((int)bytePtr[1]<<8) +
- (int)bytePtr[0];
- if (state[ch].previousValue & 0x8000)
- state[ch].previousValue -= 0x10000;
+static void ImaAdpcmWriteBlock(ft)
+ft_t ft;
+{
+ wav_t wav = (wav_t) ft->priv;
+ int chans, ch, ct;
+ short *p;
- if (bytePtr[2] > 88)
- {
- warn("IMA ADPCM Format Error (bad index value) in wav file");
- state[ch].index = 88;
- }
- else
- state[ch].index = bytePtr[2];
-
- if (bytePtr[3])
- warn("IMA ADPCM Format Error (synchronization error) in wav file");
-
- bytePtr+=4; /* Skip this header */
+ chans = ft->info.channels;
+ p = wav->samplePtr;
+ ct = p - wav->samples;
+ if (ct>=chans) {
+ /* zero-fill samples if needed to complete block */
+ for (p = wav->samplePtr; p < wav->sampleTop; p++) *p=0;
+ /* compress the samples to wav->packet */
+ for (ch=0; ch<chans; ch++)
+ ImaMashChannel(ch, chans, wav->samples, wav->samplesPerBlock, &wav->state[ch], wav->packet, 9);
- wav->samplePtr[ch] = wav->samples[ch];
- /* Decode one sample for the header */
- *(wav->samplePtr[ch]++) = state[ch].previousValue;
+ /* write the compressed packet */
+ fwrite(wav->packet, wav->blockAlign, 1, ft->fp); /* FIXME: check return value */
+ /* update lengths and samplePtr */
+ wav->dataLength += wav->blockAlign;
+ wav->numSamples += ct/chans;
+ wav->samplePtr = wav->samples;
}
+}
- /* Decompress nybbles. Remainging is bytes in block minus header */
- /* Subtract the one sample taken from header */
- remaining = samplesThisBlock-1;
-
- while (remaining) {
- /* Always decode 8 samples */
- remaining -= 8;
- /* Decode 8 left samples */
- for (i=0;i<4;i++) {
- b = *bytePtr++;
- *(wav->samplePtr[0]++) = ImaAdpcmDecode(b & 0x0f,&state[0]);
- *(wav->samplePtr[0]++) = ImaAdpcmDecode((b>>4) & 0x0f,&state[0]);
- }
- if (ft->info.channels < 2)
- continue; /* If mono, skip rest of loop */
- /* Decode 8 right samples */
- for (i=0;i<4;i++) {
- b = *bytePtr++;
- *(wav->samplePtr[1]++) = ImaAdpcmDecode(b & 0x0f,&state[1]);
- *(wav->samplePtr[1]++) = ImaAdpcmDecode((b>>4) & 0x0f,&state[1]);
- }
- }
- /* For a full block, the following should be true: */
- /* wav->samplesPerBlock = blockAlign - 8byte header + 1 sample in header */
- return wav->samplesPerBlock;
-}
-
/****************************************************************************/
/* MS ADPCM Support Functions Section */
/****************************************************************************/
-
/*
*
- * MsAdpcmDecode - Decode a given sample and update state tables
+ * MsAdpcmReadBlock - Grab and decode complete block of samples
*
*/
-
-LONG MsAdpcmDecode(deltaCode, state)
-LONG deltaCode;
-MsState_t *state;
-{
- LONG predict;
- LONG sample;
- LONG idelta;
-
- /** Compute next Adaptive Scale Factor (ASF) **/
- idelta = state->index;
- state->index = (gaiP4[deltaCode] * idelta) >> 8;
- if (state->index < 16) state->index = 16;
- if (deltaCode & 0x08) deltaCode = deltaCode - 0x10;
-
- /** Predict next sample **/
- predict = ((state->sample1 * gaiCoef1[state->bpred]) + (state->sample2 * gaiCoef2[state->bpred])) >> 8;
- /** reconstruct original PCM **/
- sample = (deltaCode * idelta) + predict;
-
- if (sample > 32767) sample = 32767;
- else if (sample < -32768) sample = -32768;
-
- state->sample2 = state->sample1;
- state->sample1 = sample;
-
- return (sample);
-}
-
-
-/*
- *
- * MsAdpcmNextBlock - Grab and decode complete block of samples
- *
- */
-unsigned short MsAdpcmNextBlock(ft)
+unsigned short MsAdpcmReadBlock(ft)
ft_t ft;
{
wav_t wav = (wav_t) ft->priv;
-
- unsigned short bytesRead;
- unsigned char *bytePtr;
+ int bytesRead;
+ int samplesThisBlock;
+ const char *errmsg;
- MsState_t state[2]; /* One decompressor state for each channel */
- unsigned short samplesThisBlock;
- unsigned short remaining;
-
- unsigned char b;
-
/* Pull in the packet and check the header */
bytesRead = fread(wav->packet,1,wav->blockAlign,ft->fp);
if (bytesRead < wav->blockAlign)
@@ -353,82 +208,11 @@
else
samplesThisBlock = wav->samplesPerBlock;
- bytePtr = wav->packet;
+ errmsg = AdpcmBlockExpandI(ft->info.channels, wav->packet, wav->samples, samplesThisBlock);
- /* Read the four-byte header for each channel */
+ if (errmsg)
+ warn((char*)errmsg);
- /* Reset the decompressor */
- state[0].bpred = *bytePtr++; /* Left */
- if (ft->info.channels > 1)
- state[1].bpred = *bytePtr++; /* Right */
- else
- state[1].bpred = 0;
-
- /* 7 should be variable from AVI/WAV header */
- if (state[0].bpred >= 7)
- {
- warn("MSADPCM bpred %x and should be less than 7\n",state[0].bpred);
- return(0);
- }
- if (state[1].bpred >= 7)
- {
- warn("MSADPCM bpred %x and should be less than 7\n",state[1].bpred);
- return(0);
- }
-
- state[0].index = *bytePtr++; state[0].index |= (*bytePtr++)<<8;
- if (state[0].index & 0x8000) state[0].index -= 0x10000;
- if (ft->info.channels > 1)
- {
- state[1].index = *bytePtr++; state[1].index |= (*bytePtr++)<<8;
- if (state[1].index & 0x8000) state[1].index -= 0x10000;
- }
-
- state[0].sample1 = *bytePtr++; state[0].sample1 |= (*bytePtr++)<<8;
- if (state[0].sample1 & 0x8000) state[0].sample1 -= 0x10000;
- if (ft->info.channels > 1)
- {
- state[1].sample1 = *bytePtr++; state[1].sample1 |= (*bytePtr++)<<8;
- if (state[1].sample1 & 0x8000) state[1].sample1 -= 0x10000;
- }
-
- state[0].sample2 = *bytePtr++; state[0].sample2 |= (*bytePtr++)<<8;
- if (state[0].sample2 & 0x8000) state[0].sample2 -= 0x10000;
- if (ft->info.channels > 1)
- {
- state[1].sample2 = *bytePtr++; state[1].sample2 |= (*bytePtr++)<<8;
- if (state[1].sample2 & 0x8000) state[1].sample2 -= 0x10000;
- }
-
- wav->samplePtr[0] = wav->samples[0];
- wav->samplePtr[1] = wav->samples[1];
-
- /* Decode two samples for the header */
- *(wav->samplePtr[0]++) = state[0].sample2;
- *(wav->samplePtr[0]++) = state[0].sample1;
- if (ft->info.channels > 1)
- {
- *(wav->samplePtr[1]++) = state[1].sample2;
- *(wav->samplePtr[1]++) = state[1].sample1;
- }
-
- /* Decompress nybbles. Minus 2 included in header */
- remaining = samplesThisBlock-2;
-
- while (remaining) {
- b = *bytePtr++;
- *(wav->samplePtr[0]++) = MsAdpcmDecode((b>>4) & 0x0f, &state[0]);
- remaining--;
- if (ft->info.channels == 1)
- {
- *(wav->samplePtr[0]++) = MsAdpcmDecode(b & 0x0f, &state[0]);
- remaining--;
- }
- else
- {
- *(wav->samplePtr[1]++) = MsAdpcmDecode(b & 0x0f, &state[1]);
- }
- }
return samplesThisBlock;
}
@@ -458,8 +242,7 @@
ULONG wAvgBytesPerSec; /* estimate of bytes per second needed */
unsigned short wBitsPerSample; /* bits per sample */
unsigned short wExtSize = 0; /* extended field for ADPCM */
- unsigned short wNumCoefs = 0; /* Related to IMA ADPCM */
-
+
ULONG data_length; /* length of sound data in bytes */
ULONG bytespersample; /* bytes per sample (per channel */
@@ -497,7 +280,7 @@
len--;
}
}
-
+ /* we only get here if we just read the magic "fmt " */
if ( len < 16 )
fail("WAVE file fmt chunk is too short");
@@ -592,9 +375,14 @@
wBitsPerSample = rshort(ft);
len -= 2;
+ wav->iCoefs = NULL;
+ wav->packet = NULL;
+ wav->samples = NULL;
+
/* ADPCM formats have extended fmt chunk. Check for those cases. */
- if (wav->formatTag == WAVE_FORMAT_ADPCM)
+ switch (wav->formatTag)
{
+ case WAVE_FORMAT_ADPCM:
if (wBitsPerSample != 4)
fail("Can only handle 4-bit MS ADPCM in wav files");
@@ -601,19 +389,30 @@
wExtSize = rshort(ft);
wav->samplesPerBlock = rshort(ft);
wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;
- wNumCoefs = rshort(ft);
+ wav->nCoefs = rshort(ft);
+ if (wav->nCoefs < 7 || wav->nCoefs > 0x100) {
+ fail("ADPCM file nCoefs (%.4hx) makes no sense\n", wav->nCoefs);
+ }
wav->packet = (unsigned char *)malloc(wav->blockAlign);
len -= 6;
- wav->samples[1] = wav->samples[0] = 0;
- /* Use ft->info.channels after this becuase wChannels is now bad */
- while (wChannels-- > 0)
- wav->samples[wChannels] = (short *)malloc(wav->samplesPerBlock*sizeof(short));
- /* Here we are setting the bytespersample AFTER de-compression */
- bytespersample = WORD;
- }
- else if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
- {
+ wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
+
+ /* SJB: will need iCoefs later for adpcm.c */
+ wav->iCoefs = (short *)malloc(wav->nCoefs * 2 * sizeof(short));
+ {
+ int i;
+ for (i=0; len>=2 && i < 2*wav->nCoefs; i++) {
+ wav->iCoefs[i] = rshort(ft);
+ /* fprintf(stderr,"iCoefs[%2d] %4d\n",i,wav->iCoefs[i]); */
+ len -= 2;
+ }
+ }
+
+ bytespersample = WORD; /* AFTER de-compression */
+ break;
+
+ case WAVE_FORMAT_IMA_ADPCM:
if (wBitsPerSample != 4)
fail("Can only handle 4-bit IMA ADPCM in wav files");
@@ -622,17 +421,14 @@
wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;
wav->packet = (unsigned char *)malloc(wav->blockAlign);
len -= 4;
-
- wav->samples[1] = wav->samples[0] = 0;
- /* Use ft->info.channels after this becuase wChannels is now bad */
- while (wChannels-- > 0)
- wav->samples[wChannels] = (short *)malloc(wav->samplesPerBlock*sizeof(short));
- /* Here we are setting the bytespersample AFTER de-compression */
- bytespersample = WORD;
- }
- else
- {
+
+ wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
+
+ bytespersample = WORD; /* AFTER de-compression */
+ break;
+ default:
bytespersample = (wBitsPerSample + 7)/8;
+
}
switch (bytespersample)
@@ -683,6 +479,9 @@
len--;
}
+ /* for ADPCM formats, there's a useless(?) 'fact' chunk before
+ * the upcoming 'data' chunk */
+
/* Now look for the wave data chunk */
for (;;)
{
@@ -700,20 +499,23 @@
}
data_length = len;
- if (wav->formatTag == WAVE_FORMAT_ADPCM)
+
+ switch (wav->formatTag)
{
+
+ case WAVE_FORMAT_ADPCM:
/* Compute easiest part of number of samples. For every block, there
are samplesPerBlock samples to read. */
wav->numSamples = (((data_length / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
- /* Next, for any partial blocks, substract overhead from it and it
+ /* Next, for any partial blocks, subtract overhead from it and it
will leave # of samples to read. */
wav->numSamples += ((data_length - ((data_length/wav->blockAlign)
*wav->blockAlign))
- (6 * ft->info.channels)) * ft->info.channels;
wav->blockSamplesRemaining = 0; /* Samples left in buffer */
- }
- else if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
- {
+ break;
+
+ case WAVE_FORMAT_IMA_ADPCM:
/* Compute easiest part of number of samples. For every block, there
are samplesPerBlock samples to read. */
wav->numSamples = (((data_length / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
@@ -723,10 +525,14 @@
*wav->blockAlign))
- (3 * ft->info.channels)) * ft->info.channels;
wav->blockSamplesRemaining = 0; /* Samples left in buffer */
- }
- else
+ initImaTable();
+ break;
+
+ default:
wav->numSamples = data_length/ft->info.size; /* total samples */
+ }
+
report("Reading Wave file: %s format, %d channel%s, %d samp/sec",
wav_format_str(wav->formatTag), ft->info.channels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
@@ -733,11 +539,13 @@
report(" %d byte/sec, %d block align, %d bits/samp, %u data bytes",
wAvgBytesPerSec, wav->blockAlign, wBitsPerSample, data_length);
- /* Can also report exteded fmt information */
+ /* Can also report extended fmt information */
if (wav->formatTag == WAVE_FORMAT_ADPCM)
- report(" %d Extsize, %d Samps/block, %d bytes/block %d Num Coefs\n",wExtSize,wav->samplesPerBlock,wav->bytesPerBlock,wNumCoefs);
+ report(" %d Extsize, %d Samps/block, %d bytes/block %d Num Coefs\n",
+ wExtSize,wav->samplesPerBlock,wav->bytesPerBlock,wav->nCoefs);
else if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
- report(" %d Extsize, %d Samps/block, %d bytes/block\n",wExtSize,wav->samplesPerBlock,wav->bytesPerBlock);
+ report(" %d Extsize, %d Samps/block, %d bytes/block\n",
+ wExtSize,wav->samplesPerBlock,wav->bytesPerBlock);
}
/*
@@ -758,16 +566,17 @@
/* If file is in ADPCM style then read in multiple blocks else */
/* read as much as possible and return quickly. */
- if (ft->info.style == ADPCM)
+ switch (ft->info.style)
{
+ case ADPCM:
done = 0;
while (done < len) { /* Still want data? */
/* See if need to read more from disk */
if (wav->blockSamplesRemaining == 0) {
if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
- wav->blockSamplesRemaining = ImaAdpcmNextBlock(ft);
+ wav->blockSamplesRemaining = ImaAdpcmReadBlock(ft);
else
- wav->blockSamplesRemaining = MsAdpcmNextBlock(ft);
+ wav->blockSamplesRemaining = MsAdpcmReadBlock(ft);
if (wav->blockSamplesRemaining == 0)
{
/* Don't try to read any more samples */
@@ -774,43 +583,40 @@
wav->numSamples = 0;
return done;
}
- wav->samplePtr[0] = wav->samples[0];
- wav->samplePtr[1] = wav->samples[1];
+ wav->blockSamplesRemaining *= ft->info.channels;
+ wav->samplePtr = wav->samples;
}
- switch(ft->info.channels) { /* Copy data into buf */
- case 1: /* Mono: Just copy left channel data */
- while ((wav->blockSamplesRemaining > 0) && (done < len))
+ /* Copy interleaved data into buf, converting short to LONG */
+ {
+ short *p, *top;
+ int ct;
+ ct = len-done;
+ if (ct > wav->blockSamplesRemaining)
+ ct = wav->blockSamplesRemaining;
+
+ done += ct;
+ wav->blockSamplesRemaining -= ct;
+ p = wav->samplePtr;
+ top = p+ct;
+ /* Output is already signed */
+ while (p<top)
{
- /* Output is already signed */
- *buf++ = LEFT(*(wav->samplePtr[0]++), 16);
- done++;
- wav->blockSamplesRemaining--;
+ *buf++ = LEFT((*p++), 16);
}
- break;
- case 2: /* Stereo: Interleave samples */
- while ((wav->blockSamplesRemaining > 0) && (done < len))
- {
- /* Output is already signed */
- *buf++ = LEFT(*(wav->samplePtr[0]++),16); /* Left */
- *buf++ = LEFT(*(wav->samplePtr[1]++),16); /* Right */
- done += 2;
- wav->blockSamplesRemaining--;
- }
- break;
- default:
- fail ("Can only handle stereo or mono files");
+ wav->samplePtr = p;
}
}
- }
- else /* else not ADPCM style */
- {
+ break;
+
+ default: /* not ADPCM style */
done = rawread(ft, buf, len);
/* If software thinks there are more samples but I/O */
- /* says otherwise, let the user no about this. */
+ /* says otherwise, let the user know about this. */
if (done == 0 && wav->numSamples != 0)
warn("Premature EOF on .wav input file");
}
+
wav->numSamples -= done;
return done;
}
@@ -825,8 +631,8 @@
wav_t wav = (wav_t) ft->priv;
if (wav->packet) free(wav->packet);
- if (wav->samples[0]) free(wav->samples[0]);
- if (wav->samples[1]) free(wav->samples[1]);
+ if (wav->samples) free(wav->samples);
+ if (wav->iCoefs) free(wav->iCoefs);
/* Needed for rawread() */
rawstopread(ft);
@@ -843,22 +649,43 @@
if (!*endptr) ft->swap = ft->swap ? 0 : 1;
wav->numSamples = 0;
- wav->second_header = 0;
- if (! ft->seekable)
- warn("Length in output .wav header will wrong since can't seek to fix it");
- wavwritehdr(ft);
+ wav->dataLength = 0;
+ if (!ft->seekable)
+ warn("Length in output .wav header will be wrong since can't seek to fix it");
+ wavwritehdr(ft, 0); /* also calculates various wav->* info */
+ wav->packet = NULL;
+ wav->samples = NULL;
+ wav->iCoefs = NULL;
+ if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM) {
+ int ch, sbsize;
+ /* #channels already range-checked for overflow in wavwritehdr() */
+ for (ch=0; ch<ft->info.channels; ch++)
+ wav->state[ch] = 0;
+ sbsize = ft->info.channels * wav->samplesPerBlock;
+ wav->packet = (unsigned char *)malloc(wav->blockAlign);
+ wav->samples = (short *)malloc(sbsize*sizeof(short));
+ wav->sampleTop = wav->samples + sbsize;
+ wav->samplePtr = wav->samples;
+ initImaTable();
+ }
}
-void wavwritehdr(ft)
+#define WHDRSIZ1 8 /* "RIFF",(long)len,"WAVE" */
+#define FMTSIZ1 24 /* "fmt ",(long)len,... fmt chunk (without any Ext data) */
+#define DATASIZ1 8 /* "data",(long)len */
+void wavwritehdr(ft, second_header)
ft_t ft;
+int second_header;
{
wav_t wav = (wav_t) ft->priv;
+ LONG fmtsize = FMTSIZ1;
+ LONG factsize = 0; /* "fact",(long)len,??? */
/* wave file characteristics */
unsigned short wFormatTag = 0; /* data format */
unsigned short wChannels; /* number of channels */
ULONG wSamplesPerSecond; /* samples per second per channel */
- ULONG wAvgBytesPerSec; /* estimate of bytes per second needed */
+ ULONG wAvgBytesPerSec; /* estimate of bytes per second needed */
unsigned short wBlockAlign; /* byte alignment of a basic sample block */
unsigned short wBitsPerSample; /* bits per sample */
ULONG data_length; /* length of sound data in bytes */
@@ -865,7 +692,8 @@
ULONG bytespersample; /* bytes per sample (per channel) */
/* Needed for rawwrite() */
- rawstartwrite(ft);
+ if (ft->info.style != ADPCM)
+ rawstartwrite(ft);
switch (ft->info.size)
{
@@ -874,7 +702,7 @@
if (ft->info.style != UNSIGNED &&
ft->info.style != ULAW &&
ft->info.style != ALAW &&
- !wav->second_header)
+ !second_header)
{
warn("Only support unsigned, ulaw, or alaw with 8-bit data. Forcing to unsigned");
ft->info.style = UNSIGNED;
@@ -885,7 +713,7 @@
if ((ft->info.style == UNSIGNED ||
ft->info.style == ULAW ||
ft->info.style == ALAW) &&
- !wav->second_header)
+ !second_header)
{
warn("Do not support Unsigned, ulaw, or alaw with 16 bit data. Forcing to Signed");
ft->info.style = SIGN2;
@@ -914,28 +742,34 @@
wFormatTag = WAVE_FORMAT_MULAW;
break;
case ADPCM:
- wFormatTag = WAVE_FORMAT_PCM;
- warn("Can not support writing ADPCM style. Overriding to Signed Words\n");
- ft->info.style = SIGN2;
- wBitsPerSample = 16;
- /* wFormatTag = WAVE_FORMAT_IMA_ADPCM;
- wBitsPerSample = 4;
- if (wBitsPerSample != 4 && !wav->second_header)
- break; */
+ warn("Experimental support writing IMA_ADPCM style.\n");
+ wFormatTag = WAVE_FORMAT_IMA_ADPCM;
+ wBitsPerSample = 4;
+ fmtsize += 4; /* plus ExtData */
+ factsize = 12; /* fact chunk */
+ break;
}
+ wav->formatTag = wFormatTag;
-
wSamplesPerSecond = ft->info.rate;
- bytespersample = (wBitsPerSample + 7)/8;
- wAvgBytesPerSec = ft->info.rate * ft->info.channels * bytespersample;
wChannels = ft->info.channels;
- wBlockAlign = ft->info.channels * bytespersample;
- if (!wav->second_header) /* use max length value first time */
- data_length = 0x7fffffffL - (8+16+12);
+ if (wFormatTag == WAVE_FORMAT_IMA_ADPCM) {
+ if (wChannels>16)
+ fail("Channels(%d) must be <= 16\n",wChannels);
+ bytespersample = 2;
+ wBlockAlign = wChannels * 64; /* reasonable default */
+ } else {
+ bytespersample = (wBitsPerSample + 7)/8;
+ wBlockAlign = wChannels * bytespersample;
+ }
+ wav->blockAlign = wBlockAlign;
+ wAvgBytesPerSec = ft->info.rate * wChannels * bytespersample;
+ if (!second_header) /* use max length value first time */
+ data_length = 0x7fffffffL - (WHDRSIZ1+fmtsize+factsize+DATASIZ1);
else /* fixup with real length */
{
if (ft->info.style == ADPCM)
- data_length = wav->numSamples / 2;
+ data_length = wav->dataLength; /* FIXME: not really */
else
data_length = bytespersample * wav->numSamples;
}
@@ -942,21 +776,36 @@
/* figured out header info, so write it */
fputs("RIFF", ft->fp);
- wlong(ft, data_length + 8+16+12); /* Waveform chunk size: FIXUP(4) */
+ wlong(ft, data_length + WHDRSIZ1+fmtsize+factsize+DATASIZ1);/* Waveform chunk size: FIXUP(4) */
fputs("WAVE", ft->fp);
fputs("fmt ", ft->fp);
- wlong(ft, (LONG)16); /* fmt chunk size */
+ wlong(ft, fmtsize-8); /* fmt chunk size */
wshort(ft, wFormatTag);
wshort(ft, wChannels);
wlong(ft, wSamplesPerSecond);
wlong(ft, wAvgBytesPerSec);
wshort(ft, wBlockAlign);
- wshort(ft, wBitsPerSample);
-
+ wshort(ft, wBitsPerSample); /* end of info common to all fmts */
+ switch (wFormatTag)
+ {
+ int nsamp;
+ case WAVE_FORMAT_IMA_ADPCM:
+ wshort(ft, 2); /* Ext fmt data length */
+ wav->samplesPerBlock = ((wBlockAlign - 4*wChannels)/(4*wChannels))*8 + 1;
+ wshort(ft, wav->samplesPerBlock);
+ fputs("fact", ft->fp);
+ wlong(ft, factsize-8); /* fact chunk size */
+ /* use max nsamps value first time */
+ nsamp = (second_header)? wav->numSamples : 0x7fffffffL;
+ wlong(ft, nsamp);
+ break;
+ default:
+ }
+
fputs("data", ft->fp);
wlong(ft, data_length); /* data chunk size: FIXUP(40) */
- if (!wav->second_header) {
+ if (!second_header) {
report("Writing Wave file: %s format, %d channel%s, %d samp/sec",
wav_format_str(wFormatTag), wChannels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
@@ -972,16 +821,44 @@
{
wav_t wav = (wav_t) ft->priv;
- wav->numSamples += len;
- rawwrite(ft, buf, len);
+ switch (wav->formatTag)
+ {
+ case WAVE_FORMAT_IMA_ADPCM:
+ while (len>0) {
+ short *p = wav->samplePtr;
+ short *top = wav->sampleTop;
+
+ if (top>p+len) top = p+len;
+ len -= top-p; /* update residual len */
+ while (p < top)
+ *p++ = ((*buf++) + 0x8000) >> 16;
+
+ wav->samplePtr = p;
+ if (p == wav->sampleTop)
+ ImaAdpcmWriteBlock(ft);
+
+ }
+ break;
+ default:
+ wav->numSamples += len;
+ rawwrite(ft, buf, len);
+ }
}
-void
-wavstopwrite(ft)
+void wavstopwrite(ft)
ft_t ft;
{
+ wav_t wav = (wav_t) ft->priv;
/* Call this to flush out any remaining data. */
- rawstopwrite(ft);
+ if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM) {
+ ImaAdpcmWriteBlock(ft);
+ } else {
+ rawstopwrite(ft);
+ }
+
+ if (wav->packet) free(wav->packet);
+ if (wav->samples) free(wav->samples);
+ if (wav->iCoefs) free(wav->iCoefs);
/* All samples are already written out. */
/* If file header needs fixing up, for example it needs the */
@@ -990,8 +867,7 @@
return;
if (fseek(ft->fp, 0L, SEEK_SET) != 0)
fail("Sorry, can't rewind output file to rewrite .wav header.");
- ((wav_t) ft->priv)->second_header = 1;
- wavwritehdr(ft);
+ wavwritehdr(ft, 1);
}
/*
--- a/src/wav.h
+++ b/src/wav.h
@@ -26,17 +26,5 @@
#define WAVE_FORMAT_G726_ADPCM (0x0064)
#define WAVE_FORMAT_G722_ADPCM (0x0065)
-typedef struct MsState {
- LONG index; /* Index into step size table */
- ULONG bpred; /* Most recent sample value */
- LONG sample1;
- LONG sample2;
-} MsState_t;
-
-typedef struct ImaState {
- int index; /* Index into step size table */
- int previousValue; /* Most recent sample value */
-} ImaState_t;
-
-
#endif /* WAV_H_INCLUDED */
+