[xiph-commits] r17672 - trunk/squishyball

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Sun Nov 28 10:22:19 PST 2010


Author: xiphmont
Date: 2010-11-28 10:22:18 -0800 (Sun, 28 Nov 2010)
New Revision: 17672

Added:
   trunk/squishyball/COPYING
   trunk/squishyball/audio.c
   trunk/squishyball/loader.c
Modified:
   trunk/squishyball/Makefile.am
   trunk/squishyball/main.c
   trunk/squishyball/main.h
Log:
Split up huge monolithic main.c by function
Add licensing


Added: trunk/squishyball/COPYING
===================================================================
--- trunk/squishyball/COPYING	                        (rev 0)
+++ trunk/squishyball/COPYING	2010-11-28 18:22:18 UTC (rev 17672)
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Modified: trunk/squishyball/Makefile.am
===================================================================
--- trunk/squishyball/Makefile.am	2010-11-28 14:14:43 UTC (rev 17671)
+++ trunk/squishyball/Makefile.am	2010-11-28 18:22:18 UTC (rev 17672)
@@ -8,7 +8,7 @@
 mandir = @MANDIR@
 man_MANS = squishyball.1
 
-squishyball_SOURCES = main.c main.h tty.c mincurses.c mincurses.h
+squishyball_SOURCES = audio.c loader.c main.c mincurses.c tty.c main.h mincurses.h
 
 debug:
 	$(MAKE) all CFLAGS="@DEBUG@"

