~ubuntu-branches/ubuntu/maverick/qgo/maverick

« back to all changes in this revision

Viewing changes to src/wavfile.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin A. Godisch
  • Date: 2005-01-01 23:07:10 UTC
  • Revision ID: james.westby@ubuntu.com-20050101230710-fhng6yidm47xlb2i
Tags: upstream-1.0.0-r2
ImportĀ upstreamĀ versionĀ 1.0.0-r2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
3
 *
 
4
 * wavfile.c - Functions for reading and writing MS-Windoze .WAV files.
 
5
 *
 
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)
 
9
 *      any later version.
 
10
 *      
 
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.
 
15
 *      
 
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
 
19
 *      
 
20
 *
 
21
 * Revision 1.4  2004/05/26 17:44:05  yfh2
 
22
 * 0.2.1 b1 preparing BSD compatibiility
 
23
 *
 
24
 * Revision 1.3  2003/04/14 07:41:49  frosla
 
25
 * 0.0.16b8: skipped some widget; cosmetics
 
26
 *
 
27
 * Revision 1.1.1.1  1999/11/21 19:50:56  wwg
 
28
 * Import wavplay-1.3 into CVS
 
29
 *
 
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
 
33
 * error.
 
34
 *
 
35
 * Revision 1.1  1997/04/14 00:14:38  wwg
 
36
 * Initial revision
 
37
 *
 
38
 *      change log:
 
39
 *      16.02.97 -      Erik de Castro Lopo             
 
40
 *      Ported from MS-Windows to Linux for Warren W. Gay's
 
41
 *      wavplay project.
 
42
 */     
 
43
 
 
44
#ifdef __linux__
 
45
 
 
46
static const char rcsid[] = "@(#)wavfile.c $Revision: 1.6 $";
 
47
 
 
48
 
 
49
#include <stdio.h>
 
50
#include <stdlib.h>
 
51
#include <stdarg.h>
 
52
#include <unistd.h>
 
53
#include <errno.h>
 
54
#include <fcntl.h>
 
55
#include <malloc.h>
 
56
#include <string.h>
 
57
#include <memory.h>
 
58
#include <signal.h>
 
59
#include <sys/types.h>
 
60
#include <sys/ipc.h>
 
61
#include <sys/ioctl.h>
 
62
#include <assert.h>
 
63
#include <linux/soundcard.h>
 
64
 
 
65
 
 
66
 
 
67
 
 
68
 
 
69
 
 
70
#include "wavplay.h"
 
71
 
 
72
#define         BUFFERSIZE              1024
 
73
#define         PCM_WAVE_FORMAT         1
 
74
 
 
75
#define         TRUE                    1
 
76
#define         FALSE                   0
 
77
 
 
78
#define RECPLAY_UPDATES_PER_SEC                3
 
79
 
 
80
 
 
81
typedef  struct
 
82
{       u_long     dwSize ;
 
83
        u_short    wFormatTag ;
 
84
        u_short    wChannels ;
 
85
        u_long     dwSamplesPerSec ;
 
86
        u_long     dwAvgBytesPerSec ;
 
87
        u_short    wBlockAlign ;
 
88
        u_short    wBitsPerSample ;
 
89
} WAVEFORMAT ;
 
90
 
 
91
typedef  struct
 
92
{       char            RiffID [4] ;
 
93
        u_long          RiffSize ;
 
94
        char            WaveID [4] ;
 
95
        char            FmtID  [4] ;
 
96
        u_long          FmtSize ;
 
97
        u_short         wFormatTag ;
 
98
        u_short         nChannels ;
 
99
        u_long          nSamplesPerSec ;
 
100
        u_long          nAvgBytesPerSec ;
 
101
        u_short         nBlockAlign ;
 
102
        u_short         wBitsPerSample ;
 
103
        char            DataID [4] ;
 
104
        u_long          nDataBytes ;
 
105
} WAVE_HEADER ;
 
