1
/* $Header: /cvsroot/qgo/qgo/src/wavfile.c,v 1.6 2004/10/30 23:36:45 yfh2 Exp $
2
* Copyright: wavfile.c (c) Erik de Castro Lopo erikd@zip.com.au
4
* wavfile.c - Functions for reading and writing MS-Windoze .WAV files.
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2, or (at your option)
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* This code was originally written to manipulate Windoze .WAV files
17
* under i386 Linux. Please send any bug reports or requests for
18
* enhancements to : erikd@zip.com.au
21
* Revision 1.4 2004/05/26 17:44:05 yfh2
22
* 0.2.1 b1 preparing BSD compatibiility
24
* Revision 1.3 2003/04/14 07:41:49 frosla
25
* 0.0.16b8: skipped some widget; cosmetics
27
* Revision 1.1.1.1 1999/11/21 19:50:56 wwg
28
* Import wavplay-1.3 into CVS
30
* Revision 1.2 1997/04/17 00:59:55 wwg
31
* Fixed so that a fmt chunk that is larger than expected
32
* is accepted, but if smaller - _then_ treat it as an
35
* Revision 1.1 1997/04/14 00:14:38 wwg
39
* 16.02.97 - Erik de Castro Lopo
40
* Ported from MS-Windows to Linux for Warren W. Gay's
46
static const char rcsid[] = "@(#)wavfile.c $Revision: 1.6 $";
59
#include <sys/types.h>
61
#include <sys/ioctl.h>
63
#include <linux/soundcard.h>
72
#define BUFFERSIZE 1024
73
#define PCM_WAVE_FORMAT 1
78
#define RECPLAY_UPDATES_PER_SEC 3
85
u_long dwSamplesPerSec ;
86
u_long dwAvgBytesPerSec ;
88
u_short wBitsPerSample ;
99
u_long nSamplesPerSec ;
100
u_long nAvgBytesPerSec ;
101
u_short nBlockAlign ;
102
u_short wBitsPerSample ;
107
/*=================================================================================================*/
109
char* findchunk (char* s1, char* s2, size_t n) ;
111
/*=================================================================================================*/
114
static WAVE_HEADER waveheader =
115
{ { 'R', 'I', 'F', 'F' },
117
{ 'W', 'A', 'V', 'E' },
118
{ 'f', 'm', 't', ' ' },
120
PCM_WAVE_FORMAT, /* wFormatTag*/
126
{ 'd', 'a', 't', 'a' },
130
static ErrFunc v_erf; /* wwg: Error reporting function */
132
static void _v_erf(const char *, va_list); /* This module's error reporting function */
133
static char emsg[2048];
138
* Error reporting function for this source module:
141
err(const char *format,...) {
143
fprintf(stdout, "error : %s \n",format);
144
/*/if ( v_erf == NULL )*/
145
/*/ return; /* Only report error if we have function */
147
_v_erf(format,ap); /* Use caller's supplied function */
153
_v_erf(const char *format,va_list ap) {
154
vsprintf(emsg,format,ap); /* Capture message into emsg[] */
159
int WaveReadHeader (int wavefile, int* channels, u_long* samplerate, int* samplebits, u_long* samples, u_long* datastart,ErrFunc erf)
160
{ static WAVEFORMAT waveformat ;
161
static char buffer [ BUFFERSIZE ] ; /* Function is not reentrant.*/
165
v_erf = erf; /* wwg: Set error reporting function */
167
if (lseek (wavefile, 0L, SEEK_SET)) {
168
err("%s",sys_errlist[errno]); /* wwg: Report error */
172
read (wavefile, buffer, BUFFERSIZE) ;
174
if (findchunk (buffer, "RIFF", BUFFERSIZE) != buffer) {
175
err("Bad format: Cannot find RIFF file marker"); /* wwg: Report error */
179
if (! findchunk (buffer, "WAVE", BUFFERSIZE)) {
180
err("Bad format: Cannot find WAVE file marker"); /* wwg: report error */
184
ptr = findchunk (buffer, "fmt ", BUFFERSIZE) ;
187
err("Bad format: Cannot find 'fmt' file marker"); /* wwg: report error */
188
return WR_BADFORMAT ;
191
ptr += 4 ; /* Move past "fmt ".*/
192
memcpy (&waveformat, ptr, sizeof (WAVEFORMAT)) ;
194
if (waveformat.dwSize < (sizeof (WAVEFORMAT) - sizeof (u_long))) {
195
err("Bad format: Bad fmt size"); /* wwg: report error */
196
return WR_BADFORMATSIZE ;
199
if (waveformat.wFormatTag != PCM_WAVE_FORMAT) {
200
err("Only supports PCM wave format"); /* wwg: report error */
201
return WR_NOTPCMFORMAT ;
204
ptr = findchunk (buffer, "data", BUFFERSIZE) ;
207
err("Bad format: unable to find 'data' file marker"); /* wwg: report error */
208
return WR_NODATACHUNK ;
211
ptr += 4 ; /* Move past "data".*/
212
memcpy (&databytes, ptr, sizeof (u_long)) ;
214
/* Everything is now cool, so fill in output data.*/
216
*channels = waveformat.wChannels ;
217
*samplerate = waveformat.dwSamplesPerSec ;
218
*samplebits = waveformat.wBitsPerSample ;
219
*samples = databytes / waveformat.wBlockAlign ;
221
*datastart = ((u_long) (ptr + 4)) - ((u_long) (&(buffer[0]))) ;
223
if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wBlockAlign) {
224
err("Bad file format"); /* wwg: report error */
225
return WR_BADFORMATDATA ;
228
if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wChannels / ((waveformat.wBitsPerSample == 16) ? 2 : 1)) {
229
err("Bad file format"); /* wwg: report error */
230
return WR_BADFORMATDATA ;
234
} ; /* WaveReadHeader*/
236
/*===========================================================================================*/
239
char* WaveFileError (int errno)
241
{ case WW_BADOUTPUTFILE : return "Bad output file.\n" ;
242
case WW_BADWRITEHEADER : return "Not able to write WAV header.\n" ;
244
case WR_BADALLOC : return "Not able to allocate memory.\n" ;
245
case WR_BADSEEK : return "fseek failed.\n" ;
246
case WR_BADRIFF : return "Not able to find 'RIFF' file marker.\n" ;
247
case WR_BADWAVE : return "Not able to find 'WAVE' file marker.\n" ;
248
case WR_BADFORMAT : return "Not able to find 'fmt ' file marker.\n" ;
249
case WR_BADFORMATSIZE : return "Format size incorrect.\n" ;
250
case WR_NOTPCMFORMAT : return "Not PCM format WAV file.\n" ;
251
case WR_NODATACHUNK : return "Not able to find 'data' file marker.\n" ;
252
case WR_BADFORMATDATA : return "Format data questionable.\n" ;
253
default : return "No error\n" ;
256
} ; /* WaveFileError*/
258
/*===========================================================================================*/
260
char* findchunk (char* pstart, char* fourcc, size_t n)
266
while (pstart < pend)
267
{ if (*pstart == *fourcc) /* found match for first char*/
269
for (k = 1 ; fourcc [k] != 0 ; k++)
270
test = (test ? ( pstart [k] == fourcc [k] ) : FALSE) ;
275
} ; /* while lpstart*/
280
/* $Source: /cvsroot/qgo/qgo/src/wavfile.c,v $ */
282
* Internal routine to allocate WAVFILE structure:
285
wavfile_alloc(const char *Pathname) {
286
WAVFILE *wfile = (WAVFILE *) malloc(sizeof (WAVFILE));
288
if ( wfile == NULL ) {
289
err("%s: Allocating WAVFILE structure",sys_errlist[ENOMEM]);
293
memset(wfile,0,sizeof *wfile);
295
if ( (wfile->Pathname = strdup(Pathname)) == NULL ) {
297
err("%s: Allocating storage for WAVFILE.Pathname",sys_errlist[ENOMEM]);
301
wfile->fd = -1; /* Initialize fd as not open */
302
wfile->wavinfo.Channels = Mono;
303
wfile->wavinfo.DataBits = 8;
309
* Internal routine to release WAVFILE structure:
310
* No errors reported.
313
wavfile_free(WAVFILE *wfile) {
314
if ( wfile->Pathname != NULL )
315
free(wfile->Pathname);
320
* Open a WAV file for reading: returns (WAVFILE *)
322
* The opened file is positioned at the first byte of WAV file data, or
323
* NULL is returned if the open is unsuccessful.
326
WavOpenForRead(const char *Pathname,ErrFunc erf) {
327
WAVFILE *wfile = wavfile_alloc(Pathname);
328
int e; /* Saved errno value */
329
UInt32 offset; /* File offset */
330
Byte ubuf[4]; /* 4 byte buffer */
331
UInt32 dbytes; /* Data byte count */
332
/* wavfile.c values : */
333
int channels; /* Channels recorded in this wav file */
334
u_long samplerate; /* Sampling rate */
335
int sample_bits; /* data bit size (8/12/16) */
336
u_long samples; /* The number of samples in this file */
337
u_long datastart; /* The offset to the wav data */
339
v_erf = erf; /* Set error reporting function */
342
return NULL; /* Insufficient memory (class B msg) */
345
* Open the file for reading:
347
if ( (wfile->fd = open(wfile->Pathname,O_RDONLY)) < 0 ) {
348
err("%s:\nOpening WAV file %s",
354
if ( lseek(wfile->fd,0L,SEEK_SET) != 0L ) {
355
err("%s:\nRewinding WAV file %s",
358
goto errxit; /* Wav file must be seekable device */
361
if ( (e = WaveReadHeader(wfile->fd,&channels,&samplerate,&sample_bits,&samples,&datastart,_v_erf)) != 0 ) {
362
err("%s:\nReading WAV header from %s",
369
* Copy WAV data over to WAVFILE struct:
372
wfile->wavinfo.Channels = Stereo;
373
else wfile->wavinfo.Channels = Mono;
375
wfile->wavinfo.SamplingRate = (UInt32) samplerate;
376
wfile->wavinfo.Samples = (UInt32) samples;
377
wfile->wavinfo.DataBits = (UInt16) sample_bits;
378
wfile->wavinfo.DataStart = (UInt32) datastart;
379
wfile->num_samples = wfile->wavinfo.Samples;
380
wfile->rw = 'R'; /* Read mode */
382
offset = wfile->wavinfo.DataStart - 4;
385
* Seek to byte count and read dbytes:
387
if ( lseek(wfile->fd,offset,SEEK_SET) != offset ) {
388
err("%s:\nSeeking to WAV data in %s",sys_errlist[errno],wfile->Pathname);
389
goto errxit; /* Seek failure */
392
if ( read(wfile->fd,ubuf,4) != 4 ) {
393
err("%s:\nReading dbytes from %s",sys_errlist[errno],wfile->Pathname);
398
* Put little endian value into 32 bit value:
401
dbytes = (dbytes << 8) | ubuf[2];
402
dbytes = (dbytes << 8) | ubuf[1];
403
dbytes = (dbytes << 8) | ubuf[0];
405
wfile->wavinfo.DataBytes = dbytes;
410
return wfile; /* Return open descriptor */
413
* Return error after failed open:
415
errxit: e = errno; /* Save errno */
416
free(wfile->Pathname); /* Dispose of copied pathname */
417
free(wfile); /* Dispose of WAVFILE struct */
418
errno = e; /* Restore error number */
419
return NULL; /* Return error indication */
423
* Apply command line option overrides to the interpretation of the input
428
WavReadOverrides(WAVFILE *wfile,WavPlayOpts *wavopts) {
432
* Override sampling rate: -s sampling_rate
434
if ( wavopts->SamplingRate.optChar != 0 ) {
435
wfile->wavinfo.SamplingRate = wavopts->SamplingRate.optValue;
436
wfile->wavinfo.bOvrSampling = 1;
440
* Override mono/stereo mode: -S / -M
442
if ( wavopts->Channels.optChar != 0 ) {
443
wfile->wavinfo.Channels = wavopts->Channels.optValue;
444
wfile->wavinfo.bOvrMode = 1;
448
* Override the sample size in bits: -b bits
450
if ( wavopts->DataBits.optChar != 0 ) {
451
wfile->wavinfo.DataBits = wavopts->DataBits.optValue;
452
wfile->wavinfo.bOvrBits = 1;
456
* Set the first sample:
458
wfile->StartSample = 0;
459
num_samples = wfile->wavinfo.Samples = wfile->num_samples;
460
if ( wavopts->StartSample != 0 ) {
461
wfile->StartSample = wavopts->StartSample;
462
wfile->wavinfo.Samples -= wfile->StartSample;
466
* Override # of samples if -t seconds option given:
468
if ( wavopts->Seconds != 0 ) {
469
wfile->wavinfo.Samples = wavopts->Seconds * wfile->wavinfo.SamplingRate;
470
if (wfile->StartSample+wfile->wavinfo.Samples > num_samples)
471
wfile->wavinfo.Samples = num_samples-1;
479
WavClose(WAVFILE *wfile,ErrFunc erf) {
480
int e = 0; /* Returned error code */
481
int channels; /* Channels recorded in this wav file */
482
u_long samplerate; /* Sampling rate */
483
int sample_bits; /* data bit size (8/12/16) */
484
u_long samples; /* The number of samples in this file */
485
u_long datastart; /* The offset to the wav data */
486
long fpos; /* File position in bytes */
488
v_erf = erf; /* Set error reporting function */
490
if ( wfile == NULL ) {
491
err("%s: WAVFILE pointer is NULL!",sys_errlist[EINVAL]);
497
* If the wav file was open for write, update the actual number
498
* of samples written to the file:
500
if ( wfile->rw == 'W' ) {
501
fpos = lseek(wfile->fd,0L,SEEK_CUR); /* Get out file position */
502
if ( (e = WaveReadHeader(wfile->fd,&channels,&samplerate,&sample_bits,&samples,&datastart,_v_erf)) != 0 )
503
err("%s:\nReading WAV header from %s",emsg,wfile->Pathname);
504
else if ( lseek(wfile->fd,(long)(datastart-4),SEEK_SET) != (long)(datastart-4) )
505
err("%s:\nSeeking in WAV header file %s",sys_errlist[errno],wfile->Pathname);
506
else if ( write(wfile->fd,&wfile->wavinfo.Samples,sizeof wfile->wavinfo.Samples) != sizeof wfile->wavinfo.Samples )
507
err("%s:\nWriting in WAV header file %s",sys_errlist[errno],wfile->Pathname);
510
* 'data' chunk was updated OK: Now we have to update the RIFF block
511
* count. Someday very soon, a real RIFF module is going to replace
514
if ( ftruncate(wfile->fd,(size_t)fpos) )
515
err("%s:\nTruncating file %s to correct size",
518
else if ( lseek(wfile->fd,4L,SEEK_SET) < 0L )
519
err("%s:\nSeek 4 for RIFF block update of %s",
523
fpos -= 8; /* Byte count for RIFF block */
524
if ( write(wfile->fd,&fpos,sizeof fpos) != sizeof fpos )
525
err("%s:\nUpdate of RIFF block count in %s failed",
532
if ( close(wfile->fd) < 0 ) {
533
err("%s:\nClosing WAV file",sys_errlist[errno]);
534
e = errno; /* Save errno value to return */
537
wavfile_free(wfile); /* Release WAVFILE structure */
539
if ( (errno = e) != 0 )
540
return -1; /* Failed exit */
541
return 0; /* Successful exit */
547
* Open /dev/dsp for reading or writing:
550
OpenDSP(WAVFILE *wfile,int omode,ErrFunc erf) {
551
int e; /* Saved errno value */
552
int t; /* Work int */
553
unsigned long ul; /* Work unsigned long */
554
DSPFILE *dfile = (DSPFILE *) malloc(sizeof (DSPFILE));
556
v_erf = erf; /* Set error reporting function */
558
if ( dfile == NULL ) {
559
err("%s: Opening DSP device",sys_errlist[errno=ENOMEM]);
563
memset(dfile,0,sizeof *dfile);
564
dfile->dspbuf = NULL;
567
* Open the device driver:
569
if ( (dfile->fd = open(AUDIODEV,omode,0)) < 0 ) {
570
err("%s:\nOpening audio device %s",
577
* Determine the audio device's block size. Should be done after
578
* setting sampling rate etc.
580
if ( ioctl(dfile->fd,SNDCTL_DSP_GETBLKSIZE,&dfile->dspblksiz) < 0 ) {
581
err("%s: Optaining DSP's block size",sys_errlist[errno]);
586
* Check the range on the buffer sizes:
588
/* Minimum was 4096 but es1370 returns 1024 for 44.1kHz, 16 bit */
589
/* and 64 for 8130Hz, 8 bit */
590
if ( dfile->dspblksiz < 32 || dfile->dspblksiz > 65536 ) {
591
err("%s: Audio block size (%d bytes)",
592
sys_errlist[errno=EINVAL],
593
(int)dfile->dspblksiz);
598
* Allocate a buffer to do the I/O through:
600
if ( (dfile->dspbuf = (char *) malloc(dfile->dspblksiz)) == NULL ) {
601
err("%s: For DSP I/O buffer",sys_errlist[errno]);
606
* Set the data bit size:
608
t = wfile->wavinfo.DataBits;
609
if ( ioctl(dfile->fd,SNDCTL_DSP_SAMPLESIZE,&t) < 0 ) {
610
err("%s: Setting DSP to %u bits",sys_errlist[errno],(unsigned)t);
615
* Set the mode to be Stereo or Mono:
617
t = wfile->wavinfo.Channels == Stereo ? 1 : 0;
618
if ( ioctl(dfile->fd,SNDCTL_DSP_STEREO,&t) < 0 ) {
619
err("%s: Unable to set DSP to %s mode",
626
* Set the sampling rate:
628
ul = wfile->wavinfo.SamplingRate;
629
if ( ioctl(dfile->fd,SNDCTL_DSP_SPEED,&ul) < 0 ) {
630
err("Unable to set audio sampling rate",sys_errlist[errno]);
635
* Return successfully opened device:
637
return dfile; /* Return file descriptor */
640
* Failed to open/initialize properly:
642
errxit: e = errno; /* Save the errno value */
644
fprintf(stdout, "error %s : \n",sys_errlist[errno]);
646
if ( dfile->fd >= 0 )
647
close(dfile->fd); /* Close device */
648
if ( dfile->dspbuf != NULL )
651
errno = e; /* Restore error code */
652
return NULL; /* Return error indication */
656
* Close the DSP device:
659
CloseDSP(DSPFILE *dfile,ErrFunc erf) {
662
v_erf = erf; /* Set error reporting function */
664
if ( dfile == NULL ) {
665
err("%s: DSPFILE is not open",sys_errlist[errno=EINVAL]);
670
if ( dfile->dspbuf != NULL )
675
err("%s: Closing DSP fd %d",sys_errlist[errno],fd);
683
* Play DSP from WAV file:
686
PlayDSP(DSPFILE *dfile,WAVFILE *wfile,DSPPROC work_proc,ErrFunc erf) {
687
UInt32 byte_count = (UInt32) wfile->wavinfo.Samples;
691
int total_bytes, update_bytes;
694
v_erf = erf; /* Set error reporting function */
697
* Check that the WAVFILE is open for reading:
699
if ( wfile->rw != 'R' ) {
700
err("%s: WAVFILE must be open for reading",sys_errlist[errno=EINVAL]);
705
* First determine how many bytes are required for each channel's sample:
707
switch ( wfile->wavinfo.DataBits ) {
715
err("%s: Cannot process %u bit samples",
716
sys_errlist[errno=EINVAL],
717
(unsigned)wfile->wavinfo.DataBits);
722
* Allow for Mono/Stereo difference:
724
if ( wfile->wavinfo.Channels == Stereo )
725
byte_count *= 2; /* Twice as many bytes for stereo */
726
else if ( wfile->wavinfo.Channels != Mono ) {
727
err("%s: DSPFILE control block is corrupted (chan_mode)",
728
sys_errlist[errno=EINVAL]);
732
byte_modulo = byte_count; /* This many bytes per sample */
733
byte_count = wfile->wavinfo.Samples * byte_modulo; /* Total bytes to process */
734
total_bytes = byte_count;
736
/* Number of bytes to write between client updates. Must be */
737
/* a multiple of dspblksiz. */
738
update_bytes = ((wfile->wavinfo.SamplingRate*byte_modulo) / (RECPLAY_UPDATES_PER_SEC*dfile->dspblksiz)) * dfile->dspblksiz;
740
if ( ioctl(dfile->fd,SNDCTL_DSP_SYNC,0) != 0 )
741
err("%s: ioctl(%d,SNDCTL_DSP_SYNC,0)",sys_errlist[errno]);
743
/* Seek to requested start sample */
744
lseek(wfile->fd,wfile->StartSample*byte_modulo,SEEK_CUR);
746
for ( ; byte_count > 0 && wfile->wavinfo.DataBytes > 0; byte_count -= (UInt32) n ) {
748
bytes = (int) ( byte_count > dfile->dspblksiz ? dfile->dspblksiz : byte_count );
750
if ( bytes > wfile->wavinfo.DataBytes ) /* Size bigger than data chunk? */
751
bytes = wfile->wavinfo.DataBytes; /* Data chunk only has this much left */
753
if ( (n = read(wfile->fd,dfile->dspbuf,bytes)) != bytes ) {
755
err("Unexpected EOF reading samples from WAV file",sys_errlist[errno=EIO]);
756
else err("Reading samples from WAV file",sys_errlist[errno]);
760
if ((clntIPC >= 0) && !((total_bytes-byte_count) % update_bytes)) {
761
msg.msg_type = ToClnt_PlayState;
762
msg.bytes = sizeof(msg.u.toclnt_playstate);
763
msg.u.toclnt_playstate.SamplesLeft = byte_count / byte_modulo;
764
msg.u.toclnt_playstate.CurrentSample =
765
wfile->num_samples - msg.u.toclnt_playstate.SamplesLeft;
766
MsgToClient(clntIPC,&msg,0);
767
} /* Tell client playback status */
769
if ( write(dfile->fd,dfile->dspbuf,n) != n ) {
770
err("Writing samples to audio device",sys_errlist[errno]);
774
wfile->wavinfo.DataBytes -= (UInt32) bytes; /* We have fewer bytes left to read */
777
* The work procedure function is called when operating
778
* in server mode to check for more server messages:
780
if ( work_proc != NULL && work_proc(dfile) ) /* Did work_proc() return TRUE? */
781
break; /* Yes, quit playing */
784
#if 0 /* I think this is doing a destructive flush: disabled */
785
if ( ioctl(dfile->fd,SNDCTL_DSP_SYNC,0) != 0 )
786
err("%s: ioctl(%d,SNDCTL_DSP_SYNC,0)",sys_errlist[errno]);
788
/* Update client time display at end of sucessful play
790
msg.msg_type = ToClnt_PlayState;
791
msg.bytes = sizeof(msg.u.toclnt_playstate);
792
msg.u.toclnt_playstate.SamplesLeft = byte_count / byte_modulo;
793
msg.u.toclnt_playstate.CurrentSample =
794
wfile->num_samples - msg.u.toclnt_playstate.SamplesLeft;
795
MsgToClient(clntIPC,&msg,0);
796
} /* Tell client playback status */
797
return 0; /* All samples played successfully */
799
errxit: return -1; /* Indicate error return */