Added: trunk/squishyball/audio.c
===================================================================
--- trunk/squishyball/audio.c	                        (rev 0)
+++ trunk/squishyball/audio.c	2010-11-28 18:22:18 UTC (rev 17672)
@@ -0,0 +1,679 @@
+/*
+ *
+ *  squishyball
+ *
+ *      Copyright (C) 2010 Xiph.Org
+ *
+ *  squishyball 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, or (at your option)
+ *  any later version.
+ *
+ *  squishyball 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 rtrecord; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ao/ao.h>
+#include "main.h"
+
+/* sample formatting helpers ********************************************************/
+
+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;
+}
+
+static inline float triangle_ditherval(float *save){
+  float r = rand()/(float)RAND_MAX-.5f;
+  float ret = *save-r;
+  *save = r;
+  return ret;
+}
+
+static void float32_to_24(pcm_t *pcm){
+  unsigned char *d = pcm->data;
+  off_t j;
+  if(sb_verbose)
+    fprintf(stderr,"\rConverting %s to 24 bit... ",pcm->path);
+  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;
+  }
+  if(sb_verbose)
+    fprintf(stderr,"...done.\n");
+  pcm->bits=24;
+  pcm->size/=4;
+  pcm->size*=3;
+}
+
+static void float32_to_16(pcm_t *pcm){
+  unsigned char *d = pcm->data;
+  off_t j;
+  union {
+    float f;
+    unsigned char c[4];
+  } m;
+
+  if(sb_verbose)
+    fprintf(stderr,"\r%s %s to 16 bit... ",
+            pcm->dither?"Dithering":"Down-converting",pcm->path);
+
+  /* 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);
+  }
+
+  if(sb_verbose)
+    fprintf(stderr,"...done.\n");
+
+  pcm->bits=16;
+  pcm->size/=2;
+}
+
+static void demote_24_to_16(pcm_t *pcm){
+  float t[pcm->ch];
+  unsigned char *d = pcm->data;
+  off_t i;
+  int ch=0;
+
+  if(sb_verbose)
+    fprintf(stderr,"\r%s %s to 16 bit... ",
+            pcm->dither?"Dithering":"Down-converting",pcm->path);
+
+  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;
+    }
+  }
+
+  if(sb_verbose)
+    fprintf(stderr,"...done.\n");
+
+  pcm->bits=16;
+  pcm->size/=3;
+  pcm->size*=2;
+}
+
+static void promote_to_24(pcm_t *pcm){
+  off_t i;
+
+  if(sb_verbose)
+    fprintf(stderr,"\rPromoting %s to 24 bit... ",pcm->path);
+
+  {
+    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;
+    }
+  }
+  if(sb_verbose)
+    fprintf(stderr,"...done.\n");
+
+  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);
+  }
+}
+
+/* Channel map reconciliation helpers *********************************/
+
+static const char *chlist[]={"X","M","L","R","C","LFE","SL","SR","BC","BL","BR","CL","CR",NULL};
+
+static 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);
+}
+
+/* fade and beep function generation **************************************/
+
+void put_val(unsigned char *d,int bps,float v){
+  int i = rint(v);
+  d[0]=i&0xff;
+  d[1]=(i>>8)&0xff;
+  if(bps==3)
+    d[2]=(i>>16)&0xff;
+}
+
+float get_val(unsigned char *d, int bps){
+  if(bps==2){
+    short i = d[0] | (d[1]<<8);
+    return (float)i;
+  }else{
+    int32_t i = ((d[0]<<8) | (d[1]<<16) | (d[2]<<24))>>8;
+    return (float)i;
+  }
+}
+
+int setup_windows(pcm_t **pcm, int test_files,
+                         float **fw1, float **fw2, float **fw3,
+                         float **b1, float **b2){
+  int i;
+  int fragsamples = pcm[0]->rate/10;  /* 100ms */
+  float mul = (pcm[0]->bits==24 ? 8388608.f : 32768.f) * .0625;
+  /* precompute the fades/beeps */
+  float *fadewindow1 = *fw1 = calloc(fragsamples,sizeof(*fadewindow1));
+  float *fadewindow2 = *fw2 = calloc(fragsamples,sizeof(*fadewindow2));
+  float *fadewindow3 = *fw3 = calloc(fragsamples,sizeof(*fadewindow3));
+  float *beep1 = *b1 = calloc(fragsamples,sizeof(*beep1));
+  float *beep2 = *b2 = calloc(fragsamples,sizeof(*beep2));
+
+  if(!fadewindow1 ||
+     !fadewindow2 ||
+     !fadewindow3 ||
+     !beep1 ||
+     !beep2)
+    exit(9);
+
+  /* fadewindow1 is a larger simple crossfade */
+  for(i=0;i<fragsamples;i++){
+    float val = cosf(M_PI*.5f*(i+.5f)/fragsamples);
+    fadewindow1[i] = val*val;
+  }
+
+  /* fadewindow2 goes to silence and back */
+  for(i=0;i<fragsamples/3;i++){
+    float val = cosf(M_PI*1.5f*(i+.5f)/fragsamples);
+    fadewindow2[i] = val*val;
+  }
+  for(;i<fragsamples;i++)
+    fadewindow2[i] = 0.f;
+
+  /* fadewindow3 crossfades with attenuation to give headroom for a beep */
+  for(i=0;i<fragsamples/4;i++){
+    float val = cosf(M_PI*2.f*(i+.5f)/fragsamples);
+    fadewindow3[i] = val*val*.875f+.125f;
+  }
+  for(;i<fragsamples*3/4;i++)
+    fadewindow3[i] = .125f;
+  for(;i<fragsamples;i++){
+    float val = cosf(M_PI*2.f*(i-fragsamples*3/4+.5f)/fragsamples);
+    fadewindow3[i] = val*val*.125f;
+  }
+
+  /* Single beep for flipping */
+  for(i=0;i<fragsamples/4;i++){
+    beep1[i]=0.f;
+    beep1[fragsamples-i-1]=0.f;
+  }
+  float base = 3.14159f*2.f*1000./pcm[0]->rate;
+  for(;i<fragsamples*3/4;i++){
+    float f = i-fragsamples/4+.5f;
+    float w = cosf(3.14159f*f/fragsamples);
+    float b =
+      sinf(f*base)+
+      sinf(f*base*3)*.33f+
+      sinf(f*base*5)*.2f+
+      sinf(f*base*7)*.14f+
+      sinf(f*base*9)*.11f;
+    w*=w;
+    beep1[i] = w*b*mul;
+  }
+
+  /* Double beep for selection */
+  for(i=0;i<fragsamples/4;i++){
+    beep2[i]=0.f;
+    beep2[fragsamples-i-1]=0.f;
+  }
+  for(;i<fragsamples/2;i++){
+    float f = i-fragsamples/4+.5f;
+    float w = cosf(3.14159f*2.f*f/fragsamples);
+    float b =
+      sinf(f*base)+
+      sinf(f*base*3)*.33f+
+      sinf(f*base*5)*.2f+
+      sinf(f*base*7)*.14f+
+      sinf(f*base*9)*.11f;
+    w*=w;
+    beep2[i] = w*b*mul;
+  }
+  base = 3.14159f*2.f*1500./pcm[0]->rate;
+  for(;i<fragsamples*3/4;i++){
+    float f = i-fragsamples/2+.5f;
+    float w = cosf(3.14159f*2.f*f/fragsamples);
+    float b =
+      sinf(f*base)+
+      sinf(f*base*3)*.33f+
+      sinf(f*base*5)*.2f+
+      sinf(f*base*7)*.14f+
+      sinf(f*base*9)*.11f;
+    w*=w;
+    beep2[i] = w*b*mul*2;
+  }
+
+  /* make sure that the samples are at least fragsamples*3 in length! If they're not, extend... */
+  {
+    int bps = (pcm[0]->bits+7)/8;
+    int ch = pcm[0]->ch;
+    int bpf = bps*ch;
+
+    if(pcm[0]->size<fragsamples*bpf*3){
+      int fadesize = pcm[0]->size/4;
+
+      for(i=0;i<test_files;i++){
+        int j,k;
+        unsigned char *newd=calloc(fragsamples*3,bpf);
+        if(!newd){
+          fprintf(stderr,"Unable to allocate memory to extend sample to minimum length.\n");
+          exit(5);
+        }
+        memcpy(newd,pcm[i]->data,fragsamples*3*bpf);
+        free(pcm[i]->data);
+        pcm[i]->data=newd;
+
+        newd+=pcm[i]->size-fadesize;
+        for(j=0;j<fadesize;j++){
+          float v = cosf(M_PI*.5f*(i+.5f)/fadesize);
+          for(k=0;k<ch;k++){
+            put_val(newd,bps,v * get_val(newd,bps));
+            newd+=bps;
+          }
+        }
+        pcm[i]->size=fragsamples*3;
+      }
+    }
+  }
+  return fragsamples;
+}
+
+/* fragment is filled such that a crossloop never begins after
+   pcm->size-fragsize, and it always begins from the start of the
+   window, even if that means starting a crossloop late because the
+   endpos moved. */
+void fill_fragment1(unsigned char *out, pcm_t *pcm, off_t start, off_t *pos, off_t end, int *loop,
+                    int fragsamples, float *fadewindow){
+  int bps = (pcm->bits+7)/8;
+  int cpf = pcm->ch;
+  int bpf = bps*cpf;
+  int fragsize = fragsamples*bpf;
+
+  /* guard limits here */
+  if(end<fragsize*3)end=fragsize*3;
+  if(end>pcm->size)end=pcm->size;
+  if(start<0)start=0;
+  if(start>pcm->size-fragsize*3)start=pcm->size-fragsize*3;
+
+  /* we fill a fragment from the data buffer of the passed in pcm_t.
+     It's possible we'll need to crossloop from the end of the sample,
+     and the start/end markers may have moved so that the cursor is
+     outside the strict sample bounds. */
+
+  /* if *loop>0, we're in the process of crosslapping at pos ><
+     start+(fragsize-*loop*bpf). Stay the course. */
+  if(*loop){
+    int lp = *loop;
+    int i,j;
+    unsigned char *A = pcm->data+*pos;
+    unsigned char *B = pcm->data+start+(fragsamples-lp)*bpf;
+    for(i=0;i<fragsamples;i++){
+      if(lp){
+        float w = fadewindow[--lp];
+        for(j=0;j<cpf;j++){
+          float val = get_val(A,bps)*(1.f-w) + get_val(B,bps)*w;
+          put_val(out,val,bps);
+          A+=bps;
+          B+=bps;
+          out+=bps;
+        }
+      }else{
+        /* crossloop finished, the rest is B */
+        memcpy(out,B,bpf);
+        B+=bpf;
+        out+=bpf;
+      }
+    }
+    *loop=0;
+    *pos=B-pcm->data;
+  }else{
+    /* no crossloop in progress... should one be? If the cursor is
+       before start, do nothing.  If it's past end-fragsize, begin a
+       crossloop immediately.  If the current fragment will extend
+       beyond end-fragsize, begin the crossloop at end-fragsize */
+    if(*pos>pcm->size-fragsize){
+      /* Error condition; should not be possible */
+      fprintf(stderr,"Internal error; %ld>%ld, Monty fucked up.\n",(long)*pos,(long)pcm->size-fragsize);
+      exit(100);
+    }else if(*pos+fragsize>end-fragsize){
+      int i,j;
+      unsigned char *A = pcm->data+*pos;
+      unsigned char *B = pcm->data+start;
+      int lp = (end-*pos)/bpf;
+      if(lp<fragsamples)lp=fragsamples; /* If we're late, start immediately, but use full window */
+
+      for(i=0;i<fragsamples;i++){
+        if(--lp>=fragsamples){
+          /* still before crossloop begins */
+          memcpy(out,A,bpf);
+          A+=bpf;
+          out+=bpf;
+        }else{
+          /* crosslooping */
+          float w = fadewindow[lp];
+          for(j=0;j<cpf;j++){
+            float val = get_val(A,bps)*(1.f-w) + get_val(B,bps)*w;
+            put_val(out,bps,val);
+            A+=bps;
+            B+=bps;
+            out+=bps;
+          }
+        }
+      }
+      *loop=(lp<0?0:lp);
+      *pos=(lp<=0?B-pcm->data:A-pcm->data);
+    }else{
+      /* no crossloop */
+      unsigned char *A = pcm->data+*pos;
+      memcpy(out,A,fragsize);
+      *loop=0;
+      *pos+=fragsize;
+    }
+  }
+}
+
+/* fragment is filled such that a crossloop is always 'exactly on
+   schedule' even if that means beginning partway through the window. */
+void fill_fragment2(unsigned char *out, pcm_t *pcm, off_t start, off_t *pos, off_t end, int *loop,
+                    int fragsamples, float *fadewindow){
+  int bps = (pcm->bits+7)/8;
+  int cpf = pcm->ch;
+  int bpf = bps*cpf;
+  int fragsize=fragsamples*bpf;
+
+  /* guard limits here */
+  if(end<fragsize*3)end=fragsize*3;
+  if(end>pcm->size)end=pcm->size;
+  if(start<0)start=0;
+  if(start>pcm->size-fragsize*3)start=pcm->size-fragsize*3;
+
+  /* loop is never in progress for a fill_fragment2; called only during a seek crosslap */
+  unsigned char *A = pcm->data+*pos;
+  if(end-*pos>=fragsize*2){
+    /* no crosslap */
+    memcpy(out,A,fragsize);
+    *loop=0;
+    *pos=A-pcm->data+fragsize;
+  }else{
+    /* just before crossloop, in the middle of a crossloop, or just after crossloop */
+    int i,j;
+    int lp = (end-*pos)/bpf;
+    unsigned char *B = pcm->data+start;
+    if(lp<fragsamples)B+=(fragsamples-lp)*bpf;
+
+    for(i=0;i<fragsamples;i++){
+      --lp;
+      if(lp>=fragsamples){
+        /* not yet crosslooping */
+        memcpy(out,A,bpf);
+        A+=bpf;
+        out+=bpf;
+      }else if (lp>=0){
+        /* now crosslooping */
+        float w = fadewindow[lp];
+        for(j=0;j<cpf;j++){
+          float val = get_val(A,bps)*(1.-w) + get_val(B,bps)*w;
+          put_val(out,val,bps);
+          A+=bps;
+          B+=bps;
+          out+=bps;
+        }
+      }else{
+        /* after crosslap */
+        memcpy(out,B,bpf);
+        B+=bpf;
+        out+=bpf;
+       }
+    }
+    *loop=(lp>0?(lp<fragsamples?lp:fragsamples):0);
+    *pos=(lp>0?A-pcm->data:B-pcm->data);
+  }
+}
+
+/* playback ******************************************************/
+
+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};
+      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 && sb_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;
+}
+

