[xiph-commits] r17633 - in websites/celt-codec.org: . squishyball
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Thu Nov 18 20:56:13 PST 2010
Author: xiphmont
Date: 2010-11-18 20:56:13 -0800 (Thu, 18 Nov 2010)
New Revision: 17633
Added:
websites/celt-codec.org/squishyball/
websites/celt-codec.org/squishyball/Makefile.am
websites/celt-codec.org/squishyball/autogen.sh
websites/celt-codec.org/squishyball/configure.ac
websites/celt-codec.org/squishyball/main.c
websites/celt-codec.org/squishyball/squishyball.1
Log:
Initial import of ~ 2/3-finished squishyball. No, it doesn't do
anything useful right now.
Added: websites/celt-codec.org/squishyball/Makefile.am
===================================================================
--- websites/celt-codec.org/squishyball/Makefile.am (rev 0)
+++ websites/celt-codec.org/squishyball/Makefile.am 2010-11-19 04:56:13 UTC (rev 17633)
@@ -0,0 +1,18 @@
+## Process this file with automake to produce Makefile.in
+
+AUTOMAKE_OPTIONS = foreign 1.11
+
+EXTRA_DIST = COPYING autogen.sh
+
+bin_PROGRAMS = squishyball
+mandir = @MANDIR@
+man_MANS = squishyball.1
+
+squishyball_SOURCES = main.c
+squishyball_LDADD = -lm
+
+debug:
+ $(MAKE) all CFLAGS="@DEBUG@"
+
+profile:
+ $(MAKE) all CFLAGS="@PROFILE@"
Added: websites/celt-codec.org/squishyball/autogen.sh
===================================================================
--- websites/celt-codec.org/squishyball/autogen.sh (rev 0)
+++ websites/celt-codec.org/squishyball/autogen.sh 2010-11-19 04:56:13 UTC (rev 17633)
@@ -0,0 +1,128 @@
+#!/bin/sh
+# Run this to set up the build system: configure, makefiles, etc.
+# (based on the version in enlightenment's cvs)
+
+package="squishyball"
+
+ACLOCAL_FLAGS=""
+
+olddir=`pwd`
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+cd "$srcdir"
+DIE=0
+
+/bin/echo "checking for autoconf... "
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "You must have autoconf installed to compile $package."
+ echo "Download the appropriate package for your distribution,"
+ echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+ DIE=1
+}
+
+VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/"
+VERSIONMKMAJ="sed -e s/\([0-9][0-9]*\)[^0-9].*/\\1/"
+VERSIONMKMIN="sed -e s/.*[0-9][0-9]*\.//"
+
+# do we need automake?
+if test -r Makefile.am; then
+ AM_OPTIONS=`fgrep AUTOMAKE_OPTIONS Makefile.am`
+ AM_NEEDED=`echo $AM_OPTIONS | $VERSIONGREP`
+ if test x"$AM_NEEDED" = "x$AM_OPTIONS"; then
+ AM_NEEDED=""
+ fi
+ if test -z $AM_NEEDED; then
+ /bin/echo -n "checking for automake... "
+ AUTOMAKE=automake
+ ACLOCAL=aclocal
+ if ($AUTOMAKE --version < /dev/null > /dev/null 2>&1); then
+ /bin/echo "yes"
+ else
+ /bin/echo "no"
+ AUTOMAKE=
+ fi
+ else
+ /bin/echo -n "checking for automake $AM_NEEDED or later... "
+ majneeded=`echo $AM_NEEDED | $VERSIONMKMAJ`
+ minneeded=`echo $AM_NEEDED | $VERSIONMKMIN`
+ for am in automake-$AM_NEEDED automake$AM_NEEDED \
+ automake automake-1.7 automake-1.8 automake-1.9 \
+ automake-1.10 automake-1.11; do
+ ($am --version < /dev/null > /dev/null 2>&1) || continue
+ ver=`$am --version < /dev/null | head -n 1 | $VERSIONGREP`
+ maj=`echo $ver | $VERSIONMKMAJ`
+ min=`echo $ver | $VERSIONMKMIN`
+ if test $maj -eq $majneeded -a $min -ge $minneeded; then
+ AUTOMAKE=$am
+ /bin/echo $AUTOMAKE
+ break
+ fi
+ done
+ test -z $AUTOMAKE && /bin/echo "no"
+ /bin/echo -n "checking for aclocal $AM_NEEDED or later... "
+ for ac in aclocal-$AM_NEEDED aclocal$AM_NEEDED \
+ aclocal aclocal-1.7 aclocal-1.8 aclocal-1.9 aclocal-1.10 aclocal-1.11; do
+ ($ac --version < /dev/null > /dev/null 2>&1) || continue
+ ver=`$ac --version < /dev/null | head -n 1 | $VERSIONGREP`
+ maj=`echo $ver | $VERSIONMKMAJ`
+ min=`echo $ver | $VERSIONMKMIN`
+ if test $maj -eq $majneeded -a $min -ge $minneeded; then
+ ACLOCAL=$ac
+ /bin/echo $ACLOCAL
+ break
+ fi
+ done
+ test -z $ACLOCAL && /bin/echo "no"
+ fi
+ test -z $AUTOMAKE || test -z $ACLOCAL && {
+ echo
+ echo "You must have automake installed to compile $package."
+ echo "Download the appropriate package for your distribution,"
+ echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+ exit 1
+ }
+fi
+
+/bin/echo -n "checking for libtool... "
+for LIBTOOLIZE in libtoolize glibtoolize nope; do
+ ($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 && break
+done
+if test x$LIBTOOLIZE = xnope; then
+ /bin/echo "nope."
+ LIBTOOLIZE=libtoolize
+else
+ /bin/echo $LIBTOOLIZE
+fi
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+ echo
+ echo "You must have libtool installed to compile $package."
+ echo "Download the appropriate package for your system,"
+ echo "or get the source from one of the GNU ftp sites"
+ echo "listed in http://www.gnu.org/order/ftp.html"
+ DIE=1
+}
+
+if test "$DIE" -eq 1; then
+ exit 1
+fi
+
+if test -z "$*"; then
+ echo "I am going to run ./configure with no arguments - if you wish "
+ echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+/bin/echo "Generating configuration files for $package, please wait...."
+
+/bin/echo " $ACLOCAL $ACLOCAL_FLAGS"
+$ACLOCAL $ACLOCAL_FLAGS || exit 1
+/bin/echo " $LIBTOOLIZE --automake --force"
+$LIBTOOLIZE --automake --force || exit 1
+/bin/echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS"
+$AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1
+/bin/echo " autoconf"
+autoconf || exit 1
+
+cd $olddir
+$srcdir/configure "$@" && /bin/echo
Property changes on: websites/celt-codec.org/squishyball/autogen.sh
___________________________________________________________________
Added: svn:executable
+ *
Added: websites/celt-codec.org/squishyball/configure.ac
===================================================================
--- websites/celt-codec.org/squishyball/configure.ac (rev 0)
+++ websites/celt-codec.org/squishyball/configure.ac 2010-11-19 04:56:13 UTC (rev 17633)
@@ -0,0 +1,79 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+cflags_save="$CFLAGS"
+AC_PREREQ(2.57)
+AC_INIT(main.c)
+AM_INIT_AUTOMAKE(squishyball, 20101117, [vorbis-dev at xiph.org])
+AC_CONFIG_FILES([Makefile])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])], [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])])
+
+# Checks for programs.
+AC_PROG_CC
+
+# Checks for libraries.
+
+PKG_CHECK_MODULES([vorbisfile], [vorbisfile])
+PKG_CHECK_MODULES([FLAC], [flac >= 0.8.0])
+PKG_CHECK_MODULES([ao], [ao > 1.0.0])
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdlib.h string.h])
+AC_CHECK_HEADERS([vorbis/vorbisfile.h])
+AC_CHECK_HEADERS([ao/ao.h])
+AC_CHECK_HEADERS([FLAC/stream_decoder.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+
+if test -z "$GCC"; then
+ case $host in
+ *-*-irix*)
+ DEBUG="-g -signed"
+ CFLAGS="-O2 -w -signed"
+ PROFILE="-p -g3 -O2 -signed"
+ ;;
+ sparc-sun-solaris*)
+ DEBUG="-v -g"
+ CFLAGS="-xO4 -fast -w -fsimple -native -xcg92"
+ PROFILE="-v -xpg -g -xO4 -fast -native -fsimple -xcg92 -Dsuncc"
+ ;;
+ *)
+ DEBUG="-g"
+ CFLAGS="-O"
+ PROFILE="-g -p"
+ ;;
+ esac
+else
+ case $host in
+ *-*-linux*)
+ DEBUG="-g -Wall -fsigned-char"
+ CFLAGS="-O2 -fsigned-char -ffast-math"
+ PROFILE="-Wall -W -pg -g -O2 -fsigned-char -ffast-math"
+ ;;
+ sparc-sun-*)
+ DEBUG="-g -Wall -fsigned-char"
+ CFLAGS="-O2 -fsigned-char"
+ PROFILE="-pg -g -O2 -fsigned-char"
+ ;;
+ *-*-darwin*)
+ DEBUG="-fno-common -g -Wall -fsigned-char"
+ CFLAGS="-fno-common -O2 -Wall -fsigned-char -ffast-math"
+ PROFILE="-fno-common -O2 -Wall -pg -g -fsigned-char -ffast-math"
+ ;;
+ *)
+ DEBUG="-g -Wall -fsigned-char"
+ CFLAGS="-O2 -fsigned-char -ffast-math"
+ PROFILE="-O2 -g -pg -fsigned-char -ffast-math"
+ ;;
+ esac
+fi
+CFLAGS="$CFLAGS $cflags_save -DVERSION='\"$VERSION\"' $vorbisfile_CFLAGS $ao_CFLAGS $FLAC_CFLAGS"
+DEBUG="$DEBUG $cflags_save -DVERSION='\\\"$VERSION\\\"' $vorbisfile_CFLAGS $ao_CFLAGS $FLAC_CFLAGS"
+PROFILE="$PROFILE $cflags_save -DVERSION='\\\"$VERSION\\\"' $vorbisfile_CFLAGS $ao_CFLAGS $FLAC_CFLAGS"
+LIBS="$LIBS $vorbisfile_LIBS $ao_LIBS $FLAC_LIBS"
+AC_SUBST(DEBUG)
+AC_SUBST(PROFILE)
+
+AC_OUTPUT
Added: websites/celt-codec.org/squishyball/main.c
===================================================================
--- websites/celt-codec.org/squishyball/main.c (rev 0)
+++ websites/celt-codec.org/squishyball/main.c 2010-11-19 04:56:13 UTC (rev 17633)
@@ -0,0 +1,1667 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+#include <vorbis/vorbisfile.h>
+#include <ao/ao.h>
+#include <FLAC/stream_decoder.h>
+#include <getopt.h>
+
+static int verbose=0;
+
+static inline int host_is_big_endian() {
+ union {
+ int32_t pattern;
+ unsigned char bytewise[4];
+ } m;
+ m.pattern = 0xfeedface; /* deadbeef */
+ if (m.bytewise[0] == 0xfe) return 1;
+ return 0;
+}
+
+typedef struct pcm_struct pcm_t;
+
+struct pcm_struct {
+ char *path;
+ int rate;
+ int bits; /* negative indicates IEEE754 float */
+ int ch;
+ char *matrix;
+ void *data;
+ off_t size;
+ int dither;
+};
+
+void free_pcm(pcm_t *pcm){
+ if(pcm){
+ if(pcm->path)free(pcm->path);
+ if(pcm->matrix)free(pcm->matrix);
+ if(pcm->data)free(pcm->data);
+ memset(pcm,0,sizeof(pcm));
+ free(pcm);
+ }
+}
+
+typedef struct{
+ int (*id_func)(char *path,unsigned char *buf);
+ pcm_t *(*load_func)(char *path, FILE *in);
+ char *format;
+} input_format;
+
+/* steal/simplify/expand file ID/load code from oggenc */
+
+/* Macros to read header data */
+#define READ_U32_LE(buf) \
+ (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U16_LE(buf) \
+ (((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U32_BE(buf) \
+ (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+#define READ_U16_BE(buf) \
+ (((buf)[0]<<8)|((buf)[1]&0xff))
+
+int wav_id(char *path,unsigned char *buf){
+ if(memcmp(buf, "RIFF", 4))
+ return 0; /* Not wave */
+ if(memcmp(buf+8, "WAVE",4))
+ return 0; /* RIFF, but not wave */
+ return 1;
+}
+
+int aiff_id(char *path,unsigned char *buf){
+ if(memcmp(buf, "FORM", 4))
+ return 0;
+ if(memcmp(buf+8, "AIF",3))
+ return 0;
+ if(buf[11]!='C' && buf[11]!='F')
+ return 0;
+ return 1;
+}
+
+int flac_id(char *path,unsigned char *buf){
+ return memcmp(buf, "fLaC", 4) == 0;
+}
+
+int oggflac_id(char *path,unsigned char *buf){
+ return memcmp(buf, "OggS", 4) == 0 &&
+ (memcmp (buf+28, "\177FLAC", 5) == 0 ||
+ flac_id(path,buf+28));
+}
+
+int vorbis_id(char *path,unsigned char *buf){
+ return memcmp(buf, "OggS", 4) == 0 &&
+ memcmp (buf+28, "\x01vorbis", 7);
+}
+
+int sw_id(char *path,unsigned char *buf){
+ /* if all else fails, look for JM's favorite extension */
+ return memcmp(path+strlen(path)-3,".sw",3)==0;
+}
+
+/* WAV file support ***********************************************************/
+
+static int find_wav_chunk(FILE *in, char *path, char *type, unsigned int *len){
+ unsigned char buf[8];
+
+ while(1){
+ if(fread(buf,1,8,in) < 8){
+ fprintf(stderr, "%s: Unexpected EOF in reading WAV header\n",path);
+ return 0; /* EOF before reaching the appropriate chunk */
+ }
+
+ if(memcmp(buf, type, 4)){
+ *len = READ_U32_LE(buf+4);
+ if(fseek(in, *len, SEEK_CUR))
+ return 0;
+
+ buf[4] = 0;
+ }else{
+ *len = READ_U32_LE(buf+4);
+ return 1;
+ }
+ }
+}
+
+pcm_t *wav_load(char *path, FILE *in){
+ unsigned char buf[40];
+ unsigned int len;
+ pcm_t *pcm = NULL;
+ int i;
+
+ if(fseek(in,12,SEEK_SET)==-1){
+ fprintf(stderr,"%s: Failed to seek\n",path);
+ goto err;
+ }
+
+ pcm = calloc(1,sizeof(pcm_t));
+ pcm->path=strdup(path);
+
+ if(!find_wav_chunk(in, path, "fmt ", &len)){
+ fprintf(stderr,"%s: Failed to find fmt chunk in WAV file\n",path);
+ goto err;
+ }
+
+ if(len < 16){
+ fprintf(stderr, "%s: Unrecognised format chunk in WAV header\n",path);
+ goto err;
+ }
+
+ if(verbose){
+ /* A common error is to have a format chunk that is not 16, 18 or
+ * 40 bytes in size. This is incorrect, but not fatal, so we only
+ * warn about it instead of refusing to work with the file.
+ * Please, if you have a program that's creating format chunks of
+ * sizes other than 16 or 18 bytes in size, report a bug to the
+ * author.
+ */
+ if(len!=16 && len!=18 && len!=40)
+ fprintf(stderr,
+ "%s: INVALID format chunk in WAV header.\n"
+ " Trying to read anyway (may not work)...\n",path);
+ }
+
+ if(len>40)len=40;
+
+ if(fread(buf,1,len,in) < len){
+ fprintf(stderr,"%s: Unexpected EOF in reading WAV header\n",path);
+ goto err;
+ }
+
+ unsigned int mask = 0;
+ unsigned int format = READ_U16_LE(buf);
+ unsigned int channels = READ_U16_LE(buf+2);
+ unsigned int samplerate = READ_U32_LE(buf+4);
+ //unsigned int bytespersec = READ_U32_LE(buf+8);
+ unsigned int align = READ_U16_LE(buf+12);
+ unsigned int samplesize = READ_U16_LE(buf+14);
+ const char *mask_map[32]={
+ "L","R","C","LFE", "BL","BR","CL","CR",
+ "BC","SL","SR","X", "X","X","X","X",
+ "X","X","X","X", "X","X","X","X",
+ "X","X","X","X", "X","X","X","X"};
+
+ if(format == 0xfffe){ /* WAVE_FORMAT_EXTENSIBLE */
+
+ if(len<40){
+ fprintf(stderr,"%s: Extended WAV format header invalid (too small)\n",path);
+ goto err;
+ }
+
+ mask = READ_U32_LE(buf+20);
+ format = READ_U16_LE(buf+24);
+ }
+
+ if(mask==0){
+ switch(channels){
+ case 1:
+ pcm->matrix = strdup("M");
+ break;
+ case 2:
+ pcm->matrix = strdup("L,R");
+ break;
+ case 3:
+ pcm->matrix = strdup("L,R,C");
+ break;
+ case 4:
+ pcm->matrix = strdup("L,R,BL,BR");
+ break;
+ case 5:
+ pcm->matrix = strdup("L,R,C,BL,BR");
+ break;
+ case 6:
+ pcm->matrix = strdup("L,R,C,LFE,BL,BR");
+ break;
+ case 7:
+ pcm->matrix = strdup("L,R,C,LFE,BC,SL,SR");
+ break;
+ default:
+ pcm->matrix = strdup("L,R,C,LFE,BL,BR,SL,SR");
+ break;
+ }
+ }else{
+ pcm->matrix = calloc(32*4,sizeof(char));
+ for(i=0;i<32;i++){
+ if(mask&(1<<i)){
+ strcat(pcm->matrix,mask_map[i]);
+ strcat(pcm->matrix,",");
+ }
+ }
+ pcm->matrix[strlen(pcm->matrix)-1]=0;
+ }
+
+ if(!find_wav_chunk(in, path, "data", &len)){
+ fprintf(stderr,"%s: Failed to find fmt chunk in WAV file\n",path);
+ goto err;
+ }
+
+ if(verbose){
+ if(align != channels * ((samplesize+7)/8)) {
+ /* This is incorrect according to the spec. Warn loudly, then ignore
+ * this value.
+ */
+ fprintf(stderr, "%s: WAV 'block alignment' value is incorrect, "
+ "ignoring.\n"
+ "The software that created this file is incorrect.\n",path);
+ }
+ }
+
+ if((format==1 && (samplesize == 24 || samplesize == 16 || samplesize == 8)) ||
+ (samplesize == 32 && format == 3)){
+ /* OK, good - we have a supported format,
+ now we want to find the size of the file */
+ pcm->rate = samplerate;
+ pcm->ch = channels;
+ pcm->bits = (format==3 ? -samplesize : samplesize);
+
+ if(len){
+ pcm->size = len;
+ }else{
+ long pos;
+ pos = ftell(in);
+ if(fseek(in, 0, SEEK_END) == -1){
+ fprintf(stderr,"%s failed to seek: %s\n",path,strerror(errno));
+ goto err;
+ }else{
+ pcm->size = ftell(in) - pos;
+ fseek(in,pos, SEEK_SET);
+ }
+ }
+
+ }else{
+ fprintf(stderr,
+ "%s: Wav file is unsupported subformat (must be 8,16, or 24 bit PCM\n"
+ "or floating point PCM\n",path);
+ goto err;
+ }
+
+ /* read the samples into memory */
+ switch(pcm->bits){
+ case 8:
+ /* load as 8 bit, expand it out to 16. */
+ pcm->data = calloc(1,pcm->size*2);
+ break;
+ case 24:
+ pcm->dither = 1;
+ case 16:
+ pcm->data = calloc(1,pcm->size);
+ break;
+ case -32:
+ pcm->data = calloc(1,pcm->size);
+ pcm->dither = 1;
+ break;
+ default:
+ /* Won't get here unless the code is modified and the modifier
+ misses expanding here and below */
+ fprintf(stderr,"%s: Unsupported bit depth\n",path);
+ goto err;
+ }
+
+ if(pcm->data == NULL){
+ fprintf(stderr,"Unable to allocate enough memory to load sample into memory\n");
+ goto err;
+ }
+
+ {
+ off_t j=0;
+ while(j<pcm->size){
+ off_t bytes = (pcm->size-j > 65536 ? 65536 : pcm->size-j);
+ if(verbose)
+ fprintf(stderr,"\rLoading %s: %ld to go... ",path,(long)(pcm->size-j));
+ j+=bytes=fread(pcm->data+j,1,bytes,in);
+ if(bytes==0)break;
+ }
+ if(j<pcm->size){
+ if(verbose)
+ fprintf(stderr,"\r%s: File ended before declared length (%ld < %ld); continuing...\n",path,(long)j,(long)pcm->size);
+ pcm->size=j;
+ }
+
+ /* 8 bit must be expanded to 16 */
+ if(samplesize==8){
+ off_t j;
+ unsigned char *d = pcm->data;
+ for(j=pcm->size-1;j>=0;j--){
+ int val = (d[j]-128)<<8;
+ d[j*2] = val&0xff;
+ d[j*2+1] = (val>>8)&0xff;
+ }
+ pcm->bits=16;
+ pcm->size*=2;
+ }
+ }
+
+ if(verbose)
+ fprintf(stderr,"\r%s: loaded. \n",path);
+
+ return pcm;
+ err:
+ free_pcm(pcm);
+ return NULL;
+}
+
+/* AIFF file support ***********************************************************/
+
+int find_aiff_chunk(FILE *in, char *path, char *type, unsigned int *len){
+ unsigned char buf[8];
+ int restarted = 0;
+
+ while(1){
+ if(fread(buf,1,8,in)<8){
+ if(!restarted) {
+ /* Handle out of order chunks by seeking back to the start
+ * to retry */
+ restarted = 1;
+ fseek(in, 12, SEEK_SET);
+ continue;
+ }
+ fprintf(stderr,"%s: Unexpected EOF in AIFF chunk\n",path);
+ return 0;
+ }
+
+ *len = READ_U32_BE(buf+4);
+
+ if(memcmp(buf,type,4)){
+ if((*len) & 0x1)
+ (*len)++;
+
+ if(fseek(in,*len,SEEK_CUR))
+ return 0;
+ }else
+ return 1;
+ }
+}
+
+double read_IEEE80(unsigned char *buf){
+ int s=buf[0]&0xff;
+ int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
+ double f=((unsigned long)(buf[2]&0xff)<<24)|
+ ((buf[3]&0xff)<<16)|
+ ((buf[4]&0xff)<<8) |
+ (buf[5]&0xff);
+
+ if(e==32767){
+ if(buf[2]&0x80)
+ return HUGE_VAL; /* Really NaN, but this won't happen in reality */
+ else{
+ if(s)
+ return -HUGE_VAL;
+ else
+ return HUGE_VAL;
+ }
+ }
+
+ f=ldexp(f,32);
+ f+= ((buf[6]&0xff)<<24)|
+ ((buf[7]&0xff)<<16)|
+ ((buf[8]&0xff)<<8) |
+ (buf[9]&0xff);
+ return ldexp(f, e-16446);
+}
+
+static inline void swap(unsigned char *a, unsigned char *b){
+ unsigned char temp=*a;
+ *a=*b;
+ *b=temp;
+}
+
+pcm_t *aiff_load(char *path, FILE *in){
+ pcm_t *pcm = NULL;
+ int aifc; /* AIFC or AIFF? */
+ unsigned int len;
+ unsigned char *buffer;
+ unsigned char buf2[12];
+ int bend = 1;
+
+ if(fseek(in,0,SEEK_SET)==-1){
+ fprintf(stderr,"%s: Failed to seek\n",path);
+ goto err;
+ }
+ if(fread(buf2,1,12,in)!=12){
+ fprintf(stderr,"%s: Failed to read AIFF header\n",path);
+ goto err;
+ }
+
+ pcm = calloc(1,sizeof(pcm_t));
+ pcm->path=strdup(path);
+
+ if(buf2[11]=='C')
+ aifc=1;
+ else
+ aifc=0;
+
+ if(!find_aiff_chunk(in, path, "COMM", &len)){
+ fprintf(stderr,"%s: No common chunk found in AIFF file\n",path);
+ goto err;
+ }
+
+ if(len < 18){
+ fprintf(stderr, "%s: Truncated common chunk in AIFF header\n",path);
+ goto err;
+ }
+
+ buffer = alloca(len);
+
+ if(fread(buffer,1,len,in) < len){
+ fprintf(stderr, "%s: Unexpected EOF in reading AIFF header\n",path);
+ goto err;
+ }
+
+ pcm->ch = READ_U16_BE(buffer);
+ pcm->rate = (int)read_IEEE80(buffer+8);
+ pcm->bits = READ_U16_BE(buffer+6);
+ pcm->size = READ_U32_BE(buffer+2)*pcm->ch*((pcm->bits+7)/8);
+
+ switch(pcm->ch){
+ case 1:
+ pcm->matrix = strdup("M");
+ break;
+ case 2:
+ pcm->matrix = strdup("L,R");
+ break;
+ case 3:
+ pcm->matrix = strdup("L,R,C");
+ break;
+ case 4:
+ pcm->matrix = strdup("L,R,BL,BR");
+ break;
+ }
+
+ if(aifc){
+ if(len < 22){
+ fprintf(stderr, "%s: AIFF-C header truncated.\n",path);
+ goto err;
+ }
+
+ if(!memcmp(buffer+18, "NONE", 4)){
+ bend = 1;
+ }else if(!memcmp(buffer+18, "sowt", 4)){
+ bend = 0;
+ }else{
+ fprintf(stderr, "%s: Can't handle compressed AIFF-C (%c%c%c%c)\n", path,
+ *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21));
+ goto err;
+ }
+ }
+
+ if(!find_aiff_chunk(in, path, "SSND", &len)){
+ fprintf(stderr, "%s: No SSND chunk found in AIFF file\n",path);
+ goto err;
+ }
+ if(len < 8) {
+ fprintf(stderr,"%s: Corrupted SSND chunk in AIFF header\n",path);
+ goto err;
+ }
+
+ if(fread(buf2,1,8, in) < 8){
+ fprintf(stderr, "%s: Unexpected EOF reading AIFF header\n",path);
+ goto err;
+ }
+
+ int offset = READ_U32_BE(buf2);
+ int blocksize = READ_U32_BE(buf2+4);
+
+ if( blocksize != 0 ||
+ !(pcm->bits==24 || pcm->bits == 16 || pcm->bits == 8)){
+ fprintf(stderr,
+ "%s: Unsupported type of AIFF/AIFC file\n"
+ " Must be 8, 16 or 24 bit integer PCM.\n",path);
+ goto err;
+ }
+
+ fseek(in, offset, SEEK_CUR); /* Swallow some data */
+
+ /* read the samples into memory */
+ switch(pcm->bits){
+ case 8:
+ /* load as 8 bit, expand it out to 16. */
+ pcm->data = calloc(1,pcm->size*2);
+ break;
+ case 24:
+ pcm->dither = 1;
+ /* fall through */
+ default:
+ pcm->data = calloc(1,pcm->size);
+ break;
+ }
+
+ if(pcm->data == NULL){
+ fprintf(stderr,"Unable to allocate enough memory to load sample into memory\n");
+ goto err;
+ }
+
+ {
+ unsigned char *d = pcm->data;
+ off_t j=0;
+ while(j<pcm->size){
+ off_t bytes = (pcm->size-j > 65536 ? 65536 : pcm->size-j);
+ if(verbose)
+ fprintf(stderr,"\rLoading %s: %ld to go... \r",path,(long)(pcm->size-j));
+ j+=bytes=fread(d+j,1,bytes,in);
+ if(bytes==0)break;
+ }
+ if(j<pcm->size){
+ if(verbose)
+ fprintf(stderr,"\r%s: File ended before declared length (%ld < %ld); continuing...\n",path,(long)j,(long)pcm->size);
+ pcm->size=j;
+ }
+
+ /* 8 bit must be expanded to 16 */
+ switch(pcm->bits){
+ case 8:
+ for(j=pcm->size-1;j>=0;j--){
+ int val = d[j]<<8;
+ d[j*2] = val&0xff;
+ d[j*2+1] = (val>>8)&0xff;
+ }
+ pcm->bits=16;
+ pcm->size*=2;
+ break;
+ case 16:
+ if(bend){
+ for(j=0;j<pcm->size/2;j++)
+ swap(d+j*2,d+j*2+1);
+ }
+ break;
+ case 24:
+ if(bend){
+ for(j=0;j<pcm->size/3;j++)
+ swap(d+j*3,d+j*3+2);
+ }
+ break;
+ }
+ }
+
+ if(verbose)
+ fprintf(stderr,"\r%s: loaded. \n",path);
+
+ return pcm;
+ err:
+ free_pcm(pcm);
+ return NULL;
+
+}
+
+/* SW loading to make JM happy *******************************************************/
+
+pcm_t *sw_load(char *path, FILE *in){
+
+ pcm_t *pcm = calloc(1,sizeof(pcm_t));
+ pcm->path=strdup(path);
+ pcm->bits=16;
+ pcm->ch=1;
+ pcm->rate=48000;
+
+ if(fseek(in,0,SEEK_END)==-1){
+ fprintf(stderr,"%s: Failed to seek\n",path);
+ goto err;
+ }
+ pcm->size=ftell(in);
+ if(pcm->size==-1 || fseek(in,0,SEEK_SET)==-1){
+ fprintf(stderr,"%s: Failed to seek\n",path);
+ goto err;
+ }
+
+ pcm->data = calloc(1,pcm->size);
+
+ if(pcm->data == NULL){
+ fprintf(stderr,"Unable to allocate enough memory to load sample into memory\n");
+ goto err;
+ }
+
+ {
+ off_t j=0;
+ while(j<pcm->size){
+ off_t bytes = (pcm->size-j > 65536 ? 65536 : pcm->size-j);
+ if(verbose)
+ fprintf(stderr,"\rLoading %s: %ld to go... ",path,(long)(pcm->size-j));
+ j+=bytes=fread(pcm->data+j,1,bytes,in);
+ if(bytes==0)break;
+ }
+ if(j<pcm->size){
+ if(verbose)
+ fprintf(stderr,"\r%s: File ended before declared length (%ld < %ld); continuing...\n",path,(long)j,(long)pcm->size);
+ pcm->size=j;
+ }
+ }
+
+ if(verbose)
+ fprintf(stderr,"\r%s: loaded. \n",path);
+
+ return pcm;
+ err:
+ free_pcm(pcm);
+ return NULL;
+}
+
+/* FLAC and OggFLAC load support *****************************************************************/
+
+typedef struct {
+ FILE *in;
+ pcm_t *pcm;
+ off_t fill;
+} flac_callback_arg;
+
+/* glorified fread wrapper */
+FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder,
+ FLAC__byte buffer[],
+ size_t *bytes,
+ void *client_data){
+ flac_callback_arg *flac = (flac_callback_arg *)client_data;
+ pcm_t *pcm = flac->pcm;
+
+ if(feof(flac->in)){
+ *bytes = 0;
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }else if(ferror(flac->in)){
+ *bytes = 0;
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+
+ if(verbose)
+ fprintf(stderr,"\rLoading %s: %ld to go... ",flac->pcm->path,(long)(pcm->size-flac->fill));
+ *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, flac->in);
+
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder,
+ const FLAC__Frame *frame,
+ const FLAC__int32 *const buffer[],
+ void *client_data){
+ flac_callback_arg *flac = (flac_callback_arg *)client_data;
+ pcm_t *pcm = flac->pcm;
+ int samples = frame->header.blocksize;
+ int channels = frame->header.channels;
+ int bits_per_sample = frame->header.bits_per_sample;
+ off_t fill = flac->fill;
+ int i, j;
+
+ if(pcm->data == NULL){
+ /* lasy initialization */
+ pcm->ch = channels;
+ pcm->bits = (bits_per_sample+7)/8*8;
+ pcm->size *= pcm->bits/8*channels;
+ pcm->data = calloc(pcm->size,1);
+ }
+
+ if(channels != pcm->ch){
+ fprintf(stderr,"\r%s: number of channels changes part way through file\n",pcm->path);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+ if(pcm->bits != (bits_per_sample+7)/8*8){
+ fprintf(stderr,"\r%s: bit depth changes part way through file\n",pcm->path);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+
+ {
+ unsigned char *d = pcm->data + fill;
+ int shift = pcm->bits - bits_per_sample;
+ switch(pcm->bits){
+ case 16:
+ for (j = 0; j < samples; j++)
+ for (i = 0; i < channels; i++){
+ d[0] = (buffer[i][j]<<shift)&0xff;
+ d[1] = (buffer[i][j]<<shift>>8)&0xff;
+ d+=2;
+ fill+=2;
+ }
+ break;
+ case 24:
+ pcm->dither = 1;
+ for (j = 0; j < samples; j++)
+ for (i = 0; i < channels; i++){
+ d[0] = (buffer[i][j]<<shift)&0xff;
+ d[1] = (buffer[i][j]<<shift>>8)&0xff;
+ d[3] = (buffer[i][j]<<shift>>16)&0xff;
+ d+=3;
+ fill+=3;
+ }
+ break;
+ default:
+ fprintf(stderr,"\r%s: Only 16 and 24 bit FLACs are supported for decode right now.\n",pcm->path);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+ }
+ flac->fill=fill;
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void metadata_callback(const FLAC__StreamDecoder *decoder,
+ const FLAC__StreamMetadata *metadata,
+ void *client_data){
+ flac_callback_arg *flac = (flac_callback_arg *)client_data;
+ pcm_t *pcm = flac->pcm;
+
+ switch (metadata->type){
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ pcm->size = metadata->data.stream_info.total_samples; /* temp setting */
+ pcm->rate = metadata->data.stream_info.sample_rate;
+ break;
+ default:
+ break;
+ }
+}
+
+void error_callback(const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status,
+ void *client_data){
+
+ flac_callback_arg *flac = (flac_callback_arg *)client_data;
+ pcm_t *pcm = flac->pcm;
+ fprintf(stderr,"\r%s: Error decoding file.\n",pcm->path);
+}
+
+FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder,
+ void *client_data){
+ flac_callback_arg *flac = (flac_callback_arg *)client_data;
+ return feof(flac->in)? true : false;
+}
+
+pcm_t *flac_load_i(char *path, FILE *in, int oggp){
+ pcm_t *pcm = calloc(1,sizeof(pcm_t));
+ flac_callback_arg *flac = calloc(1,sizeof(flac_callback_arg));
+ FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
+ FLAC__bool ret;
+ FLAC__stream_decoder_set_md5_checking(decoder, true);
+ FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
+ pcm->path=strdup(path);
+ flac->in=in;
+ flac->pcm=pcm;
+
+ if(oggp)
+ FLAC__stream_decoder_init_ogg_stream(decoder,
+ read_callback,
+ /*seek_callback=*/0,
+ /*tell_callback=*/0,
+ /*length_callback=*/0,
+ eof_callback,
+ write_callback,
+ metadata_callback,
+ error_callback,
+ flac);
+ else
+ FLAC__stream_decoder_init_stream(decoder,
+ read_callback,
+ /*seek_callback=*/0,
+ /*tell_callback=*/0,
+ /*length_callback=*/0,
+ eof_callback,
+ write_callback,
+ metadata_callback,
+ error_callback,
+ flac);
+
+ /* setup and sample reading handled by configured callbacks */
+ ret=FLAC__stream_decoder_process_until_end_of_stream(decoder);
+ FLAC__stream_decoder_finish(decoder);
+ FLAC__stream_decoder_delete(decoder);
+ free(flac);
+ if(!ret){
+ free_pcm(pcm);
+ return NULL;
+ }
+
+ /* set channel matrix */
+ switch(pcm->ch){
+ case 1:
+ pcm->matrix = strdup("M");
+ break;
+ case 2:
+ pcm->matrix = strdup("L,R");
+ break;
+ case 3:
+ pcm->matrix = strdup("L,R,C");
+ break;
+ case 4:
+ pcm->matrix = strdup("L,R,BL,BR");
+ break;
+ case 5:
+ pcm->matrix = strdup("L,R,C,BL,BR");
+ break;
+ case 6:
+ pcm->matrix = strdup("L,R,C,LFE,BL,BR");
+ break;
+ case 7:
+ pcm->matrix = strdup("L,R,C,LFE,BC,SL,SR");
+ break;
+ default:
+ pcm->matrix = strdup("L,R,C,LFE,BL,BR,SL,SR");
+ break;
+ }
+
+ if(verbose)
+ fprintf(stderr,"\r%s: loaded. \n",path);
+ return pcm;
+}
+
+pcm_t *flac_load(char *path, FILE *in){
+ return flac_load_i(path,in,0);
+}
+
+pcm_t *oggflac_load(char *path, FILE *in){
+ return flac_load_i(path,in,1);
+}
+
+/* Vorbis load support **************************************************************************/
+pcm_t *vorbis_load(char *path, FILE *in){
+ OggVorbis_File vf;
+ vorbis_info *vi=NULL;
+ pcm_t *pcm=NULL;
+ off_t fill=0;
+ int throttle=0;
+ int last_section=-1;
+
+ memset(&vf,0,sizeof(vf));
+
+ if(fseek(in,0,SEEK_SET)==-1){
+ fprintf(stderr,"%s: Failed to seek\n",path);
+ goto err;
+ }
+
+ if(ov_open_callbacks(in, &vf, NULL, 0, OV_CALLBACKS_NOCLOSE) < 0) {
+ fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n");
+ goto err;
+ }
+
+ vi=ov_info(&vf,-1);
+ pcm->path=strdup(path);
+ pcm->bits=-32;
+ pcm->ch=vi->channels;
+ pcm->rate=vi->rate;
+ pcm->size=ov_pcm_total(&vf,-1)*vi->channels*4;
+ pcm->data=calloc(pcm->size,1);
+
+ switch(pcm->ch){
+ case 1:
+ pcm->matrix = strdup("M");
+ break;
+ case 2:
+ pcm->matrix = strdup("L,R");
+ break;
+ case 3:
+ pcm->matrix = strdup("L,C,R");
+ break;
+ case 4:
+ pcm->matrix = strdup("L,R,BL,BR");
+ break;
+ case 5:
+ pcm->matrix = strdup("L,C,R,BL,BR");
+ break;
+ case 6:
+ pcm->matrix = strdup("L,C,R,BL,BR,LFE");
+ break;
+ case 7:
+ pcm->matrix = strdup("L,C,R,SL,SR,BC,LFE");
+ break;
+ default:
+ pcm->matrix = strdup("L,C,R,SL,SR,BL,BR,LFE");
+ break;
+ }
+
+ while(fill<pcm->size){
+ int current_section;
+ int i,j;
+ float **pcmout;
+ long ret=ov_read_float(&vf,&pcmout,4096,¤t_section);
+ unsigned char *d = pcm->data+fill;
+
+ if(current_section!=last_section){
+ last_section=current_section;
+ vi=ov_info(&vf,-1);
+ if(vi->channels != pcm->ch || vi->rate!=pcm->rate){
+ fprintf(stderr,"%s: Chained file changes channel count/sample rate\n",path);
+ goto err;
+ }
+ }
+
+ if(ret<0){
+ fprintf(stderr,"%s: Error while decoding file\n",path);
+ goto err;
+ }
+ if(ret==0){
+ fprintf(stderr,"%s: Audio data ended prematurely\n",path);
+ goto err;
+ }
+
+ if(sizeof(float)==4){
+ /* Assuming IEEE754, which is pedantically incorrect */
+ union {
+ float f;
+ unsigned char c[4];
+ } m;
+ if(host_is_big_endian()){
+ for(i=0;i<ret;i++){
+ for(j=0;j<pcm->ch;j++){
+ m.f=pcmout[j][i];
+ d[0] = m.c[3];
+ d[1] = m.c[2];
+ d[2] = m.c[1];
+ d[3] = m.c[0];
+ d+=4;
+ }
+ }
+ }else{
+ for(i=0;i<ret;i++){
+ for(j=0;j<pcm->ch;j++){
+ m.f=pcmout[j][i];
+ d[0] = m.c[0];
+ d[1] = m.c[1];
+ d[2] = m.c[2];
+ d[3] = m.c[3];
+ d+=4;
+ }
+ }
+ }
+ }else{
+ fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
+ exit(10);
+ }
+ fill += ret*pcm->ch*3;
+ if (verbose && (throttle&0x3f)==0)
+ fprintf(stderr,"\rLoading %s: %ld to go... ",pcm->path,(long)(pcm->size-fill));
+ throttle++;
+ }
+ ov_clear(&vf);
+
+ if(verbose)
+ fprintf(stderr,"\r%s: loaded. \n",path);
+ return pcm;
+ err:
+ ov_clear(&vf);
+ free_pcm(pcm);
+ return NULL;
+}
+
+#define MAX_ID_LEN 35
+unsigned char buf[MAX_ID_LEN];
+
+/* Define the supported formats here */
+input_format formats[] = {
+ {wav_id, wav_load, "wav"},
+ {aiff_id, aiff_load, "aiff"},
+ {flac_id, flac_load, "flac"},
+ {oggflac_id, oggflac_load,"oggflac"},
+ {vorbis_id, vorbis_load, "oggvorbis"},
+ {sw_id, sw_load, "sw"},
+ {NULL, NULL, NULL}
+};
+
+pcm_t *load_audio_file(char *path){
+ FILE *f = fopen(path,"rb");
+ int j=0;
+ int fill;
+
+ if(!f){
+ fprintf(stderr,"Unable to open file %s: %s\n",path,strerror(errno));
+ return NULL;
+ }
+
+ fill = fread(buf, 1, MAX_ID_LEN, f);
+ if(fill<MAX_ID_LEN){
+ fprintf(stderr,"%s: Input file truncated or NULL\n",path);
+ fclose(f);
+ return NULL;
+ }
+
+ while(formats[j].id_func){
+ if(formats[j].id_func(path,buf)){
+ pcm_t *ret=formats[j].load_func(path,f);
+ fclose(f);
+ return ret;
+ }
+ j++;
+ }
+ return NULL;
+}
+
+/* sample formatting helpers ********************************************************/
+
+void float32_to_24(pcm_t *pcm){
+ unsigned char *d = pcm->data;
+ off_t j;
+ for(j=0;j<pcm->size/4;j++){
+ int val=0;
+ int mantissa = d[j*4] | (d[j*4+1]<<8) | ((d[j*4+2]&0x7f)<<16) | (1<<23);
+ int exponent = 127 - ((d[j*4+2]>>7) | ((d[j*4+3]&0x7f)<<1));
+ int sign = d[j*4+3]>>7;
+ if(exponent <= 0){
+ if(exponent == -128){
+ fprintf(stderr,"%s: Input file contains invalid floating point values.\n",pcm->path);
+ exit(6);
+ }
+ if(sign)
+ val = 8388608;
+ else
+ val = 8388607;
+ }else if(exponent <= 24){
+ val = mantissa>>exponent;
+ /* round with tiebreaks toward even */
+ if(((mantissa<<(24-exponent))&0xffffff) + (val&1) > 0x800000) ++val;
+ }
+ if(sign) val= -val;
+
+ d[j*3]=val&0xff;
+ d[j*3+1]=(val>>8)&0xff;
+ d[j*3+2]=(val>>16)&0xff;
+ }
+ pcm->bits=24;
+ pcm->size/=4;
+ pcm->size*=3;
+}
+
+static inline float triangle_ditherval(float *save){
+ float r = rand()/(float)RAND_MAX-.5f;
+ float ret = *save-r;
+ *save = r;
+ return ret;
+}
+
+void float32_to_16(pcm_t *pcm){
+ unsigned char *d = pcm->data;
+ off_t j;
+ union {
+ float f;
+ unsigned char c[4];
+ } m;
+
+ /* again assumes IEEE754, which is not pedantically correct */
+ if(sizeof(float)==4){
+ float t[pcm->ch];
+ int ch=0;
+ memset(t,0,sizeof(t));
+
+ for(j=0;j<pcm->size/4;j++){
+ float val;
+ if(host_is_big_endian()){
+ m.c[0]=d[j*4+3];
+ m.c[1]=d[j*4+2];
+ m.c[2]=d[j*4+1];
+ m.c[3]=d[j*4];
+ }else{
+ m.c[0]=d[j*4];
+ m.c[1]=d[j*4+1];
+ m.c[2]=d[j*4+2];
+ m.c[3]=d[j*4+3];
+ }
+ if(pcm->dither){
+ val = rint(m.f*32768.f + triangle_ditherval(t+ch));
+ ch++;
+ if(ch>pcm->ch)ch=0;
+ }else{
+ val = rint(m.f*32768.f);
+ }
+
+ if(val>=32767.f){
+ d[j*2]=0xff;
+ d[j*2+1]=0x7f;
+ }else if(val<=-32768.f){
+ d[j*2]=0x00;
+ d[j*2+1]=0x80;
+ }else{
+ int iv = (int)val;
+ d[j*2]=iv&0xff;
+ d[j*2+1]=(iv>>8)&0xff;
+ }
+ }
+ }else{
+ fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
+ exit(10);
+ }
+
+ pcm->bits=16;
+ pcm->size/=2;
+}
+
+void demote_24_to_16(pcm_t *pcm){
+ float t[pcm->ch];
+ unsigned char *d = pcm->data;
+ off_t i;
+ int ch=0;
+ memset(t,0,sizeof(t));
+
+ for(i=0;i<pcm->size/3;i++){
+ int val = ((d[i*3+2]<<24) | (d[i*3+1]<<16) | (d[i*3]<<8))>>8;
+ if(pcm->dither){
+ val = rint (val*(1.f/256.f)+triangle_ditherval(t+ch));
+ ch++;
+ if(ch>pcm->ch)ch=0;
+ }else
+ val = rint (val*(1.f/256.f));
+
+ if(val>32767){
+ d[i*2]=0xff;
+ d[i*2+1]=0x7f;
+ }else if(val<-32768){
+ d[i*2]=0x00;
+ d[i*2+1]=0x80;
+ }else{
+ d[i*2]=val&0xff;
+ d[i*2+1]=(val>>8)&0xff;
+ }
+ }
+
+ pcm->bits=16;
+ pcm->size/=3;
+ pcm->size*=2;
+}
+
+void promote_to_24(pcm_t *pcm){
+ off_t i;
+ unsigned char *ret=realloc(pcm->data,pcm->size*3/2);
+ if(!ret){
+ fprintf(stderr,"Unable to allocate memory while promoting file to 24 bit\n");
+ exit(5);
+ }
+ pcm->data=ret;
+ for(i=pcm->size/2-1;i>=0;i--){
+ ret[i*3+2]=ret[i*2+1];
+ ret[i*3+1]=ret[i*2];
+ ret[i*3]=0;
+ }
+ pcm->bits=24;
+ pcm->size/=2;
+ pcm->size*=3;
+}
+
+void convert_to_16(pcm_t *pcm){
+ switch(pcm->bits){
+ case 16:
+ break;
+ case 24:
+ demote_24_to_16(pcm);
+ break;
+ case -32:
+ float32_to_16(pcm);
+ break;
+ default:
+ fprintf(stderr,"%s: Unsupported sample format.\n",pcm->path);
+ exit(6);
+ }
+}
+
+void convert_to_24(pcm_t *pcm){
+ switch(pcm->bits){
+ case 16:
+ promote_to_24(pcm);
+ break;
+ case 24:
+ break;
+ case -32:
+ float32_to_24(pcm);
+ break;
+ default:
+ fprintf(stderr,"%s: Unsupported sample format.\n",pcm->path);
+ exit(6);
+ }
+}
+
+const char *chlist[]={"M","L","R","C","LFE","SL","SR","BC","BL","BR","CL","CR","X",NULL};
+void tokenize_channels(char *matrix,int *out,int n){
+ int i=0;
+ char *t=strtok(matrix,",");
+ memset(out,0,sizeof(*out)*n);
+
+ while(t){
+ int j=0;
+ while(chlist[j]){
+ if(!strcmp(chlist[j],t))break;
+ j++;
+ }
+ out[i]=j;
+ i++;
+ t=strtok(NULL,",");
+ }
+}
+
+/* pre-permute sample ordering so that playback incurs ~equal
+ CPU/memory/etc load during playback */
+/* A and B must have machine sample formats */
+void reconcile_channel_maps(pcm_t *A, pcm_t *B){
+ /* arbitrary; match B to A */
+ int ai[A->ch],bi[A->ch];
+ int i,j,k;
+ off_t o;
+ int bps = (B->bits+7)/8;
+ int bpf = B->ch*bps;
+ int p[bpf];
+ unsigned char temp[bpf];
+ unsigned char *d;
+
+ tokenize_channels(A->matrix,ai,A->ch);
+ tokenize_channels(B->matrix,bi,A->ch);
+
+ for(i=0;i<A->ch;i++){
+ for(j=0;j<A->ch;j++){
+ if(bi[i]==ai[j]){
+ for(k=0;k<bps;k++)
+ p[i*bps+k]=j*bps+k;
+ break;
+ }
+ }
+ }
+
+ d=B->data;
+ for(o=0;o<B->size;){
+ for(i=0;i<bpf;i++)
+ temp[p[i]]=d[i];
+ memcpy(d,temp,bpf);
+ d+=bpf;
+ }
+
+ free(B->matrix);
+ B->matrix = strdup(A->matrix);
+}
+
+ao_device *setup_playback(int rate, int ch, int bits, char *matrix, char *device){
+ ao_option aoe={0,0,0};
+ ao_device *ret=NULL;
+ ao_sample_format sf;
+ char *aname="";
+ sf.rate=rate;
+ sf.channels=ch;
+ sf.bits=bits;
+ sf.byte_format=AO_FMT_LITTLE;
+ sf.matrix=(ch>2?matrix:0);
+ aoe.key="quiet";
+
+ if(!device){
+ /* if we don't have an explicit device, defaults make this easy */
+ int id = ao_default_driver_id();
+ aname=ao_driver_info(id)->short_name;
+ ret=ao_open_live(id, &sf, &aoe);
+ }else{
+ /* Otherwise... there's some hunting to do. */
+ /* Is the passed device a number or a name? */
+ char *test;
+ int count;
+ ao_info **info_list=ao_driver_info_list(&count);
+ int number=strtol(device,&test,10);
+ int i;
+
+ if(!device[0] || test[0]) number=-1;
+
+ /* driver info list is sorted by priority */
+ for(i=0;i<count;i++){
+ int j;
+ ao_info *info=info_list[i];
+ ao_option ao={0,0,0};
+ ao_option aoe={0,0,0};
+ int id = ao_driver_id(info->short_name);
+ char buf[80];
+
+ sprintf(buf,"%d",number);
+ ao.key=(number>=0?"id":"dev");
+ ao.value=(number>=0?buf:device);
+ ao.next=&aoe;
+ aname=info->short_name;
+
+ /* don't try to open the driver if it doesn't have the device/id
+ option; it will ignore the option and try to open its default */
+ for(j=0;j<info->option_count;j++)
+ if(!strcmp(info->options[j],ao.key))break;
+ if(j<info->option_count)
+ if((ret=ao_open_live(id,&sf,&ao)))break;
+ }
+ }
+ if(ret && verbose)
+ fprintf(stderr,"Opened %s%s audio device %s%sfor %d bit %d channel %d Hz...\n",
+ (device?"":"default "),aname,
+ (device?device:""),(device?" ":""),bits,ch,rate);
+
+ return ret;
+}
+
+struct option long_options[] = {
+ {"ab",no_argument,0,'a'},
+ {"abx",no_argument,0,'b'},
+ {"card",required_argument,0,'c'},
+ {"force-dither",no_argument,0,'d'},
+ {"end-time",no_argument,0,'e'},
+ {"help",no_argument,0,'h'},
+ {"comparisons",required_argument,0,'n'},
+ {"restart-mode",no_argument,0,'r'},
+ {"start-time",required_argument,0,'s'},
+ {"force-truncate",no_argument,0,'t'},
+ {"verbose",no_argument,0,'v'},
+ {"version",no_argument,0,'V'},
+ {"xxy",no_argument,0,'x'},
+ {0,0,0,0}
+};
+char *short_options="abc:de:hn:rs:tvVx";
+
+void usage(FILE *out){
+ fprintf(out,
+ "\nXiph.Org Squishyball %s\n"
+ "performs simple A/B, A/B/X and X/X/Y sample comparison\n"
+ "testing on the command line\n\n"
+ "USAGE:\n"
+ " squishyball [options] fileA fileB\n\n"
+ "OPTIONS:\n"
+ " -a --ab : Perform randomized A/B test\n"
+ " -b --abx : Perform A/B/X test (default)\n"
+ " -c --card <N|device> : If a number, output to Nth\n"
+ " sound device. If a device name,\n"
+ " use output driver/device matching\n"
+ " that device name.\n"
+ " -d --force-dither : Always use dither when converting\n"
+ " to 16 bit for playback on output\n"
+ " devices that do not support 24 bit\n"
+ " playback. Currently only affects\n"
+ " Vorbis playback; all other files\n"
+ " a dithered by default during down-\n"
+ " conversion.\n"
+ " -e --end-time <time> : Set sample end time for playback\n"
+ " -h --help : Print this usage information.\n"
+ " -n --comparisons <n> : Set desired number of comparisons\n"
+ " (default: 10)\n"
+ " -r --restart-mode : Set 'restart mode', where sample\n"
+ " playback restarts from start point\n"
+ " after every test selection\n"
+ " -s --start-time <time> : Set start time within sample for\n"
+ " playback\n"
+ " -t --force-truncate : Always truncate (never dither) when\n"
+ " dwon-converting samples to 16 bit for\n"
+ " playback.\n"
+ " -v --verbose : Produce more progress information.\n"
+ " -V --version : Print version and exit.\n"
+ " -x --xxy : Perform X/X/Y testing; This is\n"
+ " similar to A/B/X testing but A and\n"
+ " B are unknown. Instead of choosing\n"
+ " if X matches A or B, choose the\n"
+ " sample that does not match the other\n"
+ " two.\n"
+ "\n"
+ "INTERACTION:\n"
+ " a b x : Switch between A and B samples (A/B mode), or A, B\n"
+ " and X samples (A/B/X mode)\n"
+ " A B : Select A or B as preferred sample (A/B mode), or\n"
+ " sample A or sample B as match to sample X (A/B/X\n"
+ " testing mode)\n"
+ " 1 2 3 : Switch between first second and third samples\n"
+ " (X/X/Y testing mode)\n"
+ " ! @ # : Indicate the 'odd sample out' as sample 1, 2, or 3\n"
+ " (X/X/Y testing mode)\n"
+ " <- -> : seek back/forward one second\n"
+ " +shift or five seconds\n"
+ " +control for 15 seconds\n"
+ " +meta for one minute\n"
+ " <space> : Pause/resume playback\n"
+ "<bckspce>: Reset playback to start point\n"
+ " e : set end playback point to current playback time\n"
+ " (see also -e above)\n"
+ " E : reset end playback time to end of sample\n"
+ " r : enable restart mode, where each comparison begins\n"
+ " from the start time of the sample (see -r above)\n"
+ " R : clear restart mode; playback continues without\n"
+ " interruption after each comparison\n"
+ " s : set start playback point to current playback time\n"
+ " (see also -s above)\n"
+ " S : reset start playback time to 0:00:00.00\n"
+ " ? : Print this keymap\n"
+ " Ctrl-C : Abort test early\n"
+ "\n"
+ "SUPPORTED FILE TYPES:\n"
+ " WAV and WAVEX : 8, 16, 24 bit linear integer PCM (format 1)\n"
+ " 32 bit float (format 3)\n"
+ " AIFF and AIFC : 8, 16, 24 bit linear integer PCM\n"
+ " FLAC and OggFLAC : 16 and 24 bit\n"
+ " SW : mono signed 16 bit little endian raw\n"
+ " OggVorbis : all Vorbis I files\n"
+ "\n"
+ ,VERSION);
+}
+
+static char timebuffer[80];
+static char *make_time_string(double s){
+ long hrs=s/60/60;
+ long min=s/60-hrs*60;
+ long sec=s-hrs*60*60-min*60;
+ long hsec=(s-(int)s)*100;
+ if(hrs>0){
+ snprintf(timebuffer,80,"%ld:%02ld:%02ld.%02ld",hrs,min,sec,hsec);
+ }else if(min>0){
+ snprintf(timebuffer,80,"%ld:%02ld.%02ld",min,sec,hsec);
+ }else{
+ snprintf(timebuffer,80,"%ld.%02ld",sec,hsec);
+ }
+ return timebuffer;
+}
+
+static int parse_time(char *s,double *t){
+ double secf;
+ long secl;
+ const char *pos;
+ char *end;
+ int err;
+ err=0;
+ secl=0;
+ pos=strchr(optarg,':');
+ if(pos!=NULL){
+ char *pos2;
+ secl=strtol(optarg,&end,10)*60;
+ err|=pos!=end;
+ pos2=strchr(++pos,':');
+ if(pos2!=NULL){
+ secl=(secl+strtol(pos,&end,10))*60;
+ err|=pos2!=end;
+ pos=pos2+1;
+ }
+ }
+ else pos=optarg;
+ secf=strtod(pos,&end);
+ if(err||*end!='\0')return -1;
+
+ *t = secl+secf;
+ return 0;
+}
+
+int main(int argc, char **argv){
+ int c,long_option_index;
+ pcm_t *A=NULL;
+ pcm_t *B=NULL;
+ int test_mode=1;
+ char *device=NULL;
+ int force_dither=0;
+ int force_truncate=0;
+ int restart_mode=0;
+ int tests=10;
+ double start=0;
+ double end=-1;
+ int outbits=0;
+ ao_device *adev=NULL;
+ int decode_to_16=0;
+ /* parse options */
+
+ while((c=getopt_long(argc,argv,short_options,long_options,&long_option_index))!=EOF){
+ switch(c){
+ case 'h':
+ usage(stdout);
+ return 0;
+ case 'a':
+ test_mode=0;
+ break;
+ case 'b':
+ test_mode=1;
+ break;
+ case 'x':
+ test_mode=2;
+ break;
+ case 'c':
+ device=strdup(optarg);
+ break;
+ case 'd':
+ force_dither=1;
+ force_truncate=0;
+ break;
+ case 't':
+ force_dither=0;
+ force_truncate=1;
+ break;
+ case 'n':
+ tests=atoi(optarg);
+ if(tests<1){
+ fprintf(stderr,"Error parsing argument to -n\n");
+ exit(1);
+ }
+ break;
+ case 'e':
+ parse_time(optarg,&end);
+ break;
+ case 's':
+ parse_time(optarg,&start);
+ break;
+ case 'r':
+ restart_mode=1;
+ break;
+ case 'v':
+ verbose=1;
+ break;
+ case 'V':
+ fprintf(stdout,"%s\n",VERSION);
+ exit(0);
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ if(argc-optind!=2){
+ usage(stderr);
+ exit(1);
+ }
+
+ A=load_audio_file(argv[optind]);
+ if(A)
+ B=load_audio_file(argv[optind+1]);
+ if(!B)
+ exit(2);
+
+ /* An overloaded invariant: Only lossy samples that decode to float
+ (eg, Ogg Vorbis) will have bits==-32 but dither set to 0. If one
+ input sample is 16 bit and the other is -32/0, we want the floats
+ to render down to 16 bit, not 24 bit */
+ if((A->bits==16 && B->bits==-32 && B->dither==0) ||
+ (B->bits==16 && A->bits==-32 && A->dither==0))
+ decode_to_16=1;
+
+ if(!A->dither && force_dither)A->dither=1;
+ if(!B->dither && force_dither)B->dither=1;
+ if(A->bits!=16 && force_truncate)A->dither=0;
+ if(B->bits!=16 && force_truncate)B->dither=0;
+
+ /* Are the samples the same rate? If not, bail. */
+ if(A->rate != B->rate){
+ fprintf(stderr,"Input sample rates do not match!\n"
+ "\t%s: %dHz\n"
+ "\t%s: %dHz\n"
+ "Aborting\n",A->path,A->rate,B->path,B->rate);
+ exit(3);
+ }
+
+ /* Are the samples the same number of channels? If not, bail. */
+ if(A->ch != B->ch){
+ fprintf(stderr,"Input channel counts do not match!\n"
+ "\t%s: %dHz\n"
+ "\t%s: %dHz\n"
+ "Aborting\n",A->path,A->ch,B->path,B->ch);
+ exit(3);
+ }
+
+ if(decode_to_16){
+ convert_to_16(A);
+ convert_to_16(B);
+ }
+
+ /* before proceeding, make sure we can open up playback for the
+ requested number of channels and max bit depth; if not, we may
+ need to downconvert. */
+ outbits = abs(A->bits);
+ if(abs(B->bits)>outbits)outbits=abs(B->bits);
+ if(outbits==32)outbits=24;
+
+ /* See if we can get playback with desired bit-depth/channels */
+ ao_initialize();
+ if((adev=setup_playback(A->rate,A->ch,outbits,A->matrix,device))==NULL){
+ /* If opening playback failed for 24 bit, try for 16 */
+ if(outbits>16){
+ if((adev=setup_playback(A->rate,A->ch,16,A->matrix,device))==NULL){
+ fprintf(stderr,"Unable to open audio device for playback.\n");
+ exit(4);
+ }else{
+ if(verbose)
+ fprintf(stderr,"24 bit playback unavailable; down-converting to 16 bit\n");
+
+ /* demote to 16 bit playback right now */
+ convert_to_16(A);
+ convert_to_16(B);
+
+ }
+ }else{
+ fprintf(stderr,"Unable to open audio device for playback.\n");
+ exit(4);
+ }
+ }
+
+ /* Do we need to reconcile sample depths? */
+ if(A->bits!=B->bits || A->bits<0 || B->bits<0){
+ convert_to_24(A);
+ convert_to_24(B);
+ }
+
+ /* Sanity check channel matrices. */
+ if(A->ch>2){
+ if(!A->matrix || !B->matrix){
+ if(A->matrix){
+ if(verbose)
+ fprintf(stderr,"%s: No explicit output channel ordering determined. Using output\n"
+ "\tmatrix from %s.\n",B->path,A->path);
+ B->matrix = strdup(A->matrix);
+ }else if (B->matrix){
+ if(verbose)
+ fprintf(stderr,"%s: No explicit output channel ordering determined. Using output\n"
+ "\tmatrix from %s.\n",A->path,B->path);
+ A->matrix = strdup(B->matrix);
+ }else{
+ if(verbose)
+ fprintf(stderr,"Neither sample specifies an output channel ordering;\n"
+ "\ttossing samples at sound card, hoping something reasonable happens.\n");
+ }
+ }else{
+ /* permute/reconcile the matrices before playback begins */
+ if(strcmp(A->matrix,B->matrix))
+ reconcile_channel_maps(A,B);
+ }
+ }
+
+ /* Are the samples the same length? If not, warn and choose the shorter. */
+ if(A->size!=B->size){
+ if(verbose){
+ fprintf(stderr,"Input sample lengths do not match!\n");
+ fprintf(stderr,"\t%s: %s\n",A->path,make_time_string((double)A->size/A->ch/((A->bits+7)/8)/A->rate));
+ fprintf(stderr,"\t%s: %s\n",B->path,make_time_string((double)B->size/B->ch/((B->bits+7)/8)/B->rate));
+ fprintf(stderr,"Using the shorter sample for playback length...\n");
+ }
+ if(A->size>B->size)
+ A->size=B->size;
+ else
+ B->size=A->size;
+ }
+
+ /* playback loop */
+
+
+
+
+
+
+ /* done */
+ ao_close(adev);
+ ao_shutdown();
+ return 0;
+}
Added: websites/celt-codec.org/squishyball/squishyball.1
===================================================================
--- websites/celt-codec.org/squishyball/squishyball.1 (rev 0)
+++ websites/celt-codec.org/squishyball/squishyball.1 2010-11-19 04:56:13 UTC (rev 17633)
@@ -0,0 +1,20 @@
+ "CONVERSION AND DITHER:\n"
+ " All integer PCM file types are loaded at native bit depth.\n"
+ " Floating point file types (WAV format 3 and Ogg Vorbis)\n"
+ " are decoded to 24 integer precision.\n\n"
+ " If files A and B have different bit-depths, the lower-depth\n"
+ " file is promoted to the higher-depth prior to playback.\n"
+ " Similarly if the files have different channel orderings, the\n"
+ " channels are permuted to matching orderings before playback.\n"
+ " The files will thus have identical formatting in memory during\n"
+ " playback in an effort to exert identical hardware load.\n\n"
+ " If file playback would require 24 bit depth and the selected\n"
+ " output device supports only 16 bit depth, samples are down-\n"
+ " converted to 16 bit. Any samples that were originally 24 bit\n"
+ " (or 32 bit float) will be dithered during conversion; the\n"
+ " exception is Ogg Vorbis files. As most Vorbis files are\n"
+ " made from 16 bit sources, they will not be dithered during\n"
+ " down-conversion by default. This behavior is overridden by\n"
+ " -d, which forces dithering for Vorbis files as well. Down-\n"
+ " conversion dithering can be disabled for all input types\n"
+ " with -n.\n\n"
More information about the commits
mailing list