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.