Added: trunk/squishyball/loader.c
===================================================================
--- trunk/squishyball/loader.c	                        (rev 0)
+++ trunk/squishyball/loader.c	2010-11-28 18:22:18 UTC (rev 17672)
@@ -0,0 +1,1034 @@
+/*
+ *
+ *  squishyball
+ *
+ *      Copyright (C) 2010 Xiph.Org
+ *
+ *  squishyball 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, or (at your option)
+ *  any later version.
+ *
+ *  squishyball 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 rtrecord; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+#include <vorbis/vorbisfile.h>
+#include <FLAC/stream_decoder.h>
+#include <unistd.h>
+#include "main.h"
+
+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{
+  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))
+
+static 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;
+}
+
+static 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;
+}
+
+static int flac_id(char *path,unsigned char *buf){
+  return memcmp(buf, "fLaC", 4) == 0;
+}
+
+static 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));
+}
+
+static int vorbis_id(char *path,unsigned char *buf){
+  return memcmp(buf, "OggS", 4) == 0 &&
+    memcmp (buf+28, "\x01vorbis", 7);
+}
+
+static 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;
+    }
+  }
+}
+
+static 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(sb_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(sb_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(sb_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(sb_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(sb_verbose)
+    fprintf(stderr,"\r%s: loaded.                 \n",path);
+
+  return pcm;
+ err:
+  free_pcm(pcm);
+  return NULL;
+}
+
+/* AIFF file support ***********************************************************/
+
+static 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;
+  }
+}
+
+static 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;
+}
+
+static 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;
+  default:
+    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(sb_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(sb_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(sb_verbose)
+    fprintf(stderr,"\r%s: loaded.                 \n",path);
+
+  return pcm;
+ err:
+  free_pcm(pcm);
+  return NULL;
+
+}
+
+/* SW loading to make JM happy *******************************************************/
+
+static 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;
+  pcm->matrix=strdup("M");
+
+  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(sb_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(sb_verbose)
+        fprintf(stderr,"\r%s: File ended before declared length (%ld < %ld); continuing...\n",path,(long)j,(long)pcm->size);
+      pcm->size=j;
+    }
+  }
+
+  if(sb_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 */
+static 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(sb_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;
+}
+
+static 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;
+}
+
+static 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;
+  }
+}
+
+static 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);
+}
+
+static 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;
+}
+
+static 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(sb_verbose)
+    fprintf(stderr,"\r%s: loaded.                 \n",path);
+  return pcm;
+}
+
+static pcm_t *flac_load(char *path, FILE *in){
+  return flac_load_i(path,in,0);
+}
+
+static pcm_t *oggflac_load(char *path, FILE *in){
+  return flac_load_i(path,in,1);
+}
+
+/* Vorbis load support **************************************************************************/
+static 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,&current_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 (sb_verbose && (throttle&0x3f)==0)
+      fprintf(stderr,"\rLoading %s: %ld to go...       ",pcm->path,(long)(pcm->size-fill));
+    throttle++;
+  }
+  ov_clear(&vf);
+
+  if(sb_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 */
+static 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;
+}
+
+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);
+  }
+}
+