106
 
 
107
/*=================================================================================================*/
 
108
 
 
109
char*  findchunk (char* s1, char* s2, size_t n) ;
 
110
 
 
111
/*=================================================================================================*/
 
112
 
 
113
 
 
114
static  WAVE_HEADER  waveheader =
 
115
{       { 'R', 'I', 'F', 'F' },
 
116
                0,
 
117
        { 'W', 'A', 'V', 'E' },
 
118
        { 'f', 'm', 't', ' ' },
 
119
                16,                                                             /* FmtSize*/
 
120
                PCM_WAVE_FORMAT,                                                /* wFormatTag*/
 
121
                0,                                                              /* nChannels*/
 
122
                0,
 
123
                0,
 
124
                0,
 
125
                0,
 
126
        { 'd', 'a', 't', 'a' },
 
127
                0
 
128
} ; /* waveheader*/
 
129
 
 
130
static ErrFunc v_erf;                           /* wwg: Error reporting function */
 
131
 
 
132
static void _v_erf(const char *, va_list);      /* This module's error reporting function */
 
133
static char emsg[2048];
 
134
 
 
135
 
 
136
 
 
137
/*
 
138
 * Error reporting function for this source module:
 
139
 */
 
140
static void
 
141
err(const char *format,...) {
 
142
        va_list ap;
 
143
   fprintf(stdout, "error : %s \n",format);
 
144
        /*/if ( v_erf == NULL )*/
 
145
        /*/     return;                         /* Only report error if we have function */
 
146
        va_start(ap,format);
 
147
        _v_erf(format,ap);                      /* Use caller's supplied function */
 
148
        va_end(ap);
 
149
}
 
150
 
 
151
 
 
152
static void
 
153
_v_erf(const char *format,va_list ap) {
 
154
        vsprintf(emsg,format,ap);               /* Capture message into emsg[] */
 
155
}
 
156
 
 
157
 
 
158
 
 
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.*/
 
162
        char*   ptr ;
 
163
        u_long  databytes ;
 
164
 
 
165
        v_erf = erf;                                    /* wwg: Set error reporting function */
 
166
 
 
167
        if (lseek (wavefile, 0L, SEEK_SET)) {
 
168
                err("%s",sys_errlist[errno]);           /* wwg: Report error */
 
169
                return  WR_BADSEEK ;
 
170
        }
 
171
 
 
172
        read (wavefile, buffer, BUFFERSIZE) ;
 
173
 
 
174
        if (findchunk (buffer, "RIFF", BUFFERSIZE) != buffer) {
 
175
                err("Bad format: Cannot find RIFF file marker");        /* wwg: Report error */
 
176
                return  WR_BADRIFF ;
 
177
        }
 
178
 
 
179
        if (! findchunk (buffer, "WAVE", BUFFERSIZE)) {
 
180
                err("Bad format: Cannot find WAVE file marker");        /* wwg: report error */
 
181
                return  WR_BADWAVE ;
 
182
        }
 
183
 
 
184
        ptr = findchunk (buffer, "fmt ", BUFFERSIZE) ;
 
185
 
 
186
        if (! ptr) {
 
187
                err("Bad format: Cannot find 'fmt' file marker");       /* wwg: report error */
 
188
                return  WR_BADFORMAT ;
 
189
        }
 
190
 
 
191
        ptr += 4 ;      /* Move past "fmt ".*/
 
192
        memcpy (&waveformat, ptr, sizeof (WAVEFORMAT)) ;
 
193
 
 
194
        if (waveformat.dwSize < (sizeof (WAVEFORMAT) - sizeof (u_long))) {
 
195
                err("Bad format: Bad fmt size");                        /* wwg: report error */
 
196
                return  WR_BADFORMATSIZE ;
 
197
        }
 
