1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <memory.h>
34 #include <unistd.h>
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/file.h>
39 #include <sys/t_lock.h>
40
41 #include <AudioDebug.h>
42 #include <AudioUnixfile.h>
43 #include <libaudio.h>
44 #include <audio_hdr.h>
45 #include <audio/au.h>
46
47 // class AudioUnixfile methods
48
49 // Constructor with pathname and mode arg
50 AudioUnixfile::
AudioUnixfile(const char * path,const FileAccess acc)51 AudioUnixfile(
52 const char *path, // pathname
53 const FileAccess acc): // access mode
54 AudioStream(path), mode(acc), block(TRUE), fd(-1),
55 infostring(new char[1]), infolength(1)
56 {
57 infostring[0] = '\0';
58 }
59
60 // Destructor
61 AudioUnixfile::
~AudioUnixfile()62 ~AudioUnixfile()
63 {
64 // If the file is open, close it
65 if (opened())
66 (void) Close();
67
68 // Deallocate the dynamic storage
69 delete infostring;
70 }
71
72 // Generic open with search path routine just calls default Open()
73 AudioError AudioUnixfile::
OpenPath(const char *)74 OpenPath(
75 const char *)
76 {
77 return (Open());
78 }
79
80 // Decode an audio file header
81 // This routine reads the audio file header and decodes it.
82 //
83 // This method should be specialized by subclasses that are not files,
84 // like devices for instance.
85 //
86 // XXX - this routine should be rewritten for C++
87 AudioError AudioUnixfile::
decode_filehdr()88 decode_filehdr()
89 {
90 Boolean saveblock; // saved state of the blocking i/o flag
91 AudioHdr hdr_local; // local copy of header
92 Audio_hdr ohdr; // XXX - old libaudio hdr
93 au_filehdr_t fhdr;
94 char *ibuf;
95 int file_type;
96 int infosize;
97 int cnt;
98 struct stat st;
99 AudioError err;
100
101 // If fd is not open, or file header already decoded, skip it
102 if (!isfdset() || opened())
103 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
104
105 // Stat the file, to see if it is a regular file
106 if (fstat(getfd(), &st) < 0)
107 return (RaiseError(AUDIO_UNIXERROR));
108
109 // Make sure the file is not set for blocking i/o
110 saveblock = GetBlocking();
111 if (!saveblock)
112 SetBlocking(TRUE);
113
114 // Read the file header, but not the info field
115 // XXX - Should use C++ input method
116 cnt = read(getfd(), (char *)&fhdr, sizeof (fhdr));
117 if (cnt != sizeof (fhdr)) {
118 return (RaiseError(AUDIO_UNIXERROR));
119 }
120
121 // Check the validity of the header and get the size of the info field
122 err = (AudioError) audio_decode_filehdr(getfd(), (unsigned char *)&fhdr,
123 &file_type, &ohdr, &infosize);
124 if (err != AUDIO_SUCCESS)
125 return (RaiseError(err));
126
127 // Allocate and read in the info field
128 ibuf = new char[infosize];
129 cnt = read(getfd(), ibuf, infosize);
130 if (cnt != infosize) {
131 delete[] ibuf;
132 return (RaiseError(AUDIO_UNIXERROR));
133 }
134 SetBlocking(saveblock); // Restore the saved blocking i/o state
135
136 // XXX - convert from libaudio header
137 hdr_local = GetHeader();
138 hdr_local.sample_rate = ohdr.sample_rate;
139 hdr_local.samples_per_unit = ohdr.samples_per_unit;
140 hdr_local.bytes_per_unit = ohdr.bytes_per_unit;
141 hdr_local.channels = ohdr.channels;
142 hdr_local.encoding = (AudioEncoding) ohdr.encoding;
143 hdr_local.endian = BIG_ENDIAN; // Files are always written in
144 // big endian.
145
146 err = SetHeader(hdr_local);
147 if (err != AUDIO_SUCCESS) {
148 delete[] ibuf;
149 return (RaiseError(err));
150 }
151 SetInfostring(ibuf, infosize);
152 delete[] ibuf;
153
154 // Only trust the file size for regular files
155 if (S_ISREG(st.st_mode)) {
156 setlength(GetHeader().Bytes_to_Time(
157 st.st_size - infosize - sizeof (au_filehdr_t)));
158
159 // Sanity check
160 if ((ohdr.data_size != AUDIO_UNKNOWN_SIZE) &&
161 (GetLength() != GetHeader().Bytes_to_Time(ohdr.data_size)))
162 PrintMsg(_MGET_(
163 "AudioUnixfile: header/file size mismatch"));
164
165 // always consider it to be unknown if not reading a real file
166 // since there's no real way to verify if the header is
167 // correct.
168 } else {
169 setlength(AUDIO_UNKNOWN_TIME);
170 }
171
172 // set flag for opened() test
173 filehdrset = TRUE;
174
175 return (AUDIO_SUCCESS);
176 }
177
178 // Write an audio file header
179 // This routine encodes the audio file header and writes it out.
180 // XXX - It assumes that the file pointer is set to the start of the file.
181 //
182 // This method should be specialized by subclasses that are not files,
183 // like devices for instance.
184 //
185 // XXX - this routine should be rewritten for C++
186 AudioError AudioUnixfile::
encode_filehdr()187 encode_filehdr()
188 {
189 Boolean saveblock; // saved state of the blocking i/o flag
190 AudioHdr hdr_local; // local copy of header
191 Audio_hdr ohdr; // XXX - old libaudio hdr
192 AudioError err;
193
194 // If fd is not open, or file header already written, skip it
195 if (!isfdset() || opened())
196 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
197
198 // XXX - Set up the libaudio hdr
199 hdr_local = GetHeader();
200 hdr_local.endian = BIG_ENDIAN; // Files are always written big endian.
201 err = SetHeader(hdr_local);
202 if (err != AUDIO_SUCCESS) {
203 return (RaiseError(err));
204 }
205
206 ohdr.sample_rate = hdr_local.sample_rate;
207 ohdr.samples_per_unit = hdr_local.samples_per_unit;
208 ohdr.bytes_per_unit = hdr_local.bytes_per_unit;
209 ohdr.channels = hdr_local.channels;
210 ohdr.encoding = hdr_local.encoding;
211 if (Undefined(GetLength()))
212 ohdr.data_size = AUDIO_UNKNOWN_SIZE;
213 else
214 ohdr.data_size = (uint_t)GetHeader().Time_to_Bytes(GetLength());
215
216 /* Make sure the file is not set for blocking i/o */
217 saveblock = GetBlocking();
218 if (!saveblock)
219 SetBlocking(TRUE);
220
221 // XXX - Should use C++ output method
222 err = (AudioError) audio_write_filehdr(getfd(), &ohdr, FILE_AU,
223 infostring, infolength);
224
225 // set flag for opened() test
226 if (err == AUDIO_SUCCESS)
227 filehdrset = TRUE;
228
229 SetBlocking(saveblock); // Restore the saved blocking i/o state
230 return (RaiseError(err));
231 }
232
233 // Set a file blocking/non-blocking
234 // This method should be subclassed by objects that always block (eg, files)
235 void AudioUnixfile::
SetBlocking(Boolean b)236 SetBlocking(
237 Boolean b) // FALSE to set non-blocking
238 {
239 int flag;
240
241 // If the file is open, set blocking/non-blocking now
242 if (isfdset()) {
243 flag = fcntl(getfd(), F_GETFL, 0);
244 if ((flag < 0) && (errno == EOVERFLOW || errno == EINVAL)) {
245 RaiseError(AUDIO_UNIXERROR, Fatal,
246 (char *)"Large File");
247 } else if (b) {
248 flag &= ~(O_NDELAY | O_NONBLOCK); // set blocking
249 } else {
250 flag |= O_NONBLOCK; // set non-blocking
251 }
252 if (fcntl(getfd(), F_SETFL, flag) < 0) {
253 RaiseError(AUDIO_UNIXERROR, Warning);
254 }
255 }
256 // Set the blocking flag (this may affect the Open() behavior)
257 block = b;
258 }
259
260 // Return a pointer to the info string
261 // XXX - returns a pointer to the string stored in the object
262 // XXX - assumes ASCII data
263 char *AudioUnixfile::
GetInfostring(int & len) const264 GetInfostring(
265 int& len) const // returned length of string
266 {
267 len = infolength;
268 return (infostring);
269 }
270
271 // Set the info string
272 void AudioUnixfile::
SetInfostring(const char * str,int len)273 SetInfostring(
274 const char *str, // new info string
275 int len) // length of string
276 {
277 // If length defaulted, assume an ASCII string
278 if (len == -1)
279 len = strlen(str) + 1;
280 delete infostring;
281 infostring = new char[len];
282 infolength = len;
283 (void) memcpy(infostring, str, len);
284 }
285
286 // Close file
287 AudioError AudioUnixfile::
Close()288 Close()
289 {
290 // If the file is open, close it
291 if (isfdset()) {
292 if (close(getfd()) < 0)
293 return (RaiseError(AUDIO_UNIXERROR));
294 } else {
295 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
296 }
297
298 // Init important values, in case the file is reopened
299 setfd(-1);
300 filehdrset = FALSE;
301 (void) SetReadPosition((Double)0., Absolute);
302 (void) SetWritePosition((Double)0., Absolute);
303 return (AUDIO_SUCCESS);
304 }
305
306 // Read data from underlying file into specified buffer.
307 // No data format translation takes place.
308 // The object's read position is not updated (subclasses can change this)
309 AudioError AudioUnixfile::
ReadData(void * buf,size_t & len,Double & pos)310 ReadData(
311 void* buf, // destination buffer address
312 size_t& len, // buffer length (updated)
313 Double& pos) // start position (updated)
314 {
315 off_t offset;
316 off_t cnt;
317 AudioError err;
318
319 // Save buffer size and zero transfer count
320 cnt = (off_t)len;
321 len = 0;
322
323 // Cannot read if file is not open
324 if (!opened() || !mode.Readable())
325 return (RaiseError(AUDIO_ERR_NOEFFECT));
326
327 // Position must be valid
328 if (Undefined(pos) || (pos < 0.) || (cnt < 0))
329 return (RaiseError(AUDIO_ERR_BADARG));
330
331 // Position the file pointer to the right place
332 err = seekread(pos, offset);
333 if (err != AUDIO_SUCCESS)
334 return (err);
335
336 // Check for EOF
337 if (pos >= GetLength()) {
338 err = AUDIO_EOF;
339 err.sys = AUDIO_COPY_INPUT_EOF;
340 return (err);
341 }
342
343 // Zero-length reads are finished
344 if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
345 err = AUDIO_SUCCESS;
346 err.sys = AUDIO_COPY_ZERO_LIMIT;
347 return (err);
348 }
349
350 // Read as much data as possible
351 cnt = read(fd, (char *)buf, (int)cnt);
352 if (cnt < 0) {
353 if (errno == EOVERFLOW) {
354 perror("read");
355 exit(1);
356 } else if ((errno == EINTR) ||
357 (((errno == EWOULDBLOCK) || (errno == EAGAIN)) &&
358 !GetBlocking())) {
359 // Is this an interrupted or failed non-blocking request?
360 err = AUDIO_SUCCESS;
361 err.sys = AUDIO_COPY_SHORT_INPUT;
362 return (err);
363 }
364 return (RaiseError(AUDIO_UNIXERROR));
365 }
366
367 // End-of-file?
368 if ((cnt == 0) && GetBlocking()) {
369 if (isDevice() || isPipe()) {
370 AUDIO_DEBUG((1,
371 "Zero-length blocking device/pipe read?!\n"));
372 }
373 err = AUDIO_EOF;
374 err.sys = AUDIO_COPY_INPUT_EOF;
375 return (err);
376 }
377 err = AUDIO_SUCCESS;
378 if (cnt == 0) {
379 err.sys = AUDIO_COPY_SHORT_INPUT;
380 }
381
382 // Return the updated byte count and position
383 len = (size_t)cnt;
384 if (GetHeader().Bytes_to_Bytes(cnt) != len) {
385 AUDIO_DEBUG((1,
386 "Read returned a partial sample frame?!\n"));
387 }
388 pos = GetHeader().Bytes_to_Time(offset + len);
389
390 // Check to see if the endian is right.
391 coerceEndian((unsigned char *)buf, len, localByteOrder());
392
393 return (err);
394 }
395
396 // Write data to underlying file from specified buffer.
397 // No data format translation takes place.
398 // The object's write position is not updated (subclasses can change this)
399 AudioError AudioUnixfile::
WriteData(void * buf,size_t & len,Double & pos)400 WriteData(
401 void* buf, // source buffer address
402 size_t& len, // buffer length (updated)
403 Double& pos) // start position (updated)
404 {
405 off_t offset;
406 off_t cnt;
407 AudioError err;
408
409 // Save buffer size and zero transfer count
410 cnt = (off_t)len;
411 len = 0;
412
413 // Cannot write if file is not open
414 if (!opened() || !mode.Writeable())
415 return (RaiseError(AUDIO_ERR_NOEFFECT));
416
417 // Position must be valid
418 if (Undefined(pos) || (pos < 0.) || (cnt < 0))
419 return (RaiseError(AUDIO_ERR_BADARG));
420
421 // Zero-length writes are easy
422 if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
423 err = AUDIO_SUCCESS;
424 err.sys = AUDIO_COPY_ZERO_LIMIT;
425 return (err);
426 }
427
428 // Position the file pointer to the right place
429 err = seekwrite(pos, offset);
430 if (err != AUDIO_SUCCESS)
431 return (err);
432
433 // Make sure data is in target's endian format before writing.
434 // This conversion is done inplace so we need to change back.
435 // We assume that the data in buf is in localByteOrder.
436 // Only files should have order issues.
437 if (localByteOrder() != GetHeader().endian)
438 coerceEndian((unsigned char *)buf, (size_t)cnt, SWITCH_ENDIAN);
439
440 // Write as much data as possible
441 err = AUDIO_SUCCESS;
442 cnt = write(fd, (char *)buf, (int)cnt);
443 if (cnt < 0) {
444 if (errno == EFBIG) {
445 perror("write");
446 exit(1);
447 } else if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
448 // Is this a failed non-blocking request?
449 err.sys = AUDIO_COPY_SHORT_OUTPUT;
450 return (err);
451 }
452 return (RaiseError(AUDIO_UNIXERROR));
453 }
454 if (cnt == 0)
455 err.sys = AUDIO_COPY_SHORT_OUTPUT;
456
457 // Switch the endian back if local order doesn't match target order.
458 if (localByteOrder() != GetHeader().endian)
459 coerceEndian((unsigned char *)buf, (size_t)cnt, SWITCH_ENDIAN);
460
461 // Return the updated byte count and position
462 len = (size_t)cnt;
463 pos = GetHeader().Bytes_to_Time(offset + len);
464
465 // If the current position is beyond old EOF, update the size
466 if (!Undefined(GetLength()) && (pos > GetLength())) {
467 setlength(pos);
468 }
469
470 return (AUDIO_SUCCESS);
471 }
472
473 // Seek in input stream
474 // Ordinary streams (ie, pipes and devices) cannot be rewound.
475 // A forward seek in them consumes data by reading it.
476 //
477 // This method should be specialized by subclasses that can actually seek,
478 // like regular files for instance.
479 //
480 AudioError AudioUnixfile::
seekread(Double pos,off_t & offset)481 seekread(
482 Double pos, // position to seek to
483 off_t& offset) // returned byte offset
484 {
485 char *bufp; // temporary input buffer
486 size_t bufl; // input buffer size
487 size_t cnt; // input byte count
488 long icnt; // read size
489 Boolean saveblock; // saved state of the blocking i/o flag
490 Double buflen;
491 AudioError err;
492
493 offset = GetHeader().Time_to_Bytes(pos);
494 pos -= ReadPosition();
495
496 // If the seek is backwards, do nothing
497 if (pos < 0.)
498 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
499
500 // If the seek is to the current position, then do nothing.
501 icnt = GetHeader().Time_to_Bytes(pos);
502 if (icnt == 0)
503 return (AUDIO_SUCCESS);
504
505 // The seek is determinate and forward.
506 // We'll have to consume data to get there.
507 // First allocate a buffer to stuff the data into.
508 // Then set the stream for blocking i/o (saving the old state).
509 buflen = max(pos, 1.);
510 bufl = (size_t)GetHeader().Time_to_Bytes(buflen);
511 bufp = new char[bufl];
512 if (bufp == 0) { // allocation error, try a smaller buf
513 bufl = (size_t)sysconf(_SC_PAGESIZE);
514 bufp = new char[bufl];
515 if (bufp == 0)
516 return (RaiseError(AUDIO_UNIXERROR));
517 }
518 // XXX - May have to realign to partial frame count!
519
520 saveblock = GetBlocking();
521 if (!saveblock)
522 SetBlocking(TRUE);
523
524 // Loop until the seek is satisfied (or an error occurs).
525 do {
526 // Limit the read to keep from going too far
527 cnt = (icnt >= (long)bufl) ? bufl : (size_t)icnt;
528 err = Read(bufp, cnt);
529 if (err != AUDIO_SUCCESS)
530 break;
531 icnt -= (long)cnt;
532 } while (icnt > 0);
533
534 SetBlocking(saveblock); // Restore the saved blocking i/o state
535 delete[] bufp; // Free the temporary buffer
536 return (RaiseError(err));
537 }
538
539 // Seek in output stream
540 // Ordinary streams (ie, pipes and devices) cannot be rewound.
541 // A forward seek in them writes NULL data.
542 //
543 // This method should be specialized by subclasses that can actually seek,
544 // like regular files for instance.
545 //
546 AudioError AudioUnixfile::
seekwrite(Double pos,off_t & offset)547 seekwrite(
548 Double pos, // position to seek to
549 off_t& offset) // returned byte offset
550 {
551 char *bufp; // temporary output buffer
552 size_t bufl; // output buffer size
553 size_t cnt; // output byte count
554 long ocnt; // write size
555 Boolean saveblock; // saved state of the blocking i/o flag
556 Double buflen;
557 AudioError err;
558
559 offset = GetHeader().Time_to_Bytes(pos);
560 pos -= WritePosition();
561
562 // If the seek is backwards, do nothing
563 if (pos < 0.)
564 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
565
566 // If the seek is to the current position, then do nothing.
567 ocnt = GetHeader().Time_to_Bytes(pos);
568 if (ocnt == 0)
569 return (AUDIO_SUCCESS);
570
571 // The seek is determinate and forward.
572 // We'll have to produce NULL data to get there.
573 // XXX - not implemented correctly yet
574 buflen = max(pos, 1.);
575 bufl = (size_t)GetHeader().Time_to_Bytes(buflen);
576 bufp = new char[bufl];
577 if (bufp == 0) { // allocation error, try a smaller buf
578 bufl = (size_t)sysconf(_SC_PAGESIZE);
579 bufp = new char[bufl];
580 if (bufp == 0)
581 return (RaiseError(AUDIO_UNIXERROR));
582 }
583
584 // XXX - May have to realign to partial frame count!
585 saveblock = GetBlocking();
586 if (!saveblock)
587 SetBlocking(TRUE);
588
589 // Loop until the seek is satisfied (or an error occurs).
590 do {
591 // Limit the write to keep from going too far
592 cnt = (ocnt >= (long)bufl) ? bufl : (size_t)ocnt;
593 err = Write(bufp, cnt);
594 if (err != AUDIO_SUCCESS)
595 break;
596 ocnt -= (long)cnt;
597 } while (ocnt > 0);
598
599 SetBlocking(saveblock); // Restore the saved blocking i/o state
600 delete[] bufp; // Free the temporary buffer
601 return (RaiseError(err));
602 }
603