Modified: trunk/squishyball/main.c
===================================================================
--- trunk/squishyball/main.c	2010-11-28 14:14:43 UTC (rev 17671)
+++ trunk/squishyball/main.c	2010-11-28 18:22:18 UTC (rev 17672)
@@ -1,3 +1,26 @@
+/*
+ *
+ *  squishyball
+ *
+ *      Copyright (C) 2010 Xiph.Org
+ *
+ *  squishyball 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, or (at your option)
+ *  any later version.
+ *
+ *  squishyball 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 rtrecord; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
 #define _GNU_SOURCE
 #define _LARGEFILE_SOURCE
 #define _LARGEFILE64_SOURCE
@@ -22,1329 +45,10 @@
 #include "main.h"
 
 #define MAXFILES 10
-static int verbose=0;
+int sb_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;
-}
+char *short_options="abcd:De:hn:rRs:tvVxBMS";
 
-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;
-  default:
-    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;
-  pcm->matrix=strdup("M");
-
-  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,&current_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;
-  if(verbose)
-    fprintf(stderr,"\rConverting %s to 24 bit... ",pcm->path);
-  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;
-  }
-  if(verbose)
-    fprintf(stderr,"...done.\n");
-  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;
-
-  if(verbose)
-    fprintf(stderr,"\r%s %s to 16 bit... ",
-            pcm->dither?"Dithering":"Down-converting",pcm->path);
-
-  /* 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);
-  }
-
-  if(verbose)
-    fprintf(stderr,"...done.\n");
-
-  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;
-
-  if(verbose)
-    fprintf(stderr,"\r%s %s to 16 bit... ",
-            pcm->dither?"Dithering":"Down-converting",pcm->path);
-
-  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;
-    }
-  }
-
-  if(verbose)
-    fprintf(stderr,"...done.\n");
-
-  pcm->bits=16;
-  pcm->size/=3;
-  pcm->size*=2;
-}
-
-void promote_to_24(pcm_t *pcm){
-  off_t i;
-
-  if(verbose)
-    fprintf(stderr,"\rPromoting %s to 24 bit... ",pcm->path);
-
-  {
-    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;
-    }
-  }
-  if(verbose)
-    fprintf(stderr,"...done.\n");
-
-  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[]={"X","M","L","R","C","LFE","SL","SR","BC","BL","BR","CL","CR",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};
-      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'},
@@ -1366,7 +70,6 @@
   {"xxy",no_argument,0,'x'},
   {0,0,0,0}
 };
-char *short_options="abcd:De:hn:rRs:tvVxBMS";
 
 void usage(FILE *out){
   fprintf(out,
@@ -1474,157 +177,38 @@
   return 0;
 }
 
-float *fadewindow1;
-float *fadewindow2;
-float *fadewindow3;
-float *beep1;
-float *beep2;
-
-void put_val(unsigned char *d,int bps,float v){
-  int i = rint(v);
-  d[0]=i&0xff;
-  d[1]=(i>>8)&0xff;
-  if(bps==3)
-    d[2]=(i>>16)&0xff;
-}
-
-float get_val(unsigned char *d, int bps){
-  if(bps==2){
-    short i = d[0] | (d[1]<<8);
-    return (float)i;
-  }else{
-    int32_t i = ((d[0]<<8) | (d[1]<<16) | (d[2]<<24))>>8;
-    return (float)i;
+void randomize_samples(int *r,int *cchoice, int test_mode){
+  switch(test_mode){
+  case 1:
+    r[0] = random()&1;
+    r[1] = 1-r[0];
+    r[2] = random()&1;
+    *cchoice = (r[1]==r[2] ? 1 : 0);
+    break;
+  case 0:
+    r[0] = random()&1;
+    r[1] = 1-r[0];
+    *cchoice = 1;
+    break;
+  case 2:
+    r[0] = random()&1;
+    r[1] = random()&1;
+    if(r[0] == r[1])
+      r[2]=1-r[0];
+    else
+      r[2] = random()&1;
+    break;
+    *cchoice = (r[0]==r[1] ? 2 : (r[1]==r[2] ? 0 : 1));
   }
 }
 
-int setup_windows(pcm_t **pcm, int test_files){
-  int i;
-  int fragsamples = pcm[0]->rate/10;  /* 100ms */
-  float mul = (pcm[0]->bits==24 ? 8388608.f : 32768.f) * .0625;
-  /* precompute the fades/beeps */
-  fadewindow1 = calloc(fragsamples,sizeof(*fadewindow1));
-  fadewindow2 = calloc(fragsamples,sizeof(*fadewindow2));
-  fadewindow3 = calloc(fragsamples,sizeof(*fadewindow3));
-  beep1 = calloc(fragsamples,sizeof(*beep1));
-  beep2 = calloc(fragsamples,sizeof(*beep2));
-
-  if(!fadewindow1 ||
-     !fadewindow2 ||
-     !fadewindow3 ||
-     !beep1 ||
-     !beep2)
-    exit(9);
-
-  /* fadewindow1 is a larger simple crossfade */
-  for(i=0;i<fragsamples;i++){
-    float val = cosf(M_PI*.5f*(i+.5f)/fragsamples);
-    fadewindow1[i] = val*val;
+double factorial(int x){
+  double f = 1.;
+  while(x>1){
+    f*=x;
+    x--;
   }
-
-  /* fadewindow2 goes to silence and back */
-  for(i=0;i<fragsamples/3;i++){
-    float val = cosf(M_PI*1.5f*(i+.5f)/fragsamples);
-    fadewindow2[i] = val*val;
-  }
-  for(;i<fragsamples;i++)
-    fadewindow2[i] = 0.f;
-
-  /* fadewindow3 crossfades with attenuation to give headroom for a beep */
-  for(i=0;i<fragsamples/4;i++){
-    float val = cosf(M_PI*2.f*(i+.5f)/fragsamples);
-    fadewindow3[i] = val*val*.875f+.125f;
-  }
-  for(;i<fragsamples*3/4;i++)
-    fadewindow3[i] = .125f;
-  for(;i<fragsamples;i++){
-    float val = cosf(M_PI*2.f*(i-fragsamples*3/4+.5f)/fragsamples);
-    fadewindow3[i] = val*val*.125f;
-  }
-
-  /* Single beep for flipping */
-  for(i=0;i<fragsamples/4;i++){
-    beep1[i]=0.f;
-    beep1[fragsamples-i-1]=0.f;
-  }
-  float base = 3.14159f*2.f*1000./pcm[0]->rate;
-  for(;i<fragsamples*3/4;i++){
-    float f = i-fragsamples/4+.5f;
-    float w = cosf(3.14159f*f/fragsamples);
-    float b =
-      sinf(f*base)+
-      sinf(f*base*3)*.33f+
-      sinf(f*base*5)*.2f+
-      sinf(f*base*7)*.14f+
-      sinf(f*base*9)*.11f;
-    w*=w;
-    beep1[i] = w*b*mul;
-  }
-
-  /* Double beep for selection */
-  for(i=0;i<fragsamples/4;i++){
-    beep2[i]=0.f;
-    beep2[fragsamples-i-1]=0.f;
-  }
-  for(;i<fragsamples/2;i++){
-    float f = i-fragsamples/4+.5f;
-    float w = cosf(3.14159f*2.f*f/fragsamples);
-    float b =
-      sinf(f*base)+
-      sinf(f*base*3)*.33f+
-      sinf(f*base*5)*.2f+
-      sinf(f*base*7)*.14f+
-      sinf(f*base*9)*.11f;
-    w*=w;
-    beep2[i] = w*b*mul;
-  }
-  base = 3.14159f*2.f*1500./pcm[0]->rate;
-  for(;i<fragsamples*3/4;i++){
-    float f = i-fragsamples/2+.5f;
-    float w = cosf(3.14159f*2.f*f/fragsamples);
-    float b =
-      sinf(f*base)+
-      sinf(f*base*3)*.33f+
-      sinf(f*base*5)*.2f+
-      sinf(f*base*7)*.14f+
-      sinf(f*base*9)*.11f;
-    w*=w;
-    beep2[i] = w*b*mul*2;
-  }
-
-  /* make sure that the samples are at least fragsamples*3 in length! If they're not, extend... */
-  {
-    int bps = (pcm[0]->bits+7)/8;
-    int ch = pcm[0]->ch;
-    int bpf = bps*ch;
-
-    if(pcm[0]->size<fragsamples*bpf*3){
-      int fadesize = pcm[0]->size/4;
-
-      for(i=0;i<test_files;i++){
-        int j,k;
-        unsigned char *newd=calloc(fragsamples*3,bpf);
-        if(!newd){
-          fprintf(stderr,"Unable to allocate memory to extend sample to minimum length.\n");
-          exit(5);
-        }
-        memcpy(newd,pcm[i]->data,fragsamples*3*bpf);
-        free(pcm[i]->data);
-        pcm[i]->data=newd;
-
-        newd+=pcm[i]->size-fadesize;
-        for(j=0;j<fadesize;j++){
-          float v = cosf(M_PI*.5f*(i+.5f)/fadesize);
-          for(k=0;k<ch;k++){
-            put_val(newd,bps,v * get_val(newd,bps));
-            newd+=bps;
-          }
-        }
-        pcm[i]->size=fragsamples*3;
-      }
-    }
-  }
-  return fragsamples;
+  return f;
 }
 
 typedef struct {
@@ -1706,191 +290,12 @@
   return NULL;
 }
 
-/* fragment is filled such that a crossloop never begins after
-   pcm->size-fragsize, and it always begins from the start of the
-   window, even if that means starting a crossloop late because the
-   endpos moved. */
-void fill_fragment1(unsigned char *out, pcm_t *pcm, off_t start, off_t *pos, off_t end, int *loop,int fragsamples){
-  int bps = (pcm->bits+7)/8;
-  int cpf = pcm->ch;
-  int bpf = bps*cpf;
-  int fragsize = fragsamples*bpf;
-
-  /* guard limits here */
-  if(end<fragsize*3)end=fragsize*3;
-  if(end>pcm->size)end=pcm->size;
-  if(start<0)start=0;
-  if(start>pcm->size-fragsize*3)start=pcm->size-fragsize*3;
-
-  /* we fill a fragment from the data buffer of the passed in pcm_t.
-     It's possible we'll need to crossloop from the end of the sample,
-     and the start/end markers may have moved so that the cursor is
-     outside the strict sample bounds. */
-
-  /* if *loop>0, we're in the process of crosslapping at pos ><
-     start+(fragsize-*loop*bpf). Stay the course. */
-  if(*loop){
-    int lp = *loop;
-    int i,j;
-    unsigned char *A = pcm->data+*pos;
-    unsigned char *B = pcm->data+start+(fragsamples-lp)*bpf;
-    for(i=0;i<fragsamples;i++){
-      if(lp){
-        float w = fadewindow1[--lp];
-        for(j=0;j<cpf;j++){
-          float val = get_val(A,bps)*(1.f-w) + get_val(B,bps)*w;
-          put_val(out,val,bps);
-          A+=bps;
-          B+=bps;
-          out+=bps;
-        }
-      }else{
-        /* crossloop finished, the rest is B */
-        memcpy(out,B,bpf);
-        B+=bpf;
-        out+=bpf;
-      }
-    }
-    *loop=0;
-    *pos=B-pcm->data;
-  }else{
-    /* no crossloop in progress... should one be? If the cursor is
-       before start, do nothing.  If it's past end-fragsize, begin a
-       crossloop immediately.  If the current fragment will extend
-       beyond end-fragsize, begin the crossloop at end-fragsize */
-    if(*pos>pcm->size-fragsize){
-      /* Error condition; should not be possible */
-      fprintf(stderr,"Internal error; %ld>%ld, Monty fucked up.\n",(long)*pos,(long)pcm->size-fragsize);
-      exit(100);
-    }else if(*pos+fragsize>end-fragsize){
-      int i,j;
-      unsigned char *A = pcm->data+*pos;
-      unsigned char *B = pcm->data+start;
-      int lp = (end-*pos)/bpf;
-      if(lp<fragsamples)lp=fragsamples; /* If we're late, start immediately, but use full window */
-
-      for(i=0;i<fragsamples;i++){
-        if(--lp>=fragsamples){
-          /* still before crossloop begins */
-          memcpy(out,A,bpf);
-          A+=bpf;
-          out+=bpf;
-        }else{
-          /* crosslooping */
-          float w = fadewindow1[lp];
-          for(j=0;j<cpf;j++){
-            float val = get_val(A,bps)*(1.f-w) + get_val(B,bps)*w;
-            put_val(out,bps,val);
-            A+=bps;
-            B+=bps;
-            out+=bps;
-          }
-        }
-      }
-      *loop=(lp<0?0:lp);
-      *pos=(lp<=0?B-pcm->data:A-pcm->data);
-    }else{
-      /* no crossloop */
-      unsigned char *A = pcm->data+*pos;
-      memcpy(out,A,fragsize);
-      *loop=0;
-      *pos+=fragsize;
-    }
-  }
-}
-
-/* fragment is filled such that a crossloop is always 'exactly on
-   schedule' even if that means beginning partway through the window. */
-void fill_fragment2(unsigned char *out, pcm_t *pcm, off_t start, off_t *pos, off_t end, int *loop,int fragsamples){
-  int bps = (pcm->bits+7)/8;
-  int cpf = pcm->ch;
-  int bpf = bps*cpf;
-  int fragsize=fragsamples*bpf;
-
-  /* guard limits here */
-  if(end<fragsize*3)end=fragsize*3;
-  if(end>pcm->size)end=pcm->size;
-  if(start<0)start=0;
-  if(start>pcm->size-fragsize*3)start=pcm->size-fragsize*3;
-
-  /* loop is never in progress for a fill_fragment2; called only during a seek crosslap */
-  unsigned char *A = pcm->data+*pos;
-  if(end-*pos>=fragsize*2){
-    /* no crosslap */
-    memcpy(out,A,fragsize);
-    *loop=0;
-    *pos=A-pcm->data+fragsize;
-  }else{
-    /* just before crossloop, in the middle of a crossloop, or just after crossloop */
-    int i,j;
-    int lp = (end-*pos)/bpf;
-    unsigned char *B = pcm->data+start;
-    if(lp<fragsamples)B+=(fragsamples-lp)*bpf;
-
-    for(i=0;i<fragsamples;i++){
-      --lp;
-      if(lp>=fragsamples){
-        /* not yet crosslooping */
-        memcpy(out,A,bpf);
-        A+=bpf;
-        out+=bpf;
-      }else if (lp>=0){
-        /* now crosslooping */
-        float w = fadewindow1[lp];
-        for(j=0;j<cpf;j++){
-          float val = get_val(A,bps)*(1.-w) + get_val(B,bps)*w;
-          put_val(out,val,bps);
-          A+=bps;
-          B+=bps;
-          out+=bps;
-        }
-      }else{
-        /* after crosslap */
-        memcpy(out,B,bpf);
-        B+=bpf;
-        out+=bpf;
-       }
-    }
-    *loop=(lp>0?(lp<fragsamples?lp:fragsamples):0);
-    *pos=(lp>0?A-pcm->data:B-pcm->data);
-  }
-}
-
-void randomize_samples(int *r,int *cchoice, int test_mode){
-  switch(test_mode){
-  case 1:
-    r[0] = random()&1;
-    r[1] = 1-r[0];
-    r[2] = random()&1;
-    *cchoice = (r[1]==r[2] ? 1 : 0);
-    break;
-  case 0:
-    r[0] = random()&1;
-    r[1] = 1-r[0];
-    *cchoice = 1;
-    break;
-  case 2:
-    r[0] = random()&1;
-    r[1] = random()&1;
-    if(r[0] == r[1])
-      r[2]=1-r[0];
-    else
-      r[2] = random()&1;
-    break;
-    *cchoice = (r[0]==r[1] ? 2 : (r[1]==r[2] ? 0 : 1));
-  }
-}
-
-double factorial(int x){
-  double f = 1.;
-  while(x>1){
-    f*=x;
-    x--;
-  }
-  return f;
-}
-
 int main(int argc, char **argv){
+  float *fadewindow1;
+  float *fadewindow2;
+  float *fadewindow3;
+  float *beep1;
+  float *beep2;
   int fragsamples;
   int fragsize;
   unsigned char *fragmentA;
@@ -1984,7 +389,7 @@
       restart_mode=2;
       break;
     case 'v':
-      verbose=1;
+      sb_verbose=1;
       break;
     case 'V':
       fprintf(stdout,"%s\n",VERSION);
@@ -2051,7 +456,7 @@
         fprintf(stderr,"Unable to open audio device for playback.\n");
         exit(4);
       }else{
-        if(verbose)
+        if(sb_verbose)
           fprintf(stderr,"24-bit playback unavailable; down-converting to 16-bit\n");
         outbits=16;
       }
@@ -2086,22 +491,23 @@
     }
 
     if(flag){
-      if(verbose)
+      if(sb_verbose)
         fprintf(stderr,"Input sample lengths do not match!\n");
 
       for(i=0;i<test_files;i++){
-        if(verbose)
+        if(sb_verbose)
         fprintf(stderr,"\t%s: %s\n",pcm[i]->path,
                 make_time_string((double)pcm[i]->size/pcm[i]->ch/((pcm[i]->bits+7)/8)/pcm[i]->rate,0));
         pcm[i]->size=n;
       }
-      if(verbose)
+      if(sb_verbose)
         fprintf(stderr,"Using the shortest sample for playback length...\n");
     }
   }
 
   /* set up various transition windows/beeps */
