[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,¤t_section);
+ unsigned char *d = pcm->data+fill;
+
+ if(current_section!=last_section){
+ last_section=current_section;
+ vi=ov_info(&vf,-1);
+ if(vi->channels != pcm->ch || vi->rate!=pcm->rate){
+ fprintf(stderr,"%s: Chained file changes channel count/sample rate\n",path);
+ goto err;
+ }
+ }
+
+ if(ret<0){
+ fprintf(stderr,"%s: Error while decoding file\n",path);
+ goto err;
+ }
+ if(ret==0){
+ fprintf(stderr,"%s: Audio data ended prematurely\n",path);
+ goto err;
+ }
+
+ if(sizeof(float)==4){
+ /* Assuming IEEE754, which is pedantically incorrect */
+ union {
+ float f;
+ unsigned char c[4];
+ } m;
+ if(host_is_big_endian()){
+ for(i=0;i<ret;i++){
+ for(j=0;j<pcm->ch;j++){
+ m.f=pcmout[j][i];
+ d[0] = m.c[3];
+ d[1] = m.c[2];
+ d[2] = m.c[1];
+ d[3] = m.c[0];
+ d+=4;
+ }
+ }
+ }else{
+ for(i=0;i<ret;i++){
+ for(j=0;j<pcm->ch;j++){
+ m.f=pcmout[j][i];
+ d[0] = m.c[0];
+ d[1] = m.c[1];
+ d[2] = m.c[2];
+ d[3] = m.c[3];
+ d+=4;
+ }
+ }
+ }
+ }else{
+ fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
+ exit(10);
+ }
+ fill += ret*pcm->ch*3;
+ if (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,¤t_section);
- unsigned char *d = pcm->data+fill;
-
- if(current_section!=last_section){
- last_section=current_section;
- vi=ov_info(&vf,-1);
- if(vi->channels != pcm->ch || vi->rate!=pcm->rate){
- fprintf(stderr,"%s: Chained file changes channel count/sample rate\n",path);
- goto err;
- }
- }
-
- if(ret<0){
- fprintf(stderr,"%s: Error while decoding file\n",path);
- goto err;
- }
- if(ret==0){
- fprintf(stderr,"%s: Audio data ended prematurely\n",path);
- goto err;
- }
-
- if(sizeof(float)==4){
- /* Assuming IEEE754, which is pedantically incorrect */
- union {
- float f;
- unsigned char c[4];
- } m;
- if(host_is_big_endian()){
- for(i=0;i<ret;i++){
- for(j=0;j<pcm->ch;j++){
- m.f=pcmout[j][i];
- d[0] = m.c[3];
- d[1] = m.c[2];
- d[2] = m.c[1];
- d[3] = m.c[0];
- d+=4;
- }
- }
- }else{
- for(i=0;i<ret;i++){
- for(j=0;j<pcm->ch;j++){
- m.f=pcmout[j][i];
- d[0] = m.c[0];
- d[1] = m.c[1];
- d[2] = m.c[2];
- d[3] = m.c[3];
- d+=4;
- }
- }
- }
- }else{
- fprintf(stderr,"Unhandled case: sizeof(float)!=4\n");
- exit(10);
- }
- fill += ret*pcm->ch*3;
- if (verbose && (throttle&0x3f)==0)
- fprintf(stderr,"\rLoading %s: %ld to go... ",pcm->path,(long)(pcm->size-fill));
- throttle++;
- }
- ov_clear(&vf);
-
- if(verbose)
- fprintf(stderr,"\r%s: loaded. \n",path);
- return pcm;
- err:
- ov_clear(&vf);
- free_pcm(pcm);
- return NULL;
-}
-
-#define MAX_ID_LEN 35
-unsigned char buf[MAX_ID_LEN];
-
-/* Define the supported formats here */
-input_format formats[] = {
- {wav_id, wav_load, "wav"},
- {aiff_id, aiff_load, "aiff"},
- {flac_id, flac_load, "flac"},
- {oggflac_id, oggflac_load,"oggflac"},
- {vorbis_id, vorbis_load, "oggvorbis"},
- {sw_id, sw_load, "sw"},
- {NULL, NULL, NULL}
-};
-
-pcm_t *load_audio_file(char *path){
- FILE *f = fopen(path,"rb");
- int j=0;
- int fill;
-
- if(!f){
- fprintf(stderr,"Unable to open file %s: %s\n",path,strerror(errno));
- return NULL;
- }
-
- fill = fread(buf, 1, MAX_ID_LEN, f);
- if(fill<MAX_ID_LEN){
- fprintf(stderr,"%s: Input file truncated or NULL\n",path);
- fclose(f);
- return NULL;
- }
-
- while(formats[j].id_func){
- if(formats[j].id_func(path,buf)){
- pcm_t *ret=formats[j].load_func(path,f);
- fclose(f);
- return ret;
- }
- j++;
- }
- return NULL;
-}
-
-/* sample formatting helpers ********************************************************/
-
-void float32_to_24(pcm_t *pcm){
- unsigned char *d = pcm->data;
- off_t j;
- 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, ¤t_pos, end_pos, &loop, fragsamples);
+ fill_fragment1(fragmentA, pcm[current_sample], start_pos, ¤t_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, ¤t_pos, end_pos, &loop, fragsamples);
+ fill_fragment2(fragmentB, pcm[current_sample], start_pos, ¤t_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