1 /*-------------- Telecommunications & Signal Processing Lab ---------------
2                              McGill University
3 
4 Routine:
5   int AFdReadData (AFILE *AFp, long int offs, double Dbuff[], int Nreq)
6 
7 Purpose:
8   Read data from an audio file (return double values)
9 
10 Description:
11   This routine returns a specified number of samples at a given sample offset
12   in an audio file.  The data in the file is converted to double format on
13   output.  The sample data in the file is considered to be preceded and
14   followed by zero-valued samples.  Thus if the sample offset is negative or
15   points to beyond the number of samples in the file, the appropriate number
16   of zero-valued samples is returned.  The file must have been opened using
17   routine AFopnRead.
18 
19   The following program fragment illustrates the use of this routine to read
20   overlapping frames of data.  For the simpler case of sequential access to the
21   data without overlap, the variable Lmem should be set to zero.
22 
23     AFp = AFopnRead (...);
24     ...
25     Lmem =...
26     Lframe =...
27     Nadv = Lframe-Lmem;
28     offs = -Lmem;
29     while (1) {
30       Nout = AFdReadData (AFp, offs, Dbuff, Lframe);
31       offs = offs+Nadv;
32       if (Nout == 0)
33         break;
34       ...
35     }
36 
37   On encountering an error, the default behaviour is to print an error message
38   and halt execution.
39 
40 Parameters:
41   <-  int AFdReadData
42       Number of data values transferred from the file.  On reaching the end of
43       the file, this value may be less than Nreq, in which case the last
44       elements are set to zero.  This value can be used by the calling routine
45       to determine when the data from the file has been exhausted.
46    -> AFILE *AFp
47       Audio file pointer for an audio file opened by AFopnRead
48    -> long int offs
49       Offset into the file in samples.  If offs is positive, the first value
50       returned is offs samples from the beginning of the data.  The file data
51       is considered to be preceded by zeros.  Thus if offs is negative, the
52       appropriate number of zeros will be returned.  These zeros before the
53       beginning of the data are counted as part of the count returned in Nout.
54   <-  double Dbuff[]
55       Array of doubles to receive the Nreq samples
56    -> int Nreq
57       Number of samples requested.  Nreq may be zero.
58 
59 Author / revision:
60   P. Kabal  Copyright (C) 2003
61   $Revision: 1.2 $  $Date: 2003/05/09 01:03:46 $
62 
63 -------------------------------------------------------------------------*/
64 
65 #include <assert.h>
66 #include <stdlib.h>		/* EXIT_FAILURE */
67 
68 #include <libtsp.h>
69 #include <libtsp/AFdataio.h>
70 #include <libtsp/AFmsg.h>
71 #include <libtsp/AFpar.h>
72 
73 #define MINV(a, b)	(((a) < (b)) ? (a) : (b))
74 #define MAXV(a, b)	(((a) > (b)) ? (a) : (b))
75 
76 /* Reading routines */
77 static int
78 (*AF_Read[NFD])(AFILE *AFp, double Dbuff[], int Nreq) =
79   { NULL,	AFdRdMulaw,	AFdRdAlaw,	AFdRdU1,	AFdRdI1,
80     AFdRdI2,	AFdRdI3,	AFdRdI4,	AFdRdF4,	AFdRdF8,
81     AFdRdTA };
82 
83 /*
84   The option flag ErrorHalt affects error handling.
85   If ErrorHalt is clear, execution continues after an error
86    - Fewer than requested elements are returned.  To distinguish between an
87      error and and end-of-file, AFp->Error must be examined.
88    - An unexpected end-of-file is an error
89    - AFp->Error must be zero on input to this routine
90 */
91 
92 
93 int
AFdReadData(AFILE * AFp,long int offs,double Dbuff[],int Nreq)94 AFdReadData (AFILE *AFp, long int offs, double Dbuff[], int Nreq)
95 
96 {
97   int i, Nb, Nv, Nr, Nout;
98 
99 /* Check the operation  */
100   assert (AFp->Op == FO_RO);
101   assert (! AFp->Error);
102 
103 /* Fill in zeros at the beginning of data */
104   Nb = (int) MAXV (0, MINV (-offs, Nreq));
105   for (i = 0; i < Nb; ++i) {
106     Dbuff[i] = 0.0;
107     ++offs;
108   }
109   Nout = Nb;
110 
111 /* Position the file */
112   AFp->Error = AFposition (AFp, offs);
113 
114 /* The file reading routines expect that the file is positioned at the data
115    to be read.  They use the following AFp fields:
116      AFp->fp - file pointer
117      AFp->Swapb - data swap indicator
118      AFp->ScaleF - data scaling factor
119   An error is detected on the outside by calling ferror() or by checking
120   AFp->Error (for text data files).
121 
122   Errors:  Nr < Nreq - Nb  && ferror or AFp->Error set
123   EOF:     Nr < Nreq - Nb  && ferror and AFp->Error not set
124 
125   This routine updates the following AFp values
126     AFp->Error - Set for an error
127     AFp->Isamp - Current data sample.  This value is set to the current
128       position before reading and incremented by the number of samples read.
129     AFp->Nsamp - Number of samples (updated if not defined initially and EOF
130       is detected)
131 */
132 
133 /* Transfer data from the file */
134   if (AFp->Nsamp == AF_NSAMP_UNDEF)
135     Nv = Nreq - Nout;
136   else
137     Nv = (int) MINV (Nreq - Nout, AFp->Nsamp - offs);
138 
139   if (! AFp->Error && Nv > 0) {
140     Nr = (*AF_Read[AFp->Format]) (AFp, &Dbuff[Nb], Nv);
141     Nout += Nr;
142     AFp->Isamp += Nr;
143 
144 /* Check for errors */
145     if (Nr < Nv) {
146       if (ferror (AFp->fp)) {
147 	UTsysMsg ("AFdReadData - %s %ld", AFM_ReadErrOffs, AFp->Isamp);
148 	AFp->Error = AF_IOERR;
149       }
150       else if (AFp->Error)
151 	UTwarn ("AFdReadData - %s %ld", AFM_ReadErrOffs, AFp->Isamp);
152       else if (AFp->Nsamp != AF_NSAMP_UNDEF) {
153 	UTwarn ("AFdReadData - %s %ld", AFM_UEoFOffs, AFp->Isamp);
154 	AFp->Error = AF_UEOF;
155       }
156       else
157 	AFp->Nsamp = AFp->Isamp;
158     }
159   }
160 
161 /* Zeros at the end of the file */
162   for (i = Nout; i < Nreq; ++i)
163     Dbuff[i] = 0.0;
164 
165   if (AFp->Error && (AFoptions ())->ErrorHalt)
166     exit (EXIT_FAILURE);
167 
168   return Nout;
169 }
170