-  fragsamples=setup_windows(pcm,test_files);
+  fragsamples=setup_windows(pcm,test_files,
+                            &fadewindow1,&fadewindow2,&fadewindow3,&beep1,&beep2);
 
   /* set up terminal */
   atexit(min_panel_remove);
@@ -2408,15 +814,18 @@
             loop=0;
           }
         }else{
-          fill_fragment1(fragmentA, pcm[current_sample], start_pos, &current_pos, end_pos, &loop, fragsamples);
+          fill_fragment1(fragmentA, pcm[current_sample], start_pos, &current_pos, end_pos, &loop,
+                         fragsamples, fadewindow1);
           if(do_flip || do_seek || do_select){
             current_sample=randomize[current_choice];
             if(do_seek){
               current_pos=save_pos+seek_to;
-              fill_fragment2(fragmentB, pcm[current_sample], start_pos, &current_pos, end_pos, &loop, fragsamples);
+              fill_fragment2(fragmentB, pcm[current_sample], start_pos, &current_pos, end_pos, &loop,
+                             fragsamples, fadewindow1);
               seek_to=0;
             }else{
-              fill_fragment1(fragmentB, pcm[current_sample], start_pos, &save_pos, end_pos, &save_loop, fragsamples);
+              fill_fragment1(fragmentB, pcm[current_sample], start_pos, &save_pos, end_pos, &save_loop,
+                             fragsamples, fadewindow1);
             }
           }
         }