198
 
 
199
        if (waveformat.wFormatTag != PCM_WAVE_FORMAT) {
 
200
                err("Only supports PCM wave format");                   /* wwg: report error */
 
201
                return  WR_NOTPCMFORMAT ;
 
202
        }
 
203
 
 
204
        ptr = findchunk (buffer, "data", BUFFERSIZE) ;
 
205
 
 
206
        if (! ptr) {
 
207
                err("Bad format: unable to find 'data' file marker");   /* wwg: report error */
 
208
                return  WR_NODATACHUNK ;
 
209
        }
 
210
 
 
211
        ptr += 4 ;      /* Move past "data".*/
 
212
        memcpy (&databytes, ptr, sizeof (u_long)) ;
 
213
 
 
214
        /* Everything is now cool, so fill in output data.*/
 
215
 
 
216
        *channels   = waveformat.wChannels ;
 
217
        *samplerate = waveformat.dwSamplesPerSec ;
 
218
        *samplebits = waveformat.wBitsPerSample ;
 
219
        *samples    = databytes / waveformat.wBlockAlign ;
 
220
        
 
221
        *datastart  = ((u_long) (ptr + 4)) - ((u_long) (&(buffer[0]))) ;
 
222
 
 
223
        if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wBlockAlign) {
 
224
                err("Bad file format");                 /* wwg: report error */
 
225
                return  WR_BADFORMATDATA ;
 
226
        }
 
227
 
 
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 ;
 
231
        }
 
232
 
 
233
  return  0 ;
 
234
} ; /* WaveReadHeader*/
 
235
 
 
236
/*===========================================================================================*/
 
237
 
 
238
#if 0
 
239
char*  WaveFileError (int  errno)
 
240
{       switch (errno)
 
241
        {       case    WW_BADOUTPUTFILE        : return "Bad output file.\n" ;
 
242
                case    WW_BADWRITEHEADER       : return "Not able to write WAV header.\n" ;
 
243
                
 
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" ;
 
254
        } ;
 
255
        return  NULL ;  
 
256
} ; /* WaveFileError*/
 
257
#endif
 
258
/*===========================================================================================*/
 
259
 
 
260
char* findchunk  (char* pstart, char* fourcc, size_t n)
 
261
{       char    *pend ;
 
262
        int             k, test ;
 
263
 
 
264
        pend = pstart + n ;
 
265
 
 
266
        while (pstart < pend)
 
267
        {       if (*pstart == *fourcc)       /* found match for first char*/
 
268
                {       test = TRUE ;
 
269
                        for (k = 1 ; fourcc [k] != 0 ; k++)
 
270
                                test = (test ? ( pstart [k] == fourcc [k] ) : FALSE) ;
 
271
                        if (test)
 
272
                                return  pstart ;
 
273
                        } ; /* if*/
 
274
                pstart ++ ;
 
275
                } ; /* while lpstart*/
 
276
 
 
277
        return  NULL ;
 
278
} ; /* findchuck*/
 
279
 
 
280
/* $Source: /cvsroot/qgo/qgo/src/wavfile.c,v $ */
 
281
/*
 
282
 * Internal routine to allocate WAVFILE structure:
 
283
 */
 
284
static WAVFILE *
 
285
wavfile_alloc(const char *Pathname) {
 
286
        WAVFILE *wfile = (WAVFILE *) malloc(sizeof (WAVFILE));
 
287
 
 
288
        if ( wfile == NULL ) {
 
289
                err("%s: Allocating WAVFILE structure",sys_errlist[ENOMEM]);
 
290
                return NULL;
 
291
        }
 
292
 
 
293
        memset(wfile,0,sizeof *wfile);
 
294
 
 
295
        if ( (wfile->Pathname = strdup(Pathname)) == NULL ) {
 
296
                free(wfile);
 
297
                err("%s: Allocating storage for WAVFILE.Pathname",sys_errlist[ENOMEM]);
 
298
                return NULL;
 
299
        }
 
300
 
 
301
        wfile->fd = -1;                         /* Initialize fd as not open */
 
302
        wfile->wavinfo.Channels = Mono;
 
303
        wfile->wavinfo.DataBits = 8;
 
304
 
 
305
        return wfile;
 
306
}
 
