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
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.
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]
23 * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
38 #include <sys/types.h>
41 #include <sys/t_lock.h>
43 #include <AudioDebug.h>
44 #include <AudioUnixfile.h>
46 #include <audio_hdr.h>
49 // class AudioUnixfile methods
51 // Constructor with pathname and mode arg
54 const char *path
, // pathname
55 const FileAccess acc
): // access mode
56 AudioStream(path
), fd(-1), block(TRUE
), mode(acc
),
57 infostring(new char[1]), infolength(1)
66 // If the file is open, close it
70 // Deallocate the dynamic storage
74 // Generic open with search path routine just calls default Open()
75 AudioError
AudioUnixfile::
82 // Decode an audio file header
83 // This routine reads the audio file header and decodes it.
85 // This method should be specialized by subclasses that are not files,
86 // like devices for instance.
88 // XXX - this routine should be rewritten for C++
89 AudioError
AudioUnixfile::
92 Boolean saveblock
; // saved state of the blocking i/o flag
93 AudioHdr hdr_local
; // local copy of header
94 Audio_hdr ohdr
; // XXX - old libaudio hdr
103 // If fd is not open, or file header already decoded, skip it
104 if (!isfdset() || opened())
105 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
107 // Stat the file, to see if it is a regular file
108 if (fstat(getfd(), &st
) < 0)
109 return (RaiseError(AUDIO_UNIXERROR
));
111 // Make sure the file is not set for blocking i/o
112 saveblock
= GetBlocking();
116 // Read the file header, but not the info field
117 // XXX - Should use C++ input method
118 cnt
= read(getfd(), (char *)&fhdr
, sizeof (fhdr
));
119 if (cnt
!= sizeof (fhdr
)) {
120 return (RaiseError(AUDIO_UNIXERROR
));
123 // Check the validity of the header and get the size of the info field
124 err
= (AudioError
) audio_decode_filehdr(getfd(), (unsigned char *)&fhdr
,
125 &file_type
, &ohdr
, &infosize
);
126 if (err
!= AUDIO_SUCCESS
)
127 return (RaiseError(err
));
129 // Allocate and read in the info field
130 ibuf
= new char[infosize
];
131 cnt
= read(getfd(), ibuf
, infosize
);
132 if (cnt
!= infosize
) {
134 return (RaiseError(AUDIO_UNIXERROR
));
136 SetBlocking(saveblock
); // Restore the saved blocking i/o state
138 // XXX - convert from libaudio header
139 hdr_local
= GetHeader();
140 hdr_local
.sample_rate
= ohdr
.sample_rate
;
141 hdr_local
.samples_per_unit
= ohdr
.samples_per_unit
;
142 hdr_local
.bytes_per_unit
= ohdr
.bytes_per_unit
;
143 hdr_local
.channels
= ohdr
.channels
;
144 hdr_local
.encoding
= (AudioEncoding
) ohdr
.encoding
;
145 hdr_local
.endian
= BIG_ENDIAN
; // Files are always written in
148 err
= SetHeader(hdr_local
);
149 if (err
!= AUDIO_SUCCESS
) {
151 return (RaiseError(err
));
153 SetInfostring(ibuf
, infosize
);
156 // Only trust the file size for regular files
157 if (S_ISREG(st
.st_mode
)) {
158 setlength(GetHeader().Bytes_to_Time(
159 st
.st_size
- infosize
- sizeof (au_filehdr_t
)));
162 if ((ohdr
.data_size
!= AUDIO_UNKNOWN_SIZE
) &&
163 (GetLength() != GetHeader().Bytes_to_Time(ohdr
.data_size
)))
165 "AudioUnixfile: header/file size mismatch"));
167 // always consider it to be unknown if not reading a real file
168 // since there's no real way to verify if the header is
171 setlength(AUDIO_UNKNOWN_TIME
);
174 // set flag for opened() test
177 return (AUDIO_SUCCESS
);
180 // Write an audio file header
181 // This routine encodes the audio file header and writes it out.
182 // XXX - It assumes that the file pointer is set to the start of the file.
184 // This method should be specialized by subclasses that are not files,
185 // like devices for instance.
187 // XXX - this routine should be rewritten for C++
188 AudioError
AudioUnixfile::
191 Boolean saveblock
; // saved state of the blocking i/o flag
192 AudioHdr hdr_local
; // local copy of header
193 Audio_hdr ohdr
; // XXX - old libaudio hdr
196 // If fd is not open, or file header already written, skip it
197 if (!isfdset() || opened())
198 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
200 // XXX - Set up the libaudio hdr
201 hdr_local
= GetHeader();
202 hdr_local
.endian
= BIG_ENDIAN
; // Files are always written big endian.
203 err
= SetHeader(hdr_local
);
204 if (err
!= AUDIO_SUCCESS
) {
205 return (RaiseError(err
));
208 ohdr
.sample_rate
= hdr_local
.sample_rate
;
209 ohdr
.samples_per_unit
= hdr_local
.samples_per_unit
;
210 ohdr
.bytes_per_unit
= hdr_local
.bytes_per_unit
;
211 ohdr
.channels
= hdr_local
.channels
;
212 ohdr
.encoding
= hdr_local
.encoding
;
213 if (Undefined(GetLength()))
214 ohdr
.data_size
= AUDIO_UNKNOWN_SIZE
;
216 ohdr
.data_size
= (uint_t
)GetHeader().Time_to_Bytes(GetLength());
218 /* Make sure the file is not set for blocking i/o */
219 saveblock
= GetBlocking();
223 // XXX - Should use C++ output method
224 err
= (AudioError
) audio_write_filehdr(getfd(), &ohdr
, FILE_AU
,
225 infostring
, infolength
);
227 // set flag for opened() test
228 if (err
== AUDIO_SUCCESS
)
231 SetBlocking(saveblock
); // Restore the saved blocking i/o state
232 return (RaiseError(err
));
235 // Set a file blocking/non-blocking
236 // This method should be subclassed by objects that always block (eg, files)
239 Boolean b
) // FALSE to set non-blocking
243 // If the file is open, set blocking/non-blocking now
245 flag
= fcntl(getfd(), F_GETFL
, 0);
246 if ((flag
< 0) && (errno
== EOVERFLOW
|| errno
== EINVAL
)) {
247 RaiseError(AUDIO_UNIXERROR
, Fatal
,
248 (char *)"Large File");
250 flag
&= ~(O_NDELAY
| O_NONBLOCK
); // set blocking
252 flag
|= O_NONBLOCK
; // set non-blocking
254 if (fcntl(getfd(), F_SETFL
, flag
) < 0) {
255 RaiseError(AUDIO_UNIXERROR
, Warning
);
258 // Set the blocking flag (this may affect the Open() behavior)
262 // Return a pointer to the info string
263 // XXX - returns a pointer to the string stored in the object
264 // XXX - assumes ASCII data
265 char *const AudioUnixfile::
267 int& len
) const // returned length of string
273 // Set the info string
276 const char *str
, // new info string
277 int len
) // length of string
279 // If length defaulted, assume an ASCII string
281 len
= strlen(str
) + 1;
283 infostring
= new char[len
];
285 (void) memcpy(infostring
, str
, len
);
289 AudioError
AudioUnixfile::
292 // If the file is open, close it
294 if (close(getfd()) < 0)
295 return (RaiseError(AUDIO_UNIXERROR
));
297 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
300 // Init important values, in case the file is reopened
303 (void) SetReadPosition((Double
)0., Absolute
);
304 (void) SetWritePosition((Double
)0., Absolute
);
305 return (AUDIO_SUCCESS
);
308 // Read data from underlying file into specified buffer.
309 // No data format translation takes place.
310 // The object's read position is not updated (subclasses can change this)
311 AudioError
AudioUnixfile::
313 void* buf
, // destination buffer address
314 size_t& len
, // buffer length (updated)
315 Double
& pos
) // start position (updated)
321 // Save buffer size and zero transfer count
325 // Cannot read if file is not open
326 if (!opened() || !mode
.Readable())
327 return (RaiseError(AUDIO_ERR_NOEFFECT
));
329 // Position must be valid
330 if (Undefined(pos
) || (pos
< 0.) || (cnt
< 0))
331 return (RaiseError(AUDIO_ERR_BADARG
));
333 // Position the file pointer to the right place
334 err
= seekread(pos
, offset
);
335 if (err
!= AUDIO_SUCCESS
)
339 if (pos
>= GetLength()) {
341 err
.sys
= AUDIO_COPY_INPUT_EOF
;
345 // Zero-length reads are finished
346 if (GetHeader().Bytes_to_Bytes(cnt
) == 0) {
348 err
.sys
= AUDIO_COPY_ZERO_LIMIT
;
352 // Read as much data as possible
353 cnt
= read(fd
, (char *)buf
, (int)cnt
);
355 if (errno
== EOVERFLOW
) {
358 } else if ((errno
== EINTR
) ||
359 (((errno
== EWOULDBLOCK
) || (errno
== EAGAIN
)) &&
361 // Is this an interrupted or failed non-blocking request?
363 err
.sys
= AUDIO_COPY_SHORT_INPUT
;
366 return (RaiseError(AUDIO_UNIXERROR
));
370 if ((cnt
== 0) && GetBlocking()) {
371 if (isDevice() || isPipe()) {
373 "Zero-length blocking device/pipe read?!\n"));
376 err
.sys
= AUDIO_COPY_INPUT_EOF
;
381 err
.sys
= AUDIO_COPY_SHORT_INPUT
;
384 // Return the updated byte count and position
386 if (GetHeader().Bytes_to_Bytes(cnt
) != len
) {
388 "Read returned a partial sample frame?!\n"));
390 pos
= GetHeader().Bytes_to_Time(offset
+ len
);
392 // Check to see if the endian is right.
393 coerceEndian((unsigned char *)buf
, len
, localByteOrder());
398 // Write data to underlying file from specified buffer.
399 // No data format translation takes place.
400 // The object's write position is not updated (subclasses can change this)
401 AudioError
AudioUnixfile::
403 void* buf
, // source buffer address
404 size_t& len
, // buffer length (updated)
405 Double
& pos
) // start position (updated)
411 // Save buffer size and zero transfer count
415 // Cannot write if file is not open
416 if (!opened() || !mode
.Writeable())
417 return (RaiseError(AUDIO_ERR_NOEFFECT
));
419 // Position must be valid
420 if (Undefined(pos
) || (pos
< 0.) || (cnt
< 0))
421 return (RaiseError(AUDIO_ERR_BADARG
));
423 // Zero-length writes are easy
424 if (GetHeader().Bytes_to_Bytes(cnt
) == 0) {
426 err
.sys
= AUDIO_COPY_ZERO_LIMIT
;
430 // Position the file pointer to the right place
431 err
= seekwrite(pos
, offset
);
432 if (err
!= AUDIO_SUCCESS
)
435 // Make sure data is in target's endian format before writing.
436 // This conversion is done inplace so we need to change back.
437 // We assume that the data in buf is in localByteOrder.
438 // Only files should have order issues.
439 if (localByteOrder() != GetHeader().endian
)
440 coerceEndian((unsigned char *)buf
, (size_t)cnt
, SWITCH_ENDIAN
);
442 // Write as much data as possible
444 cnt
= write(fd
, (char *)buf
, (int)cnt
);
446 if (errno
== EFBIG
) {
449 } else if ((errno
== EWOULDBLOCK
) || (errno
== EAGAIN
)) {
450 // Is this a failed non-blocking request?
451 err
.sys
= AUDIO_COPY_SHORT_OUTPUT
;
454 return (RaiseError(AUDIO_UNIXERROR
));
457 err
.sys
= AUDIO_COPY_SHORT_OUTPUT
;
459 // Switch the endian back if local order doesn't match target order.
460 if (localByteOrder() != GetHeader().endian
)
461 coerceEndian((unsigned char *)buf
, (size_t)cnt
, SWITCH_ENDIAN
);
463 // Return the updated byte count and position
465 pos
= GetHeader().Bytes_to_Time(offset
+ len
);
467 // If the current position is beyond old EOF, update the size
468 if (!Undefined(GetLength()) && (pos
> GetLength())) {
472 return (AUDIO_SUCCESS
);
475 // Seek in input stream
476 // Ordinary streams (ie, pipes and devices) cannot be rewound.
477 // A forward seek in them consumes data by reading it.
479 // This method should be specialized by subclasses that can actually seek,
480 // like regular files for instance.
482 AudioError
AudioUnixfile::
484 Double pos
, // position to seek to
485 off_t
& offset
) // returned byte offset
487 char *bufp
; // temporary input buffer
488 size_t bufl
; // input buffer size
489 size_t cnt
; // input byte count
490 long icnt
; // read size
491 Boolean saveblock
; // saved state of the blocking i/o flag
495 offset
= GetHeader().Time_to_Bytes(pos
);
496 pos
-= ReadPosition();
498 // If the seek is backwards, do nothing
500 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
502 // If the seek is to the current position, then do nothing.
503 icnt
= GetHeader().Time_to_Bytes(pos
);
505 return (AUDIO_SUCCESS
);
507 // The seek is determinate and forward.
508 // We'll have to consume data to get there.
509 // First allocate a buffer to stuff the data into.
510 // Then set the stream for blocking i/o (saving the old state).
511 buflen
= max(pos
, 1.);
512 bufl
= (size_t)GetHeader().Time_to_Bytes(buflen
);
513 bufp
= new char[bufl
];
514 if (bufp
== 0) { // allocation error, try a smaller buf
515 bufl
= (size_t)sysconf(_SC_PAGESIZE
);
516 bufp
= new char[bufl
];
518 return (RaiseError(AUDIO_UNIXERROR
));
520 // XXX - May have to realign to partial frame count!
522 saveblock
= GetBlocking();
526 // Loop until the seek is satisfied (or an error occurs).
528 // Limit the read to keep from going too far
529 cnt
= (icnt
>= (long)bufl
) ? bufl
: (size_t)icnt
;
530 err
= Read(bufp
, cnt
);
531 if (err
!= AUDIO_SUCCESS
)
536 SetBlocking(saveblock
); // Restore the saved blocking i/o state
537 delete bufp
; // Free the temporary buffer
538 return (RaiseError(err
));
541 // Seek in output stream
542 // Ordinary streams (ie, pipes and devices) cannot be rewound.
543 // A forward seek in them writes NULL data.
545 // This method should be specialized by subclasses that can actually seek,
546 // like regular files for instance.
548 AudioError
AudioUnixfile::
550 Double pos
, // position to seek to
551 off_t
& offset
) // returned byte offset
553 char *bufp
; // temporary output buffer
554 size_t bufl
; // output buffer size
555 size_t cnt
; // output byte count
556 long ocnt
; // write size
557 Boolean saveblock
; // saved state of the blocking i/o flag
561 offset
= GetHeader().Time_to_Bytes(pos
);
562 pos
-= WritePosition();
564 // If the seek is backwards, do nothing
566 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
568 // If the seek is to the current position, then do nothing.
569 ocnt
= GetHeader().Time_to_Bytes(pos
);
571 return (AUDIO_SUCCESS
);
573 // The seek is determinate and forward.
574 // We'll have to produce NULL data to get there.
575 // XXX - not implemented correctly yet
576 buflen
= max(pos
, 1.);
577 bufl
= (size_t)GetHeader().Time_to_Bytes(buflen
);
578 bufp
= new char[bufl
];
579 if (bufp
== 0) { // allocation error, try a smaller buf
580 bufl
= (size_t)sysconf(_SC_PAGESIZE
);
581 bufp
= new char[bufl
];
583 return (RaiseError(AUDIO_UNIXERROR
));
586 // XXX - May have to realign to partial frame count!
587 saveblock
= GetBlocking();
591 // Loop until the seek is satisfied (or an error occurs).
593 // Limit the write to keep from going too far
594 cnt
= (ocnt
>= (long)bufl
) ? bufl
: (size_t)ocnt
;
595 err
= Write(bufp
, cnt
);
596 if (err
!= AUDIO_SUCCESS
)
601 SetBlocking(saveblock
); // Restore the saved blocking i/o state
602 delete bufp
; // Free the temporary buffer
603 return (RaiseError(err
));