Modified: trunk/squishyball/main.h
===================================================================
--- trunk/squishyball/main.h	2010-11-28 14:14:43 UTC (rev 17671)
+++ trunk/squishyball/main.h	2010-11-28 18:22:18 UTC (rev 17672)
@@ -23,6 +23,7 @@
 
 #ifndef _SB__H_
 #define _SB__H_
+#include <ao/ao.h>
 
 #define MAXTRIALS 50
 typedef struct pcm_struct pcm_t;
@@ -38,7 +39,27 @@
   int dither;
 };
 
+extern int sb_verbose;
 
+extern pcm_t *load_audio_file(char *path);
+extern void free_pcm(pcm_t *pcm);
+
+extern void convert_to_16(pcm_t *pcm);
+extern void convert_to_24(pcm_t *pcm);
+extern void reconcile_channel_maps(pcm_t *A, pcm_t *B);
+extern void put_val(unsigned char *d,int bps,float v);
+extern float get_val(unsigned char *d, int bps);
+extern int setup_windows(pcm_t **pcm, int test_files,
+                         float **fw1, float **fw2, float **fw3,
+                         float **b1, float **b2);
+extern void fill_fragment1(unsigned char *out, pcm_t *pcm,
+                           off_t start, off_t *pos, off_t end, int *loop,
+                           int fragsamples, float *fw);
+extern void fill_fragment2(unsigned char *out, pcm_t *pcm,
+                           off_t start, off_t *pos, off_t end, int *loop,
+                           int fragsamples, float *fw);
+extern ao_device *setup_playback(int rate, int ch, int bits, char *matrix, char *device);
+
 extern char *make_time_string(double s,int pad);
 extern void panel_init(pcm_t **pcm, int test_files, int test_mode, double start, double end, double size,
                        int flip_mode,int repeat_mode,int trials,char *trial_list);



More information about the commits mailing list