This is headers.c in view mode; [Download] [Up]
/* readers/writers for various sound file headers
*
* In the header, CLM needs support for an arbitrarily long comment (for mix et al),
* and in general I'm only interested in writing 2's complement, 16-bit (or better),
* uncompressed sound files with one sound per file and (preferably) channels interleaved.
* "Linear" below means 2's complement integer.
*
* Currently supported read/write:
* NeXT/Sun/DEC 16-bit linear (format 3), with AFsp extensions to the header
* AIFF 16-bit linear
* RIFF 16-bit linear (called PCM, which strikes me as confused)
*
* Currently supported read-only:
* NeXT/Sun/DEC 8-bit mulaw, 8-bit linear, 24-bit linear, 32-bit linear, 32-bit float, 8-bit alaw, 16-bit emphasized, double
* AIFF 8-bit linear, 8SVX 8-bit linear
* IRCAM 16-bit linear (BICSF), EBICSF
* NIST-SPHERE 16-bit linear
* INRS, ESPS, AVR 16-bit linear, AVR 8-bit linear and unsigned
* RIFF 8-bit alaw, 8-bit mulaw, 8-bit unsigned, 32-bit linear
* VOC 8-bit signed(?)
* no header (will prompt for info it needs) (CD-R is one of these)
* Sound Tools 8-bit unsigned(?)
* Turtle Beach SMP 16-bit linear
* Sound Designer II (the data file is the same as a no-header file with interleaved big-endian data)
*
* for a few of these I'm still trying to get documentation -- best sources of info
* are ftp.cwi.nl:pub/audio (info files), the AFsp sources, the SOX sources, and the CSound sources.
* sox and gsm are at ftp.cwi.nl, AFsp is from kabal@Polaris.EE.McGill.CA (Peter Kabal) as
* aldebaran.EE.McGill.CA:pub/AFsp/AFspV1R2.tar.Z. The Sound Designer formats are described
* in the "Developer Documentation" from Digidesign.
*
* I'm willing to add almost anything to this list -- to add another header
* I need the header layout as a sequence of bytes; to add another data format
* I need the interpretation of that data. To write a new header type, there
* has to be room for any amount of commentary in the header.
*
* Unsupported with no plans for eventual support:
* Sun/NeXT endless compressed versions, AES and MULAW-SQUELCH formats
* AIFC compressed, HCOM, MOD, Mus10, SAM
* RIFF ADPCM
* Sound Designer format 1 is obsolete
*/
#include <stdio.h>
#include <fcntl.h>
#include <math.h>
#include "sound_types.h"
#ifndef AKCL
extern int clm_read(int fd, char *buf, int n);
extern int clm_write(int fd, char *buf, int n);
extern short clm_short(short n);
extern int clm_int(int n);
extern long clm_seek(int fd, long offset, int origin);
extern int clm_reopen_write(char *arg);
extern int clm_open_read(char *arg);
extern int clm_create(char *name);
#endif
#define HDRBUFSIZ 256
/* better be more than 50 */
static char hdrbuf[HDRBUFSIZ];
static char I_DSND[4] = {'.','s','n','d'}; /* NeXT/Sun/Dec/SGI/AFsp first word */
static char I_FORM[4] = {'F','O','R','M'}; /* AIFF first word */
static char I_AIFF[4] = {'A','I','F','F'}; /* AIFF second word */
static char I_AIFC[4] = {'A','I','F','C'}; /* ditto but might be compressed data */
static char I_COMM[4] = {'C','O','M','M'};
static char I_SSND[4] = {'S','S','N','D'};
static char I_APPL[4] = {'A','P','P','L'};
static char I_CLM_[4] = {'C','L','M',' '}; /* I hereby claim this AIFF chunk name */
static char I_RIFF[4] = {'R','I','F','F'}; /* RIFF first word */
static char I_WAVE[4] = {'W','A','V','E'};
static char I_fmt_[4] = {'f','m','t',' '};
static char I_data[4] = {'d','a','t','a'};
static char I_clm_[4] = {'c','l','m',' '};
static char I_NIST[4] = {'N','I','S','T'}; /* first word of NIST SPHERE files */
static char I_8SVX[4] = {'8','S','V','X'}; /* AIFF other choice */
static char I_VOC0[4] = {'C','r','e','a'}; /* Actual text is "Creative Voice File" */
static char I_VOC1[4] = {'t','i','v','e'};
static char I_SNDT[4] = {'S','O','U','N'}; /* Sound Tools first word="SOUND" -- not unique as SMP files start with "SOUND SAMPLE" */
static char I_SMP1[4] = {'D',' ','S','A'};
static char I_SMP2[4] = {'M','P','L','E'};
static char I_BODY[4] = {'B','O','D','Y'}; /* next 4 for 8svx chunk names */
static char I_VHDR[4] = {'V','H','D','R'};
static char I_CHAN[4] = {'C','H','A','N'};
static char I_ANNO[4] = {'A','N','N','O'};
static char I_AVR_[4] = {'2','B','I','T'}; /* first word of AVR files */
static int I_DECN = 0x2e736400; /* first word of DEC files (?) */
static int I_IRCAM = 0x0001a364; /* ditto IRCAM */
#define NINRS 7
static const unsigned int I_INRS[NINRS]={0xcb460020,0xd0465555,0xfa460000,0x1c470040,0xc5b8ff7f,0x7a470000,0x9c470040};
static int data_size, data_location, srate, chans, header_type, sound_format, original_sound_format;
static int comment_start, comment_end, header_distributed, type_specifier;
int c_snd_header_data_size (void) {return(data_size);}
int c_snd_header_data_location (void) {return(data_location);}
int c_snd_header_chans (void) {return(chans);}
int c_snd_header_srate (void) {return(srate);}
int c_snd_header_type (void) {return(header_type);}
int c_snd_header_format (void) {return(sound_format);}
int c_snd_header_distributed (void) {return(header_distributed);}
int c_snd_header_comment_start (void) {return(comment_start);}
int c_snd_header_comment_end (void) {return(comment_end);}
int c_snd_header_type_specifier (void) {return(type_specifier);}
int c_snd_header_original_format (void) {return(original_sound_format);}
void c_set_snd_header (int in_srate, int in_chans, int in_format) {srate=in_srate; chans=in_chans; sound_format=in_format;}
int c_snd_header_datum_size (void)
{
switch (sound_format)
{
case snd_8_linear: return(1); break;
case snd_16_linear: return(2); break;
case snd_8_unsigned: return(1); break;
case snd_8_mulaw: return(1); break;
case snd_8_alaw: return(1); break;
case snd_32_linear: return(4); break;
case snd_32_float: return(4); break;
case snd_24_linear: return(3); break;
case snd_64_double: return(8); break;
}
}
int c_snd_samples (int format, int size)
{
switch (format)
{
case snd_8_linear: return(size<<1); break;
case snd_16_linear: return(size); break;
case snd_8_unsigned: return(size<<1); break;
case snd_8_mulaw: return(size<<1); break;
case snd_8_alaw: return(size<<1); break;
case snd_32_linear: return(size>>1); break;
case snd_32_float: return(size>>1); break;
case snd_24_linear: return((size>>2)+(size>>1)); break;
case snd_64_double: return(size>>2); break;
}
}
int be_or_le(int n1, int n2)
{
/* check both big and little endian versions for equality */
return ((n1 == n2) ||
(((n1>>24)&0xff)+((n1<<24)&0xff000000)+((n1>>8)&0xff00)+((n1<<8)&0xff0000)) == n2);
}
static void read_bicsf_header (int chan);
/* ------------------------------------ NeXT (or Sun) --------------------------------
* formats agree at least on the set given below between NeXT and Sun
* according to the Sox program, Dec format is the same.
* SGI also supports this format without change, and AFsp is an extension of it.
* This is, in my opinion, the best existing sound-file header format.
*
* 0: ".snd"
* 4: data_location (bytes) (not necessarily word aligned on Sun)
* 8: data_size (bytes)
* 12: data format indicator -- see table below
* 16: srate (int)
* 20: chans
* 24: comment start
*
* in an AFsp file, the first 4 bytes of the comment are "AFsp",
* see headers.lisp for readers/writers of AFsp fields
* for bicsf, the integer at 28 is 107364 or 107415
*
* on NeXTStep, always big-endian.
*/
void read_next_header (int chan)
{
#ifdef MAC
#pragma unused(chan)
#endif
int maybe_bicsf;
type_specifier = (*((int *)(hdrbuf)));
data_location = clm_int((*((int *)(hdrbuf+4))));
data_size = clm_int((*((int *)(hdrbuf+8))));
original_sound_format = clm_int((*((int *)(hdrbuf+12))));
switch (original_sound_format)
{ /* defined in /usr/include/sound/soundstruct.h */
/* see headers.lisp for a table of all known format names */
case 1: sound_format = snd_8_mulaw; break;
case 2: sound_format = snd_8_linear; break;
case 3: sound_format = snd_16_linear; break;
case 4: sound_format = snd_24_linear; break;
case 5: sound_format = snd_32_linear; break;
case 6: sound_format = snd_32_float; break;
case 7: sound_format = snd_64_double; break;
case 18: sound_format = snd_16_linear; break;
/* "emphasized"
* I believe the de-emphasis filter is:
* y(n) = 0.997646 x(n) + 1.99516 x(n-1) + 0.997512 x(n-2) - 1.99516 y(n-1) - 0.995159 y(n-2)
* from an active filter design by Atau Tanaka, Perry Cook, and Nick Porcaro, kindly supplied to me by JOS.
*
* Perhaps just as good is Xavier Serra's de-emphasis filter: y(n) = x(n) + .9 y(n-1)
*
* for now, I'll just pass it through unfiltered.
*/
case 27: sound_format = snd_8_alaw; break;
default: sound_format = snd_unsupported; break;
}
srate = clm_int((*((int *)(hdrbuf+16))));
chans = clm_int((*((int *)(hdrbuf+20))));
comment_start = 24;
comment_end = data_location - 1;
header_distributed = 0;
maybe_bicsf = clm_int((*((int *)(hdrbuf+28))));
if (maybe_bicsf == 107364) read_bicsf_header(chan);
data_size = c_snd_samples(sound_format,data_size);
}
void write_next_header (int chan, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
int i,j;
(*((int *)hdrbuf)) = (*(int *)I_DSND); /* ".snd" */
(*((int *)(hdrbuf+4))) = clm_int(loc);
(*((int *)(hdrbuf+8))) = clm_int(siz);
if (format == snd_16_linear)
(*((int *)(hdrbuf+12))) = clm_int(3);
else
(*((int *)(hdrbuf+12))) = clm_int(1);
(*((int *)(hdrbuf+16))) = clm_int(srate);
(*((int *)(hdrbuf+20))) = clm_int(chans);
write(chan,hdrbuf,24);
j = 0;
for (i=0;i<len;i++)
{
hdrbuf[j]=comment[i];
j++;
if (j == HDRBUFSIZ)
{
write(chan,hdrbuf,HDRBUFSIZ); /* not clm_write because this is an ASCII string of bytes */
j = 0;
}
}
for (i=0;i<(loc-(len+24));i++) /* now fill left over bytes with nulls */
{
hdrbuf[j]=0;
j++;
if (j == HDRBUFSIZ)
{
write(chan,hdrbuf,HDRBUFSIZ);
j = 0;
}
}
if (j != 0) write(chan,hdrbuf,j);
}
void update_next_header (int chan, int siz)
{
lseek(chan,8L,0);
(*((int *)(hdrbuf))) = clm_int(siz);
write(chan,hdrbuf,4);
}
/* ------------------------------------ AIFF ------------------------------------
*
* 0: "FORM"
* 4: size (bytes)
* 8: "AIFF" or "AIFC" -- the latter includes compressed formats
*
* Thereafter the file is organized into "chunks", each chunk being
* a 4-byte identifer followed by an int (4-bytes) giving the chunk size
* not including the 8-byte header. AIFF data is signed.
*
* The chunks we want are "COMM", "SSND", and "APPL". See comments below for details.
*/
/* ieee-80 conversions -- design by committee! */
/* this code taken from CSound sources -- apparently originally written by Malcolm Slaney at Apple */
#define ULPOW2TO31 ((unsigned long)0x80000000L)
#define DPOW2TO31 ((double)2147483648.0) /* 2^31 */
static double myUlongToDouble(unsigned long ul)
{
double val;
if(ul & ULPOW2TO31) val = DPOW2TO31 + (ul & (~ULPOW2TO31));
else val = ul;
return val;
}
static unsigned long myDoubleToUlong(double val)
{
unsigned long ul;
if(val < DPOW2TO31) ul = (unsigned long)val;
else ul = ULPOW2TO31 | (unsigned long)(val-DPOW2TO31);
return ul;
}
double ieee_80_to_double(char *p)
{
char sign;
short exp = 0;
unsigned long mant1 = 0;
unsigned long mant0 = 0;
double val;
exp = *p++; exp <<= 8; exp |= *p++; sign = (exp & 0x8000) ? 1 : 0; exp &= 0x7FFF;
mant1 = *p++; mant1 <<= 8; mant1 |= *p++; mant1 <<= 8; mant1 |= *p++; mant1 <<= 8; mant1 |= *p++;
mant0 = *p++; mant0 <<= 8; mant0 |= *p++; mant0 <<= 8; mant0 |= *p++; mant0 <<= 8; mant0 |= *p++;
if(mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0)
return 0.0;
else
{
val = myUlongToDouble(mant0) * pow(2.0,-63.0);
val += myUlongToDouble(mant1) * pow(2.0,-31.0);
val *= pow(2.0,((double) exp) - 16383.0);
return sign ? -val : val;
}
}
void double_to_ieee_80(double val, char *p)
{
char sign = 0;
short exp = 0;
unsigned long mant1 = 0;
unsigned long mant0 = 0;
if(val < 0.0) { sign = 1; val = -val; }
if(val != 0.0) /* val identically zero -> all elements zero */
{
exp = (short)(log(val)/log(2.0) + 16383.0);
val *= pow(2.0, 31.0+16383.0-(double)exp);
mant1 = myDoubleToUlong(val);
val -= myUlongToDouble(mant1);
val *= pow(2.0, 32.0);
mant0 = myDoubleToUlong(val);
}
*p++ = ((sign<<7)|(exp>>8)); *p++ = 0xFF & exp;
*p++ = 0xFF & (mant1>>24); *p++ = 0xFF & (mant1>>16); *p++ = 0xFF & (mant1>> 8); *p++ = 0xFF & (mant1);
*p++ = 0xFF & (mant0>>24); *p++ = 0xFF & (mant0>>16); *p++ = 0xFF & (mant0>> 8); *p++ = 0xFF & (mant0);
}
int ieee_80_to_long(char *hdrbuf, int loc)
{
int i;
i=ieee_80_to_double((char *)(hdrbuf+loc));
return(i);
}
void long_to_ieee_80(int srate, char *hdrbuf, int loc)
{
double fx;
fx = srate;
double_to_ieee_80(fx,(char *)(hdrbuf+loc));
}
int reset_hdrbuf(int chan, int loc)
{
lseek(chan,loc,0);
read(chan,hdrbuf,HDRBUFSIZ);
return(loc+HDRBUFSIZ);
}
static int update_form_size, update_frames_size, update_frames_location, update_ssnd_location;
void read_aiff_header (int chan)
{
/* we know we have checked for FORM xxxx AIFF|AIFC when we arrive here */
/* as far as I can tell, the COMM block has the header data we seek, and the SSND block has the sound data */
/* everything else will be ignored -- apparently we can depend on seeing a "chunk" name, then size */
int chunksize,chunkname,offset,frames,curend,chunkloc,appl_name,happy;
type_specifier = (*((int *)(hdrbuf)));
chunkloc = 12;
curend = 32;
offset = 0;
comment_start = 0;
comment_end = 0;
sound_format = snd_16_linear;
header_distributed = 1;
srate = 0;
chans = 0;
happy = 1;
update_form_size = (*(int *)(hdrbuf+4));
while (happy)
{
if ((chunkloc+8) > curend) /* get enough data to read chunk header */
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
chunkname = (*(int *)(hdrbuf+chunkloc));
chunksize = (*(int *)(hdrbuf+chunkloc+4));
if (chunkname == (*(int *)I_COMM))
{
if ((chunkloc+26) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
chans = (*(short *)(hdrbuf+chunkloc+8));
frames = (*(int *)(hdrbuf+chunkloc+10));
update_frames_location = chunkloc+10+offset;
update_frames_size = frames;
if ((*(short *)(hdrbuf+chunkloc+14)) != 16)
{
original_sound_format = ((*(short *)(hdrbuf+chunkloc+14)));
if (original_sound_format == 8) sound_format = snd_8_linear;
else sound_format = snd_unsupported;
}
data_size = (frames*2*chans);
srate = ieee_80_to_long(hdrbuf,chunkloc+16);
}
else
{
if (chunkname == (*(int *)I_SSND))
{
if ((chunkloc+12) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
update_ssnd_location = offset+chunkloc+4;
data_location = (*(int *)(hdrbuf+chunkloc+8)) + offset + 16 + chunkloc; /* Baroque! */
/* offset is where the hdrbuf is positioned in the file, chunkloc is the start */
/* of the chunk within hdrbuf, the sound data offset itself is at loc+8 and the */
/* 0-based location of the sound data is at the end of the chunk = 16 (8=header+4=offset+4=blocksize) */
happy = 0;
}
else
{
if (chunkname == (*(int *)I_ANNO))
{
if ((chunkloc+8) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
comment_start = chunkloc+8;
comment_end = chunkloc+7+chunksize;
}
else
/* ignored types include 'MARK' for a marker, 'INST' for looping control, 'MIDI' obvious */
/* 'COMT' for a comment -- not used by CLM because it makes various dumb assumptions -- */
/* for example, the length of the comment is limited to 65536 chars; then there are */
/* the "let's get really silly" chunks like 'NAME', 'AUTH', '(c) ', */
/* 'AESD' for audio recording info, and finally, the one we're looking for here... */
if (chunkname == (*(int *)I_APPL))
{
if ((chunkloc+12) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
appl_name = (*(int *)(hdrbuf+chunkloc+8));
if (appl_name == (*(int *)I_CLM_))
{
/* my own chunk has the arbitrary length comment I use (actually the ASCII */
/* representation of a lisp program evaluated in the CLU package) to handle mix et al. */
/* It is nothing more than the actual string -- remember to pad to even length here. */
comment_start = offset + chunkloc + 12;
comment_end = comment_start + chunksize - 5;
}
}
}
}
chunkloc += (8+chunksize);
}
data_size = c_snd_samples(sound_format,data_size);
}
void write_aiff_header (int chan, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
/* we write the simplest possible AIFF header: AIFF | COMM | APPL-CLM_ if needed | SSND eof. */
/* the assumption being that we're going to be appending sound data once the header is out */
int offset,i,j,lenhdr,extra,curend;
#pragma unused(loc,format)
lenhdr=0;
if (len != 0) lenhdr = 12;
(*(int *)hdrbuf) = (*(int *)I_FORM);
(*(int *)(hdrbuf+4)) = len+38+16+siz+lenhdr;
(*(int *)(hdrbuf+8)) = (*(int *)I_AIFF);
(*(int *)(hdrbuf+12)) = (*(int *)I_COMM);
(*(int *)(hdrbuf+16)) = 18;
(*(short *)(hdrbuf+20)) = (short)chans;
(*(int *)(hdrbuf+22)) = (int)(siz / (chans*2));
(*(short *)(hdrbuf+26)) = 16;
long_to_ieee_80(srate,hdrbuf,28);
offset = 38;
i = 38;
extra = 0;
curend = 0;
if (len != 0)
{
offset += len+12;
if ((len % 4) != 0)
extra = (4 - (len % 4));
(*(int *)(hdrbuf+38)) = (*(int *)I_APPL);
(*(int *)(hdrbuf+42)) = len+4+extra;
(*(int *)(hdrbuf+46)) = (*(int *)I_CLM_);
i = 50;
for (j=0;j<len;j++)
{
if (i == HDRBUFSIZ)
{
curend += HDRBUFSIZ;
write(chan,hdrbuf,HDRBUFSIZ);
i=0;
}
hdrbuf[i]=comment[j];
i++;
}
if (extra != 0)
{
if ((i+extra) > HDRBUFSIZ)
{
curend += i;
write(chan,hdrbuf,i);
i=0;
}
for (j=0;j<extra;j++)
{
hdrbuf[i] = 0;
i++;
}
}
}
if ((i+16) > HDRBUFSIZ)
{
curend += i;
write(chan,hdrbuf,i);
i = 0;
}
(*(int *)(hdrbuf+i)) = (*(int *)I_SSND);
(*(int *)(hdrbuf+i+4)) = (siz+8);
(*(int *)(hdrbuf+i+8)) = 0; /* "offset" */
(*(int *)(hdrbuf+i+12)) = 0; /* "blocksize " */
data_location = i+16+curend;
write(chan,hdrbuf,i+16);
}
void update_aiff_header (int chan, int siz)
{
/* we apparently have to make sure the form size and the data size are correct */
/* assumed here that we'll only be updating our own AIFF files */
/* There are 3 such locations -- the 2nd word of the file which is the overall form size, */
/* the frames variable in the COMM chunk, and the chunk-size variable in the SSND chunk */
read(chan,hdrbuf,32);
read_aiff_header(chan);
lseek(chan,4L,0);
(*(int *)hdrbuf) = (siz+update_form_size);
write(chan,hdrbuf,4);
lseek(chan,update_frames_location,0);
(*(int *)hdrbuf) = ((siz / (chans*2))+update_frames_size);
write(chan,hdrbuf,4);
lseek(chan,update_ssnd_location,0);
(*(int *)hdrbuf) = siz+8;
write(chan,hdrbuf,4);
}
/* ------------------------------------ RIFF ------------------------------------
* nearly identical to AIFF from our point of view
*
* 0: "RIFF"
* 4: size
* 8: "WAVE"
*
* rest very similar to AIFF -- on the Mac, these files contain big-endian data but little-endian headers!
*/
#ifdef __LITTLE_ENDIAN__
#define le(n) n
#define les(n) n
#else
int le(int n) {return(((n>>24)&0xff) + ((n<<24)&0xff000000) + ((n>>8)&0xff00) + ((n<<8)&0xff0000));}
short les(short n) {return( ((n>>8)&0xff) + ((n<<8)&0xff00));}
#endif
void read_riff_header (int chan)
{
/* we know we have checked for RIFF xxxx WAVE when we arrive here */
int chunksize,chunkname,offset,curend,chunkloc,happy,bits;
type_specifier = (*((int *)(hdrbuf)));
chunkloc = 12;
curend = 32;
offset = 0;
comment_start = 0;
comment_end = 0;
header_distributed = 1;
sound_format = snd_unsupported;
srate = 0;
chans = 0;
happy = 1;
update_form_size = le((*(int *)(hdrbuf+4)));
while (happy)
{
if ((chunkloc+8) > curend) /* get enough data to read chunk header */
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
chunkname = (*(int *)(hdrbuf+chunkloc)); /* was le */
chunksize = le((*(int *)(hdrbuf+chunkloc+4)));
if (chunkname == (*(int *)I_fmt_))
{
if ((chunkloc+26) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
/*
* 8: short format code --1=PCM for example
* 10: short chans --1
* 12: long rate --48000 (0xbb80)
* 16: long ave rate --65655 (0x10077)
* 20: short align --2
* 22: short data size (bits) --16
*
* R I F F # # # # W A V E f m t sp
* 5249 4646 f851 0500 5741 5645 666d 7420
* e40f 0000 0100 0100 80bb 0000 0077 0100
* 0200 1000 0000 0000 0000 0000 0000 0000
*
* #x000551f8 = 348664 = size in bytes - 8
* #x00000fe4 = 4068 [fmt_ chunk size?]
*
* formats are 6=alaw, 7=mulaw, 0x15=digistd, 0x16=digifix, (what are these really?)
* 0x102=ibm alaw and 0x101=ibm mulaw, (what's the difference?)
* 1=PCM, 2=ADPCM (not supported in CLM)
* see headers.lisp for a table of all known format names
*/
original_sound_format = les(((*(short *)(hdrbuf+chunkloc+8))));
chans = les((*(short *)(hdrbuf+chunkloc+10)));
srate = le((*(int *)(hdrbuf+chunkloc+12)));
bits = les((*(short *)(hdrbuf+22)));
if (bits == 16) sound_format = snd_16_linear; else
if ((original_sound_format == 6) && (bits == 8)) sound_format = snd_8_alaw; else
if ((original_sound_format == 7) && (bits == 8)) sound_format = snd_8_mulaw; else
if (original_sound_format == 1)
{
if (bits == 8) sound_format = snd_8_linear; else
if (bits == 32) sound_format = snd_32_linear;
}
else
if ((original_sound_format == 0x102) && (bits == 8)) sound_format = snd_8_alaw; else
if ((original_sound_format == 0x101) && (bits == 8)) sound_format = snd_8_mulaw;
}
else
{
if (chunkname == (*(int *)I_data))
{
if ((chunkloc+12) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
update_ssnd_location = offset+chunkloc+4;
data_location = offset + 8 + chunkloc;
data_size = le((*(int *)(hdrbuf+chunkloc+4)));
happy = 0;
}
else
if (chunkname == (*(int *)I_clm_))
{
if ((chunkloc+8) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
comment_start = offset + chunkloc + 8;
comment_end = comment_start + chunksize - 1; /* end of comment not start of next chunk */
}
}
chunkloc += (8+chunksize);
}
data_size = c_snd_samples(sound_format,data_size);
}
void write_riff_header (int chan, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
int offset,i,j,lenhdr,extra,curend;
#pragma unused(loc,format)
lenhdr=0;
if (len != 0) lenhdr = 12;
(*(int *)hdrbuf) = (*(int *)I_RIFF);
(*(int *)(hdrbuf+4)) = le(len+36+14+siz+lenhdr-8);
(*(int *)(hdrbuf+8)) = (*(int *)I_WAVE);
(*(int *)(hdrbuf+12)) = (*(int *)I_fmt_);
(*(int *)(hdrbuf+16)) = le(26-8);
(*(short *)(hdrbuf+20)) = les(1);
(*(short *)(hdrbuf+22)) = les((short)chans);
(*(int *)(hdrbuf+24)) = le(srate);
(*(short *)(hdrbuf+28)) = les(2);
(*(int *)(hdrbuf+30)) = le(16);
offset = 34;
i = 34;
extra = 0;
curend = 0;
if (len != 0)
{
offset += len+12;
if ((len % 4) != 0)
extra = (4 - (len % 4));
(*(int *)(hdrbuf+38)) = (*(int *)I_clm_);
(*(int *)(hdrbuf+42)) = len+extra;
i = 42;
for (j=0;j<len;j++)
{
if (i == HDRBUFSIZ)
{
curend += HDRBUFSIZ;
write(chan,hdrbuf,HDRBUFSIZ);
i=0;
}
hdrbuf[i]=comment[j];
i++;
}
if (extra != 0)
{
if ((i+extra) > HDRBUFSIZ)
{
curend += i;
write(chan,hdrbuf,i);
i=0;
}
for (j=0;j<extra;j++)
{
hdrbuf[i] = 0;
i++;
}
}
}
if ((i+16) > HDRBUFSIZ)
{
curend += i;
write(chan,hdrbuf,i);
i = 0;
}
(*(int *)(hdrbuf+i)) = (*(int *)I_data);
(*(int *)(hdrbuf+i+4)) = le(siz);
data_location = i+8+curend;
write(chan,hdrbuf,i+8);
}
void update_riff_header (int chan, int siz)
{
read(chan,hdrbuf,32);
read_riff_header(chan);
lseek(chan,4L,0);
(*(int *)hdrbuf) = le((siz+update_form_size));
write(chan,hdrbuf,4);
lseek(chan,update_ssnd_location,0);
(*(int *)hdrbuf) = le(siz+8);
write(chan,hdrbuf,4);
}
/* ------------------------------------ NIST ------------------------------------
* read-only in CLM
*
* 0: "NIST_1A"
* 8: data_location as ASCII representation of integer (apparently always " 1024")
* 16: start of complicated header -- see below for details
*
* The most recent version of the SPHERE package is available
* via anonymous ftp from jaguar.ncsl.nist.gov [129.6.48.157] in
* compressed tar form as "sphere-v.tar.Z" (where "v" is the version
* code).
*
* here's an example:
*
* NIST_1A
* 1024
* database_id -s5 TIMIT
* database_version -s3 1.0
* utterance_id -s8 aks0_sa1
* channel_count -i 1
* sample_count -i 63488
* sample_rate -i 16000
* sample_min -i -6967
* sample_max -i 7710
* sample_n_bytes -i 2
* sample_byte_format -s2 01
* sample_sig_bits -i 16
* end_head
*/
int decode_nist_value (char *str,int base,int end)
{
/* can be -i -r or -snnn where nnn=ascii rep of integer = len of string (!) */
/* we'll deal only with integer fields */
int i,j;
char value[80];
i=base;
while ((i<end) && (str[i] != '-')) i++; /* look for -i or whatever */
while ((i<end) && (str[i] != ' ')) i++; /* look for space after it */
i++;
for (j=0;i<end;j++,i++)
value[j]=str[i];
value[j]=0;
sscanf(value,"%d",&i);
return(i);
}
void read_nist_header (int chan)
{
char str[80],name[80];
int happy = 1;
int k,hend,curbase,j,n,nm,samples,bytes;
type_specifier = (*((int *)(hdrbuf))); /* the actual id is "NIST_1A" */
for (k=8;k<16;k++) str[k-8]=hdrbuf[k];
sscanf(str,"%d",&data_location); /* always "1024" */
n = 16;
hend = 32;
k=0;
curbase = 0;
samples = 0;
bytes = 0;
srate = 0;
chans = 0;
comment_start = 16;
comment_end = 16;
for (j=0;j<80;j++) str[j]=' ';
while (happy)
{
/* much as in xIFF files, march through the file looking for the data we're after */
/* in this case we munch a character at a time... */
str[k] = hdrbuf[n];
if ((((str[k] == '\0') || (str[k] == '\n')) || ((curbase+n+1) >= data_location)) || (k == 79))
{
/* got a complete record (assuming no embedded newlines, of course) */
/* now look for a record we care about and decode it */
nm = 0;
while (str[nm] != ' ')
{
name[nm] = str[nm];
nm++;
}
name[nm]=0;
if (strcmp(name,"sample_rate") == 0) srate = decode_nist_value(str,nm,k); else
if (strcmp(name,"channel_count") == 0) chans = decode_nist_value(str,nm,k); else
if (strcmp(name,"end_head") == 0) {happy = 0; comment_end=curbase+n-1;} else
if (strcmp(name,"sample_count") == 0) samples = decode_nist_value(str,nm,k); else
if ((bytes == 0) && (strcmp(name,"sample_n_bytes") == 0)) bytes = decode_nist_value(str,nm,k); else
if ((bytes == 0) && (strcmp(name,"sample_sig_bits") == 0)) {bytes = decode_nist_value(str,nm,k); bytes = (bytes>>3);}
for (j=0;j<=k;j++) str[j]=' ';
k=0;
if ((curbase+n+1) > 512) happy=0;
}
else
k++;
n++;
if (n >= hend)
{
curbase += hend;
n = 0;
read(chan,hdrbuf,HDRBUFSIZ);
hend = HDRBUFSIZ;
}
}
data_size = samples*bytes;
if (bytes == 2) sound_format = snd_16_linear; else sound_format = snd_8_mulaw;
data_size = c_snd_samples(sound_format,data_size);
header_distributed = 1;
}
/* ------------------------------------ BICSF ------------------------------------
* read-only in CLM (actually, this is EBICSF and the old BICSF is called IRCAM below)
*
* 0-28: NeXT-compatible header, read by read_next_header above.
* 28: bicsf magic number (107364 or trouble)
* 32: srate as a 32-bit float
* 36: chans
* 40: data format indicator (2=16-bit linear, 4=32-bit float)
* 44: begin chunks, if any
*
* followed by AIFF-style chunked header info with chunks like:
*
* COMM size comment
* MAXA size {max amps (up to 4)} {frame offsets) time-tag unix msec counter
* CUE, PRNT, ENV etc
*/
static void read_bicsf_header (int chan)
{
type_specifier = (*((int *)(hdrbuf+28)));
header_type = BICSF_sound_file;
if (data_size == 0) data_size = (lseek(chan,0L,2)-data_location);
original_sound_format = clm_int((*((int *)(hdrbuf+40))));
switch (original_sound_format)
{
case 2: sound_format = snd_16_linear; break;
case 4: sound_format = snd_32_float; break;
default: break;
}
/* now check for a COMM chunk, setting the comment pointers */
{
int chunksize,chunkname,offset,curend,chunkloc,happy;
chunkloc = 28+16; /* next header + magic number, srate, chans, packing, then chunks, I think */
curend = HDRBUFSIZ;
offset = 0;
comment_start = 0;
comment_end = 0;
header_distributed = 1;
happy = 1;
while (happy)
{
if (curend >= data_location)
happy = 0;
else
{
if ((chunkloc+8) > curend) /* get enough data to read chunk header */
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
chunkname = (*(int *)(hdrbuf+chunkloc));
chunksize = (*(int *)(hdrbuf+chunkloc+4));
if (chunkname == (*(int *)I_COMM))
{
comment_start = curend+chunkloc+8;
comment_end = comment_start + chunksize;
happy = 0;
}
else
{
if ((chunkname == 0) || (chunksize == 0))
happy = 0;
}
chunkloc += (8+chunksize);
}
}
}
/* from here we fall back into read_next_header */
}
/* ------------------------------------ IRCAM ------------------------------------
* read-only in CLM (old-style BICSF)
*
* 0: 0x1a364
* 4: srate as a 32-bit float
* 8: chans
* 12: data format indicator (2=16-bit linear, 4=32-bit float)
* 16: comment start -- how to tell if it's a real comment?
* 1024: data start
*/
static void read_ircam_header (int chan)
{
type_specifier = (*((int *)(hdrbuf)));
data_location = 1024;
data_size = (lseek(chan,0L,2)-1024);
original_sound_format = clm_int((*((int *)(hdrbuf+12))));
switch (original_sound_format)
{
case 2: sound_format = snd_16_linear; break;
case 4: sound_format = snd_32_float; break;
default: sound_format = snd_unsupported; break;
}
srate = (*((float *)(hdrbuf+4)));
chans = clm_int((*((int *)(hdrbuf+8))));
comment_start = 16;
comment_end = 1023;
header_distributed = 0;
data_size = c_snd_samples(sound_format,data_size);
}
/* ------------------------------------ AVR --------------------------------------
* read-only in CLM
*
* 0: "2BIT"
* 4: sample name (null padded ASCII)
* 12: chans (short) (0=mono, -1=stereo)
* 14: sample size (8 or 16 bit) (short) (value is 8 or 16)
* 16: sample format (signed or unsigned) (short) (0=unsigned, -1=signed)
* 18: loop, 20: midi
* 22: srate (integer)
* 26: length in samples
* 30: loop beg, 34: loop end, 38: midi, 40: compression, 42: nada, 44: name
* 64: comment (limited to 64 bytes)
* 128: data start
*/
void read_avr_header(int chan)
{
#ifdef MAC
#pragma unused(chan)
#endif
int dsize,dsigned,i;
chans = (*((short *)(hdrbuf+12)));
if (chans == 0) chans=1; else chans=2;
data_location = 128;
data_size = (*((int *)(hdrbuf+26)));
comment_start = 64;
i = 64;
if (HDRBUFSIZ >= 128)
{
while ((i < 128) && (hdrbuf[i] != 0)) i++;
comment_end = (i-1);
}
else comment_end = 127;
header_distributed = 0;
srate = (*((int *)(hdrbuf+22)));
dsize = (*((short *)(hdrbuf+14)));
dsigned = (*((short *)(hdrbuf+16)));
if (dsize == 16)
{
if (dsigned == 0)
sound_format = snd_unsupported; /* unsigned 16-bit!? */
else sound_format = snd_16_linear;
}
else
{
if (dsigned == 0)
sound_format = snd_8_unsigned;
else sound_format = snd_8_linear;
}
}
/* ------------------------------------ VOC --------------------------------------
* read-only in CLM
*
* 0: "Creative Voice File" followed by a couple ctrl-Z ('32) (swapped data)
* 20: header end (short)
* [20]: first block:
* block code, 1=data, 0=end
* block len as 24 bit int(?)
* if data, then rate code (byte), then data
*/
void read_voc_header(int chan)
{
int type,happy,len,curbase,file_len;
sound_format = snd_8_unsigned;
chans = 1;
happy = 1;
comment_start = 0;
comment_end = 0;
file_len=lseek(chan,0L,2);
curbase = ((int)(hdrbuf[20])+(((int)(hdrbuf[21]))<<8));
lseek(chan,curbase,0);
read(chan,hdrbuf,HDRBUFSIZ);
while (happy)
{
type = (int)(hdrbuf[0]);
len=(((int)hdrbuf[3])<<16)+(((int)hdrbuf[2])<<8)+(((int)hdrbuf[1]));
if (type == 1)
{
data_size = len-3;
data_location = curbase+6;
srate = (hdrbuf[4]&0xff);
if (hdrbuf[5] != 0) {printf("uh oh:%d ",hdrbuf[5]); fflush(stdout);}
happy = 0;
}
else
{
if ((len+curbase)<file_len)
{
lseek(chan,curbase+len+4,0);
read(chan,hdrbuf,HDRBUFSIZ);
curbase += len;
}
else happy = 0;
}
}
srate = (int)(1000000.0/(256-srate));
data_size = c_snd_samples(sound_format,data_size);
}
/* ------------------------------------ SNDT -------------------------------------
* read-only in CLM
*
* this taken from sndrtool.c (sox-10):
* 0: "SOUND"
* 6: 0x1a
* 8-11: 0
* 12-15: nsamples
* 16-19: 0
* 20-23: nsamples
* 24-25: srate
* 26-27: 0
* 28-29: 10
* 30-31: 4
* 32-> : <filename> "- File created by Sound Exchange"
* .->95: 0
*/
void read_sndt_header(int chan)
{
#pragma unused(chan);
sound_format = snd_8_unsigned;
chans = 1;
comment_start = 0;
comment_end = 0;
srate = (*(short *)(hdrbuf+24));
data_location = 96;
data_size = (*(int *)(hdrbuf+12));
if (data_size != (*(int *)(hdrbuf+20))) {printf("uh oh"); fflush(stdout);}
header_distributed = 0;
data_size = c_snd_samples(sound_format,data_size);
}
/* ------------------------------------ SMP -------------------------------------
* read-only in CLM
*
* 0: "SOUND SAMPLE DATA "
* 18: "2.1 "
* 22-81: comment
* 82-111: sample name
* header 112 bytes
* long samples (bytes=samples*2)
* then data start
* data
* trailer:
* short reserved, 88 bytes of "loop" data, 8*14 bytes of markers
* byte for MIDI -- all this junk even for straight data
* long rate
*
* always little endian
*/
void read_smp_header(int chan)
{
sound_format = snd_16_linear;
chans = 1;
comment_start=22;
comment_end=81;
data_location = 116;
lseek(chan,112,0);
read(chan,hdrbuf,4);
data_size = (((int)hdrbuf[3])<<24)+(((int)hdrbuf[2])<<16)+(((int)hdrbuf[1])<<8)+(((int)(hdrbuf[0])));
lseek(chan,data_size+116+88+8*14+1,0);
read(chans,hdrbuf,4);
srate = (((int)hdrbuf[3])<<24)+(((int)hdrbuf[2])<<16)+(((int)hdrbuf[1])<<8)+(((int)(hdrbuf[0])));
data_size = c_snd_samples(sound_format,data_size);
}
/* ------------------------------------ ESPS -------------------------------------
* read-only in CLM
*
* 16: 0x00006a1a or 0x1a6a0000
* 136: if not 0, chans + format = 32-bit float
* 144: if not 0, chans + format = 16-bit linear
* see below for srate search
*
* from AFgetInfoES.c:
*
* Bytes Type Contents
* 8 -> 11 -- Header size (bytes)
* 12 -> 15 int Sampled data record size
* 16 -> 19 int File identifier
* 40 -> 65 char File creation date
* 124 -> 127 int Number of samples (may indicate zero) -- is that an error?
* 132 -> 135 int Number of doubles in a data record
* 136 -> 139 int Number of floats in a data record
* 140 -> 143 int Number of longs in a data record
* 144 -> 147 int Number of shorts in a data record
* 148 -> 151 int Number of chars in a data record
* 160 -> 167 char User name
* 333 -> H-1 -- "Generic" header items, including "record_freq" {followed by a "double8"=64-bit float}
* H -> ... -- Audio data
*/
int decode_esps_double (char *buf, int base)
{
/* "8 byte double" */
double gad;
char fl[8];
int i;
#ifdef __LITTLE_ENDIAN__
for (i=0;i<8;i++) fl[i]=buf[i+base];
#else
for (i=0;i<8;i++) fl[7-i]=buf[i+base];
#endif
gad = (*((double *)(fl)));
i = (int)gad;
return(i);
}
void read_esps_header (int chan)
{
int siz;
char str[80];
int happy = 1;
int k,hend,curbase,j,n;
data_location = clm_int((*(int *)(hdrbuf+8)));
siz = lseek(chan,0L,2);
lseek(chan,136,0);
data_size = siz - data_location;
header_distributed = 0;
srate = 0;
comment_start = 0;
comment_end = 0;
read(chan,hdrbuf,HDRBUFSIZ);
chans = clm_int((*(int *)(hdrbuf+8))); /* 144 */
if (chans != 0)
sound_format = snd_16_linear;
else
{
chans = clm_int((*(int *)(hdrbuf))); /* 136 */
if (chans != 0) sound_format = snd_32_float;
}
/* search for "record_freq" to get srate */
lseek(chan,333,0);
read(chan,hdrbuf,HDRBUFSIZ);
curbase=333;
hend=curbase+HDRBUFSIZ;
k=0;
n=0;
for (j=0;j<80;j++) str[j]=' ';
while (happy)
{
str[k] = hdrbuf[n];
if ((str[k] == 'q') || (str[k] == 3) || ((curbase+n+1) >= data_location) || (k == 78))
{
str[k+1]=0;
if (strcmp(str,"record_freq") == 0)
{
if ((n+11)<HDRBUFSIZ)
curbase=n+3; /* lessee, 1 ("q") + 1 (null) + 1 to grow on? */
else
{
lseek(chan,curbase+n+3,0);
read(chan,hdrbuf,8);
curbase=0;
}
srate = decode_esps_double(hdrbuf,curbase);
happy = 0;
}
if ((curbase+n+1) >= data_location) happy = 0;
k=0;
}
else
k++;
n++;
if (n >= hend)
{
curbase += hend;
n = 0;
read(chan,hdrbuf,HDRBUFSIZ);
hend = HDRBUFSIZ;
}
}
data_size = c_snd_samples(sound_format,data_size);
}
/* ------------------------------------ INRS -------------------------------------
* read-only in CLM
*
* from AFgetInfoIN.c:
*
* INRS-Telecommunications audio file:
* Bytes Type Contents
* 0 -> 3 float Sampling Frequency (VAX float format)
* 6 -> 25 char Creation time (e.g. Jun 12 16:52:50 1990)
* 26 -> 29 int Number of speech samples in the file
* The data in an INRS-Telecommunications audio file is in 16-bit integer
* format. Header is always 512 bytes. Always mono.
*
*/
void read_inrs_header (int chan)
{
int siz;
siz = lseek(chan,0L,2);
comment_start = 0;
comment_end = 0;
header_distributed = 0;
sound_format = INRS_sound_file;
srate = (*((int *)hdrbuf));
chans = 1;
data_location = 512;
data_size = siz-512;
}
/* ------------------------------------ 8SVX -------------------------------------
* read-only in CLM
*
* very similar to AIFF:
* "BODY" => [4] samples [n] data
* "VHDR" => srate (short)
* "CHAN" => chans
* "ANNO" and "NAME"
*/
void read_8svx_header (int chan)
{
int chunksize,chunkname,offset,curend,chunkloc,happy;
type_specifier = (*((int *)(hdrbuf)));
chunkloc = 12;
curend = 32;
offset = 0;
comment_start = 0;
comment_end = 0;
sound_format = snd_8_linear;
header_distributed = 1;
srate = 0;
chans = 0;
happy = 1;
update_form_size = (*(int *)(hdrbuf+4));
while (happy)
{
if ((chunkloc+8) > curend) /* get enough data to read chunk header */
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
chunkname = (*(int *)(hdrbuf+chunkloc));
chunksize = (*(int *)(hdrbuf+chunkloc+4));
if (chunkname == (*(int *)I_CHAN))
{
if ((chunkloc+12) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
chans = (*(int *)(hdrbuf+chunkloc+8));
chans = (chans & 0x01) + ((chans & 0x02) >> 1) + ((chans & 0x04) >> 2) + ((chans & 0x08) >> 3);
/* what in heaven's name is this? Each bit corresponds to a channel? */
}
else
{
if (chunkname == (*(int *)I_VHDR))
{
if ((chunkloc+12) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
srate = (*(short *)(hdrbuf+chunkloc+8));
}
else
{
if (chunkname == (*(int *)I_ANNO))
{
if ((chunkloc+8) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
comment_start = chunkloc+8;
comment_end = chunkloc+7+chunksize;
}
else
{
if (chunkname == (*(int *)I_BODY))
{
if ((chunkloc+12) > curend)
{
offset += chunkloc;
curend = reset_hdrbuf(chan,chunkloc);
chunkloc = 0;
}
data_size = (*(int *)(hdrbuf+chunkloc+8));
data_location = chunkloc+12;
}
}
}
}
chunkloc += (8+chunksize);
}
data_size = c_snd_samples(sound_format,data_size);
}
/* ------------------------------------ no header and Sound Designer II -------------------------------------
*
* Sound Designer II data fork is a raw data file -- interleaved channels, bytes in sequence.
* The associated resource fork under "STR " has "sample-size" "sample-rate" and "channels".
* Similarly the resources "sdDD" id 1000, "sdML" id 1000, and "sdLL" id 1000 return pascal records of
* respectively Document Data, Markers and text, Loop data. I don't see any useful info in any of these
* except perhaps the file comment in the "sdDD" record of type str255 10 bytes in (I think).
* See headers.lisp for a reader for some of this stuff.
*
* read-only in CLM
*/
void read_no_header (int chan)
{
#pragma unused(chan);
srate = 44100;
chans = 2;
comment_start = 0;
comment_end = 0;
header_distributed = 0;
data_location = 0;
data_size = lseek(chan,0L,2);
/* read-header in headers.lisp will ask the user if this is really what he wants */
/* The upshot is that fasmix (i.e. merge) cannot deal directly with raw sound data */
}
/* ------------------------------------ all together now ------------------------------------ */
void c_read_header_with_fd (int chan) /* internal optimization for merge.c */
{
int word_0;
read(chan,hdrbuf,32);
header_type = unsupported_sound_file;
word_0 = (*((int *)hdrbuf));
if ((be_or_le(word_0,(*(int *)I_DSND))) || (be_or_le(word_0,I_DECN)))
{
header_type = NeXT_sound_file;
read_next_header(chan);
}
else
{
if (be_or_le(word_0,(*(int *)I_FORM)))
{
/* next 4 bytes are apparently the file size or something equally useless */
word_0 = (*((int *)(hdrbuf+8)));
if ((be_or_le(word_0,(*(int *)I_AIFF))) || (be_or_le(word_0,(*(int *)I_AIFC))))
{
/* we'll assume the compression type is 'NONE' */
header_type = AIFF_sound_file;
read_aiff_header(chan);
}
else
{
if (be_or_le(word_0,(*(int *)I_8SVX)))
{
header_type = SVX_sound_file;
read_8svx_header(chan);
}
}
}
else
{
if (be_or_le(word_0,(*(int *)I_RIFF)))
{
word_0 = (*((int *)(hdrbuf+8)));
if (be_or_le(word_0,(*(int *)I_WAVE)))
{
header_type = RIFF_sound_file;
read_riff_header(chan);
}
}
else
{
if (be_or_le(word_0,I_IRCAM))
{
header_type = IRCAM_sound_file;
read_ircam_header(chan);
}
else
{
if (be_or_le(word_0,(*(int *)I_NIST)))
{
header_type = NIST_sound_file;
read_nist_header(chan);
}
else
{
if (be_or_le(word_0,(*(int *)I_SNDT)))
{
if ((be_or_le(*(int *)I_SMP1,(*(int *)(hdrbuf+4)))) &&
(be_or_le(*(int *)I_SMP2,(*(int *)(hdrbuf+8)))))
{
header_type = SMP_sound_file;
read_smp_header(chan);
}
else
{
header_type = SNDT_sound_file;
read_sndt_header(chan);
}
}
else
{
if ((be_or_le(word_0,(*(int *)I_VOC0))) &&
(be_or_le(*(int *)(hdrbuf+4),(*(int *)I_VOC1))))
{
header_type = VOC_sound_file;
read_voc_header(chan);
}
else
{
if (be_or_le(word_0,(*(int *)I_AVR_)))
{
header_type = AVR_sound_file;
read_avr_header(chan);
}
else
{ /* no recognized magic number at start -- poke around in possible header for other types */
/* ESPS is either 0x00006a1a or 0x1a6a0000 at byte 16 */
word_0 = (*(int *)(hdrbuf+16));
if (be_or_le(word_0,0x00006a1a))
{
header_type = ESPS_sound_file;
read_esps_header(chan);
}
else
{
int i,happy;
happy = 0;
for (i=0;i<NINRS;i++)
{
if (be_or_le(word_0,I_INRS[i]))
happy = 1;
}
if (happy)
{
header_type = INRS_sound_file;
read_inrs_header(chan);
}
else
{
header_type = raw_sound_file;
read_no_header(chan);
}
}
}
}
}
}
}
}
}
}
}
int c_read_header (char *name)
{
int chan;
chan = clm_open_read(name);
if (chan == -1) return(-1);
c_read_header_with_fd(chan);
close(chan);
return(0);
}
int c_write_header (char *name, int type, int srate, int chans, int loc, int siz, int format, char *comment, int len)
{
int chan;
chan = clm_create(name);
if (chan == -1) return(-1);
switch (type)
{
case NeXT_sound_file: write_next_header(chan,srate,chans,loc,siz,format,comment,len); break;
case AIFF_sound_file: write_aiff_header(chan,srate,chans,loc,siz,format,comment,len); break;
case RIFF_sound_file: write_riff_header(chan,srate,chans,loc,siz,format,comment,len); break;
default:
{
printf("cannot write this header!");
fflush(stdout);
break;
}
}
close(chan);
return(0);
}
void c_update_header_with_fd(int chan, int type, int siz)
{
lseek(chan,0L,0);
switch (type)
{
case NeXT_sound_file: update_next_header(chan,siz); break;
case AIFF_sound_file: update_aiff_header(chan,siz); break;
case RIFF_sound_file: update_riff_header(chan,siz); break;
default:
{
printf("cannot update this header!");
fflush(stdout);
break;
}
}
}
int c_update_header (char *name, int type, int siz, int srate, int format, int chans, int loc)
{
int chan;
chan = clm_reopen_write(name);
if (chan == -1) return(-1);
c_update_header_with_fd(chan,type,siz);
if (type == NeXT_sound_file)
{
if (srate != 0)
{
lseek(chan,16L,0);
(*((int *)(hdrbuf))) = clm_int(srate);
write(chan,hdrbuf,4);
}
if (format != 0)
{
lseek(chan,12L,0);
(*((int *)(hdrbuf))) = clm_int(format);
write(chan,hdrbuf,4);
}
if (chans != 0)
{
lseek(chan,20L,0);
(*((int *)(hdrbuf))) = clm_int(chans);
write(chan,hdrbuf,4);
}
if (loc != 0)
{
lseek(chan,4L,0);
(*((int *)(hdrbuf))) = clm_int(loc);
write(chan,hdrbuf,4);
}
}
close(chan);
return(0);
}
/* ------------------------------------------------------------------------
* old Mus10, SAM formats, just for completeness
*
* These were used for sound data on the PDP-10s at SAIL and CCRMA in the 70's and 80's.
* The word length was 36-bits.
*
* "New" format as used by nearly all CCRMA software pre-1990:
*
* WD 0 - '525252525252 (chosen because "it is unlikely to occur at random"!)
* WD 1 - Clock rate in Hz (PDP-10 36-bit floating point) -- see trans.lisp for details
* PDP-10 floating point format was sign in bit 0, excess 128 exponent in 1-8, fraction in 9-35
* WD 2 - #samples per word,,pack-code, (has # samples per word in LH, pack-code in RH)
* 0 for 12-bit fixed point (this was the main one)
* 1 for 18-bit fixed point
* 2 for 9-bit floating point incremental (never used)
* 3 for 36-bit floating point (never used)
* 4 for 16-bit sambox fixed point, right justified
* 5 for 20-bit sambox fixed point
* 6 for 20-bit right-adjusted fixed point (sambox SAT format=SAM output)
* 7 for 16-bit fixed point, left justified (never used)
* N>9 for N bit bytes in ILDB format (never used)
* WD 3 - # channels
* WD 4 - Maximum amplitude (if known) (PDP-10 float)
* WD 5 - number of Sambox ticks per pass (inverse of Sambox clock rate, sort of)
* WD 6 - Total #samples in file. If 0 then #wds_in_file*#samps_per_wd assumed.
* WD 7 - Block size (if any). 0 means sound is not blocked.
* WDs '10-'77 Reserved for EDSND usage, but I used it anyway.
* WDs '100-'177 Text description of file (in ASCIZ format) (ASCIZ=ASCII+null if I remember right = C string(?))
*
* "Old" format (pre-1977)
*
* WD 0 - '525252525252
* WD 1 - Clock rate => rate as integer,,code
* code=0 for 6.4Kc (or anything else)
* =1 for 12.8Kc, =2 for 25.6Kc, =3 for 51.2Kc
* =5 for 102.4Kc, =6 for 204.8Kc
* WD 2 - #sample per word,,pack
* 0 for 12 bit
* 1 for 16 bit (18 bit)
* 2 for 9 bit floating point incremental
* 3 for 36-bit floating point
* N>9 for N bit bytes in ILDB format
* WD 3 - # channels
* WD 4 - Maximum amplitude (if known, otherwise 0)
* WDs 5-77 Reserved for future expansion
* WDs 100-177 Text description of file (in ASCIZ format)
*
* ------------------------------------
* and even more esoteric... from the MIT PDP-6 (1971-76):
* JRST 1
* 0
* blkcnt,,2000
* duration,, --- ; negative halfword number to be counted down for duration
* v0 v1 v2 v3 v4 v5 ; 6 six-bit byes, each encoding pitch no. from 2.-63, 63 not used
* duration,, --
* v0 v1 v2 v3 v4 v5
* ...
* checksum
* ...
* blkcnt,,blkaddr ; neg half word in block, not counting itself or checksum,,addr of block start
* ...
* 0,,--
* 0
* checksum ;=ROT 1 and ADD including blkcnt,,blkaddr
* -1,,41
* JRST 101
* checksum
* -1,,41
* JRST 101
* checksum
* JUMPA 101
* JUMPA 101
*
* ah, the good old days...
*/
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.