#include "cs.h" /* WAVE.C */ /* Modified 1998 June 5 by Damien Miller & Robin Whittle */ /* to compile under glibc, rather than the earlier C library. */ /* One change only - search for "glibc" */ /* diskin.c and wave.c both used tell() */ #include "soundio.h" #include "wave.h" #include "sfheader.h" #include #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif static struct wav_head formhdr; static long framesize; /* RWD.2.98 to avoid recalculating variable headersizes */ static long this_hdrsize = 0; static short this_format = 0; static long datasize_offset = 0; static long factchunk_offset= 0; static long nchans = 0; /* because not enough info passed to wavReWriteHdr() ! */ static short lenshort(short sval) /* coerce a natural short into a littlendian short */ { char benchar[2]; char *p = benchar; *p++ = 0xFF & sval; *p = 0xFF & (sval >> 8); return(*(short *)benchar); } static long lenlong(long lval) /* coerce a natural long into a littlendian long */ { char benchar[4]; char *p = benchar; *p++ = (char)(0xFF & lval); *p++ = (char)(0xFF & (lval >> 8)); *p++ = (char)(0xFF & (lval >> 16)); *p = (char)(0xFF & (lval >> 24)); return(*(long *)benchar); } static short natlshort(short sval) /* coerce a littlendian short into a natural short */ { unsigned char benchar[2]; short natshort; *(short *)benchar = sval; natshort = benchar[1]; natshort <<= 8; natshort |= benchar[0]; return(natshort); } static long natllong(long lval) /* coerce a littlendian long into a natural long */ { unsigned char benchar[4]; unsigned char *p = benchar + 3; long natlong; *(long *)benchar = lval; natlong = *p--; natlong <<= 8; natlong |= *p--; natlong <<= 8; natlong |= *p--; natlong <<= 8; natlong |= *p; return(natlong); } /* RWD.2.98 many changes to set correct variable-header length etc */ void wavWriteHdr( /* Write WAV header at start of file. */ int fd, /* Called after open, before data writes*/ int sampsize, /* sample size in bytes */ int nchls, double sr) /* sampling rate */ { long databytes; #ifdef DEBUG printf("wavWriteHdr: fd %d sampsize %d nchls %d sr %lf\n", fd,sampsize,nchls,sr); #endif framesize = sampsize * nchls; databytes = 0; /* reset later by wavReWriteHdr */ formhdr.magic = *((long *)RIFF_ID); formhdr.len0 = lenlong(databytes + 36/*WAVHDRSIZ*/); formhdr.magic1 = *((long *)WAVE_ID); formhdr.magic2 = *((long *)FMT_ID); /*formhdr.len = lenlong((long)16); /* length of format chunk */ /* RWD.2.98 */ this_format = (short)(sampsize==4 ? 3 : 1); formhdr.format = lenshort(this_format); /* PCM */ formhdr.len = this_format == 3 ? lenlong(18L) : lenlong(16L); nchans = nchnls; /* RWD for wavReWriteHdr() for fact chunk */ formhdr.nchns = lenshort((short)nchls); formhdr.rate = lenlong((long)sr); formhdr.aver = lenlong((long)(sr * framesize)); formhdr.nBlockAlign = lenshort((short)framesize); /* bytes per frame */ formhdr.size = lenshort((short)(8 * sampsize)); /* bits per sample */ /* RWD.2.98 have to do these later */ #if 0 formhdr.magic3 = *((long *)DATA_ID); formhdr.datasize = lenlong(databytes); #endif if (write(fd, (char *)&formhdr, sizeof(formhdr)) != sizeof(formhdr)) die("error writing WAV header"); /* RWD add cbSize if format = 3 */ if (this_format==3){ short cbSize = 0; long factid = *((long *)FACT_ID); long chunksize = lenlong( sizeof(long)); long factdata = 0; /* filled in later... */ if ((write(fd,(char *) &cbSize,sizeof(short)) != sizeof(short)) || (write(fd,(char *)&factid,sizeof(long)) <0) || (write(fd,(char *)&chunksize,sizeof(long)) <0) || ((factchunk_offset = lseek(fd,0,SEEK_CUR)) < 0) || (write(fd,(char *)&factdata,sizeof(long)) < 0)) die("error writing WAV header"); /* add statutory fact chunk for non-PCM formats */ } /* RWD.2.98 then write data chunk info */ { long magic3 = *((long *)DATA_ID); long datasize = lenlong(databytes); if ((write(fd,(char *)&magic3, sizeof(long)) != sizeof(long)) || ((datasize_offset = lseek(fd,0,SEEK_CUR)) < 0) || (write(fd,(char *)&datasize,sizeof(long)) != sizeof(long))) die("error writing WAV header"); } this_hdrsize = datasize_offset+sizeof(long); } /* RWD: WHY aren't the same params passed here as to wavWriteHdr() ???? */ void wavReWriteHdr(int fd, /* Write proper sizes into WAV header */ long datasize) /* called before closing file */ { /* & optionally under -R */ /* The line below changed for * compatibility with glibc. * Previously "tell(fd)" */ long endpos = lseek(fd, 0, SEEK_CUR); /* RWD.2.98: all changed to deal with variable headersize */ long length = lenlong(endpos-8); long size = lenlong(datasize); long size_in_samps = lenlong((datasize / sizeof(float)) / nchans);/* for fact chunk */ if (endpos != datasize + this_hdrsize) die("inconsistent WAV size"); /* RWD.2.98: should be able to fudge something! */ if (lseek(fd, 4L, 0)<0 || write(fd, (char *)&length, sizeof(long)) != sizeof(long) || (lseek(fd,datasize_offset,0) < 0) || write(fd,(char *)&size,sizeof(long)) != sizeof(long)) die("error rewriting WAV header"); /* update fact chunk if non-PCM format */ if (this_format==(short)3) if ((lseek(fd,factchunk_offset,0) <0) || (write(fd,(char *)&size_in_samps,sizeof(long)) != sizeof(long))) die("error rewriting WAV header"); lseek(fd, endpos, 0); } int is_wav_form(long firstlong) /* test a long for wav form ID */ /* called by readheader prior to wavReadHeader */ { return (firstlong == *(long *)RIFF_ID); } void wavReadHeader( /* Read WAV header, fill hdr, & */ int fd, /* postn rd ptr to start of samps*/ char *fname, HEADATA *hdr, /* datablock for passing data back */ long firstlong, SOUNDIN *p) { struct wav_head form; IGNORE(fname); p->filetyp = 0; /* ensure no bytrev on sreadin here */ if (!is_wav_form(firstlong)) /* double check it's a form header */ die("bad form for wavReadHeader"); /* & read remainder */ sreadin(fd,(char *)&form + sizeof(long),sizeof(form) - sizeof(long),p); if (form.magic1 != *(long *) WAVE_ID) { err_printf( "Got form.magic = %lx\n", form.magic); die("form header not type wav"); } hdr->sr = natllong(form.rate); hdr->nchnls = natlshort(form.nchns); hdr->sampsize = (natlshort(form.size)) / 8; hdr->format = (hdr->sampsize == 2 ? AE_SHORT : hdr->sampsize == 1 ? AE_UNCH : form.format == 3 ? AE_FLOAT : AE_LONG); hdr->hdrsize = sizeof(form); hdr->filetyp = TYP_WAV; hdr->aiffdata = NULL; /* hdr->audsize = natllong(form.datasize); RWD.2.98 see below... */ hdr->readlong = FALSE; hdr->firstlong = 0; /* RWD.2.98 check for cbSize field */ if (form.len==18){ short cbSize; if (read(fd,(void*)&cbSize,sizeof(short)) <0) die("error skipping unknown chunk in WAV file"); if (cbSize>0) die("error reading format data: is this a compressed file?"); } /* RWD.2.98 now step through all following chunks, until we find data chunk */ { long chunk_id, chunksize; if ((read(fd,(void *)&chunk_id,sizeof(long)) < 0) || (read(fd,(void *) &chunksize,sizeof(long)) < 0) || natllong(chunksize) < 0) /*TODO: can we have zero-sized chunks???? */ die("error reading unknown chunk in WAV file"); while (chunk_id != *(long*)DATA_ID) { if (lseek(fd,natllong(chunksize),SEEK_CUR) < 0 || read(fd,(void*)&chunk_id,sizeof(long)) < 0 || read(fd,(void*)&chunksize,sizeof(long)) < 0 || natllong(chunksize) < 0) { die("error skipping unknown chunk in WAV file"); } } hdr->audsize = natllong(chunksize); hdr->hdrsize = lseek(fd,0,SEEK_CUR); /* RWD Apr 97 */ } }