307
 
 
308
/*
 
309
 * Internal routine to release WAVFILE structure:
 
310
 * No errors reported.
 
311
 */
 
312
static void
 
313
wavfile_free(WAVFILE *wfile) {
 
314
        if ( wfile->Pathname != NULL )
 
315
                free(wfile->Pathname);
 
316
        free(wfile);
 
317
}
 
318
 
 
319
/*
 
320
 * Open a WAV file for reading: returns (WAVFILE *)
 
321
 *
 
322
 * The opened file is positioned at the first byte of WAV file data, or
 
323
 * NULL is returned if the open is unsuccessful.
 
324
 */
 
325
WAVFILE *
 
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 */
 
338
 
 
339
        v_erf = erf;                            /* Set error reporting function */
 
340
 
 
341
        if ( wfile == NULL )
 
342
                return NULL;                    /* Insufficient memory (class B msg) */
 
343
 
 
344
        /*
 
345
         * Open the file for reading:
 
346
         */
 
347
        if ( (wfile->fd = open(wfile->Pathname,O_RDONLY)) < 0 ) {
 
348
                err("%s:\nOpening WAV file %s",
 
349
                        sys_errlist[errno],
 
350
                        wfile->Pathname);
 
351
                goto errxit;
 
352
        }
 
353
 
 
354
        if ( lseek(wfile->fd,0L,SEEK_SET) != 0L ) {
 
355
                err("%s:\nRewinding WAV file %s",
 
356
                        sys_errlist[errno],
 
357
                        wfile->Pathname);
 
358
                goto errxit;            /* Wav file must be seekable device */
 
359
        }
 
360
 
 
361
        if ( (e = WaveReadHeader(wfile->fd,&channels,&samplerate,&sample_bits,&samples,&datastart,_v_erf)) != 0 ) {
 
362
                err("%s:\nReading WAV header from %s",
 
363
                        emsg,
 
364
                        wfile->Pathname);
 
365
                goto errxit;
 
366
        }
 
367
 
 
368
        /*
 
369
         * Copy WAV data over to WAVFILE struct:
 
370
         */
 
371
        if ( channels == 2 )
 
372
                wfile->wavinfo.Channels = Stereo;
 
373
        else    wfile->wavinfo.Channels = Mono;
 
374
 
 
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 */
 
381
 
 
382
        offset = wfile->wavinfo.DataStart - 4;
 
383
 
 
384
        /*
 
385
         * Seek to byte count and read dbytes:
 
386
         */
 
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 */
 
390
        }
 
391
 
 
392
        if ( read(wfile->fd,ubuf,4) != 4 ) {
 
393
                err("%s:\nReading dbytes from %s",sys_errlist[errno],wfile->Pathname);
 
394
                goto errxit;
 
395
        }
 
396
 
 
397
        /*
 
398
         * Put little endian value into 32 bit value:
 
399
         */
 
400
        dbytes = ubuf[3];
 
401
        dbytes = (dbytes << 8) | ubuf[2];
 
402
        dbytes = (dbytes << 8) | ubuf[1];
 
403
        dbytes = (dbytes << 8) | ubuf[0];
 
404
 
 
405
        wfile->wavinfo.DataBytes = dbytes;
 
406
 
 
407
        /*
 
408
         * Open succeeded:
 
409
         */
 
410
        return wfile;                           /* Return open descriptor */
 
411
 
 
412
        /*
 
413
         * Return error after failed open:
 
414
         */
 
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 */
 
420
}
 
421
 
 
422
/*
 
423
 * Apply command line option overrides to the interpretation of the input
 
424
 * wav file:
 
425
 *
 
426
 */
 
427
void
 
428
WavReadOverrides(WAVFILE *wfile,WavPlayOpts *wavopts) {
 
429
        UInt32 num_samples;
 
430
 
 
431
        /*
 
432
         * Override sampling rate: -s sampling_rate
 
433
         */
 
434
        if ( wavopts->SamplingRate.optChar != 0 ) {
 
435
                wfile->wavinfo.SamplingRate = wavopts->SamplingRate.optValue;
 
436
                wfile->wavinfo.bOvrSampling = 1;
 
437
        }
 
438
 
 
439
        /*
 
440
         * Override mono/stereo mode: -S / -M
 
441
         */
 
442
        if ( wavopts->Channels.optChar != 0 ) {
 
443
                wfile->wavinfo.Channels = wavopts->Channels.optValue;
 
444
                wfile->wavinfo.bOvrMode = 1;
 
445
        }
 
446
 
 
447
        /*
 
448
         * Override the sample size in bits: -b bits
 
449
         */
 
450
        if ( wavopts->DataBits.optChar != 0 ) {
 
451
                wfile->wavinfo.DataBits = wavopts->DataBits.optValue;
 
452
                wfile->wavinfo.bOvrBits = 1;
 
453
        }
 
454
 
 
455
        /*
 
456
         * Set the first sample:
 
457
         */
 
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;
 
463
        }
 
464
 
 
465
        /*
 
466
         * Override # of samples if -t seconds option given:
 
467
         */
 
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;
 
472
        }
 
473
}
 
474
 
 
475
/*
 
476
 * Close a WAVFILE
 
477
 */
 
478
int
 
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 */
 
487
 
 
488
        v_erf = erf;                            /* Set error reporting function */
 
489
 
 
490
        if ( wfile == NULL ) {
 
491
                err("%s: WAVFILE pointer is NULL!",sys_errlist[EINVAL]);
 
492
                errno = EINVAL;
 
493
                return -1;
 
494
        }
 
495
 
 
496
        /*
 
497
         * If the wav file was open for write, update the actual number
 
498
         * of samples written to the file:
 
499
         */
 
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);
 
508
                else    {
 
509
                        /*
 
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
 
512
                         * this fudging.
 
513
                         */
 
514
                        if ( ftruncate(wfile->fd,(size_t)fpos) )
 
515
                                err("%s:\nTruncating file %s to correct size",
 
516
                                        sys_errlist[errno],
 
517
                                        wfile->Pathname);
 
518
                        else if ( lseek(wfile->fd,4L,SEEK_SET) < 0L )
 
519
                                err("%s:\nSeek 4 for RIFF block update of %s",
 
520
                                        sys_errlist[errno],
 
521
                                        wfile->Pathname);
 
522
                        else    {
 
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",
 
526
                                                sys_errlist[errno],
 
527
                                                wfile->Pathname);
 
528
                        }
 
529
                }
 
530
        }
 
531
 
 
532
        if ( close(wfile->fd) < 0 ) {
 
533
                err("%s:\nClosing WAV file",sys_errlist[errno]);
 
534
                e = errno;                      /* Save errno value to return */
 
535
        }
 
536
 
 
537
        wavfile_free(wfile);                    /* Release WAVFILE structure */
 
538
 
 
539
        if ( (errno = e) != 0 )
 
540
                return -1;                      /* Failed exit */
 
541
        return 0;                               /* Successful exit */
 
542
}
 
543
 
 
544
/*
 
545
 
 
546
/*
 
547
 * Open /dev/dsp for reading or writing:
 
548
 */
 
549
DSPFILE *
 
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));
 
555
 
 
556
        v_erf = erf;                            /* Set error reporting function */
 
557
 
 
558
        if ( dfile == NULL ) {
 
559
                err("%s: Opening DSP device",sys_errlist[errno=ENOMEM]);
 
560
                return NULL;
 
561
        }
 
562
 
 
563
        memset(dfile,0,sizeof *dfile);
 
564
        dfile->dspbuf = NULL;
 
565
 
 
566
        /*
 
567
         * Open the device driver:
 
568
         */
 
569
        if ( (dfile->fd = open(AUDIODEV,omode,0)) < 0 ) {
 
570
                err("%s:\nOpening audio device %s",
 
571
                        sys_errlist[errno],
 
572
                        AUDIODEV);
 
573
                goto errxit;
 
574
        }
 
575
 
 
576
        /*
 
577
         * Determine the audio device's block size.  Should be done after
 
578
         * setting sampling rate etc.
 
579
         */
 
580
        if ( ioctl(dfile->fd,SNDCTL_DSP_GETBLKSIZE,&dfile->dspblksiz) < 0 ) {
 
581
                err("%s: Optaining DSP's block size",sys_errlist[errno]);
 
582
                goto errxit;
 
583
        }
 
584
 
 
585
        /*
 
586
         * Check the range on the buffer sizes:
 
587
         */
 
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);
 
594
                goto errxit;
 
595
        }
 
596
 
 
597
        /*
 
598
         * Allocate a buffer to do the I/O through:
 
599
         */
 
600
        if ( (dfile->dspbuf = (char *) malloc(dfile->dspblksiz)) == NULL ) {
 
601
                err("%s: For DSP I/O buffer",sys_errlist[errno]);
 
602
                goto errxit;
 
603
        }
 
604
 
 
605
        /*
 
606
         * Set the data bit size:
 
607
         */
 
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);
 
611
                goto errxit;
 
612
        }
 
613
 
 
614
        /*
 
615
         * Set the mode to be Stereo or Mono:
 
616
         */
 
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",
 
620
                        sys_errlist[errno],
 
621
                        t?"Stereo":"Mono");
 
622
                goto errxit;
 
623
        }
 
624
 
 
625
        /*
 
626
         * Set the sampling rate:
 
627
         */
 
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]);
 
631
                goto errxit;
 
632
        }
 
633
 
 
634
        /*
 
635
         * Return successfully opened device:
 
636
         */
 
637
        return dfile;                           /* Return file descriptor */
 
638
 
 
639
        /*
 
640
         * Failed to open/initialize properly:
 
641
         */
 
642
errxit: e = errno;                              /* Save the errno value */
 
643
 
 
644
 fprintf(stdout, "error %s : \n",sys_errlist[errno]);
 
645
 
 
646
        if ( dfile->fd >= 0 )
 
647
                close(dfile->fd);               /* Close device */
 
648
        if ( dfile->dspbuf != NULL )
 
649
                free(dfile->dspbuf);
 
650
        free(dfile);
 
651
        errno = e;                              /* Restore error code */
 
652
        return NULL;                            /* Return error indication */
 
653
}
 
654
 
 
655
/*
 
656
 * Close the DSP device:
 
657
 */
 
658
int
 
659
CloseDSP(DSPFILE *dfile,ErrFunc erf) {
 
660
        int fd;
 
661
 
 
662
        v_erf = erf;                            /* Set error reporting function */
 
663
 
 
664
        if ( dfile == NULL ) {
 
665
                err("%s: DSPFILE is not open",sys_errlist[errno=EINVAL]);
 
666
                return -1;
 
667
        }
 
668
 
 
669
        fd = dfile->fd;
 
670
        if ( dfile->dspbuf != NULL )
 
671
                free(dfile->dspbuf);
 
672
        free(dfile);
 
673
 
 
674
        if ( close(fd) ) {
 
675
                err("%s: Closing DSP fd %d",sys_errlist[errno],fd);
 
676
                return -1;
 
677
        }
 
678
 
 
679
        return 0;
 
680
}
 
681
 
 
682
/*
 
683
 * Play DSP from WAV file:
 
684
 */
 
685
int
 
686
PlayDSP(DSPFILE *dfile,WAVFILE *wfile,DSPPROC work_proc,ErrFunc erf) {
 
687
        UInt32 byte_count = (UInt32) wfile->wavinfo.Samples;
 
688
        int bytes;
 
689
        int n;
 
690
        int byte_modulo;
 
691
        int total_bytes, update_bytes;
 
692
        SVRMSG msg;
 
693
 
 
694
        v_erf = erf;                            /* Set error reporting function */
 
695
 
 
696
        /*
 
697
         * Check that the WAVFILE is open for reading:
 
698
         */
 
699
        if ( wfile->rw != 'R' ) {
 
700
                err("%s: WAVFILE must be open for reading",sys_errlist[errno=EINVAL]);
 
701
                return -1;
 
702
        }
 
703
 
 
704
        /*
 
705
         * First determine how many bytes are required for each channel's sample:
 
706
         */
 
707
        switch ( wfile->wavinfo.DataBits ) {
 
708
        case 8 :
 
709
                byte_count = 1;
 
710
                break;
 
711
        case 16 :
 
712
                byte_count = 2;
 
713
                break;
 
714
        default :
 
715
                err("%s: Cannot process %u bit samples",
 
716
                        sys_errlist[errno=EINVAL],
 
717
                        (unsigned)wfile->wavinfo.DataBits);
 
718
                return -1;
 
719
        }
 
720
 
 
721
        /*
 
722
         * Allow for Mono/Stereo difference:
 
723
         */
 
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]);
 
729
                return -1;
 
730
        }
 
731
 
 
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;
 
735
 
 
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;
 
739
 
 
740
        if ( ioctl(dfile->fd,SNDCTL_DSP_SYNC,0) != 0 )
 
741
                err("%s: ioctl(%d,SNDCTL_DSP_SYNC,0)",sys_errlist[errno]);
 
742
 
 
743
        /* Seek to requested start sample */
 
744
        lseek(wfile->fd,wfile->StartSample*byte_modulo,SEEK_CUR);
 
745
 
 
746
        for ( ; byte_count > 0 && wfile->wavinfo.DataBytes > 0; byte_count -= (UInt32) n ) {
 
747
 
 
748
                bytes = (int) ( byte_count > dfile->dspblksiz ? dfile->dspblksiz : byte_count );
 
749
 
 
750
                if ( bytes > wfile->wavinfo.DataBytes ) /* Size bigger than data chunk? */
 
751
                        bytes = wfile->wavinfo.DataBytes;       /* Data chunk only has this much left */
 
752
 
 
753
                if ( (n = read(wfile->fd,dfile->dspbuf,bytes)) != bytes ) {
 
754
                        if ( n >= 0 )
 
755
                                err("Unexpected EOF reading samples from WAV file",sys_errlist[errno=EIO]);
 
756
                        else    err("Reading samples from WAV file",sys_errlist[errno]);
 
757
                        goto errxit;
 
758
                }
 
759
                /*
 
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 */
 
768
 
 
769
                if ( write(dfile->fd,dfile->dspbuf,n) != n ) {
 
770
                        err("Writing samples to audio device",sys_errlist[errno]);
 
771
                        goto errxit;
 
772
                }
 
773
 
 
774
                wfile->wavinfo.DataBytes -= (UInt32) bytes;     /* We have fewer bytes left to read */
 
775
 
 
776
                /*
 
777
                 * The work procedure function is called when operating
 
778
                 * in server mode to check for more server messages:
 
779
                 */
 
780
                if ( work_proc != NULL && work_proc(dfile) )    /* Did work_proc() return TRUE? */
 
781
                        break;                                  /* Yes, quit playing */
 
782
        }
 
783
 
 
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]);
 
787
#endif
 
788
        /* Update client time display at end of sucessful play
 
789
        if (clntIPC >= 0) {
 
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 */
 
798
 
 
799
errxit: return -1;      /* Indicate error return */
 
800
}
 
801
 
 
802
 
 
803
 
 
804
 
 
805
#endif