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 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include <sys/types.h>
40 #include <AudioFile.h>
42 #include <AudioDebug.h>
44 #include <audio_hdr.h>
46 // XX64 This should go away when <sys/mman.h> gets fixed.
47 extern "C" int madvise(caddr_t
, size_t, int);
49 // class AudioFile methods
52 // Initialize temporary file params
54 #define TMPFILE "/audiotoolXXXXXX"
55 static char *tmpdir
= NULL
;
56 static const char *tmpname
= "(temporary file)";
57 static const FileAccess tmpmode
= ReadWrite
;
58 static const VMAccess defaccess
= SequentialAccess
;
60 // Initialize default access mode, used when a filename is supplied
61 const FileAccess
AudioFile::defmode
= ReadOnly
;
63 // Default audio file path prefix environment variable
64 const char *AudioFile::AUDIO_PATH
= "AUDIOPATH";
67 // Constructor with no arguments opens a read/write temporary file
70 AudioUnixfile(tmpname
, tmpmode
),
71 hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0),
76 // Constructor with pathname and optional mode arg
79 const char *path
, // filename
80 const FileAccess acc
): // access mode
81 AudioUnixfile(path
, acc
),
82 hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0),
87 // Destructor must call the local Close() routine
91 // If the file was open, close it
96 // Set a default temporary file directory
97 AudioError
AudioFile::
103 // Verify intended path
104 if ((stat(path
, &st
) < 0) ||
105 !S_ISDIR(st
.st_mode
) ||
106 (access(path
, W_OK
) < 0)) {
108 return (AUDIO_UNIXERROR
);
113 tmpdir
= (char *)malloc(strlen(path
) + 1);
114 (void) strcpy(tmpdir
, path
);
115 return (AUDIO_SUCCESS
);
119 // Create a named file according to the current mode setting
120 AudioError
AudioFile::
122 const char *path
) // pathname or 0
130 // Convert the open mode to an int argument for open()
131 openmode
= GetAccess();
133 // Was the header properly set?
135 return (RaiseError(AUDIO_ERR_BADHDR
));
137 // Can't create if already opened or if mode or name not set
138 if ((openmode
== -1) || opened() || (strlen(path
) == 0))
139 return (RaiseError(AUDIO_ERR_NOEFFECT
));
141 // If temporary file, create and unlink it.
142 if (strcmp(path
, tmpname
) == 0) {
143 // Construct the temporary file path
144 tmpstr
= (char *)malloc(1 + strlen(TMPFILE
) +
145 strlen((tmpdir
== NULL
) ? TMPDIR
: tmpdir
));
146 (void) sprintf(tmpstr
, "%s%s",
147 (tmpdir
== NULL
) ? TMPDIR
: tmpdir
, TMPFILE
);
148 tmpf
= mktemp(tmpstr
);
150 // Open the temp file and unlink it
151 err
= createfile(tmpf
);
152 if ((err
== AUDIO_SUCCESS
) && (unlink(tmpf
) < 0)) {
154 err
= RaiseError(AUDIO_UNIXERROR
, Warning
);
161 desc
= open(path
, openmode
| O_CREAT
| O_TRUNC
, 0666);
162 if ((desc
< 0) && (errno
== EOVERFLOW
)) {
163 return (RaiseError(AUDIO_UNIXERROR
, Fatal
,
164 (char *)"Large File"));
165 } else if (desc
< 0) {
166 return (RaiseError(AUDIO_UNIXERROR
));
169 // Set the file descriptor (this marks the file open)
172 // Write the file header with current (usually unknown) size
173 err
= encode_filehdr();
174 if (err
!= AUDIO_SUCCESS
) {
176 (void) close(desc
); // If error, remove file
181 // Save the length that got written, then set it to zero
182 origlen
= GetLength();
185 // Set the size of the file header
186 hdrsize
= lseek(desc
, (off_t
)0, SEEK_CUR
);
189 (void) close(desc
); // If error, remove file
195 return (AUDIO_SUCCESS
);
198 // Create a file whose name is already set, according to the mode setting
199 AudioError
AudioFile::
202 return (createfile(GetName()));
205 // Open a file whose name is set
206 AudioError
AudioFile::
209 return (OpenPath(NULL
));
212 // Open a file, using the specified path prefixes
213 AudioError
AudioFile::
226 // Convert the open mode to an int argument for open()
227 openmode
= GetAccess();
228 filename
= GetName();
229 flen
= strlen(filename
);
231 // Can't open if already opened or if mode or name not set
232 if ((openmode
== -1) || opened() || (strlen(filename
) == 0))
233 return (RaiseError(AUDIO_ERR_NOEFFECT
));
236 // 1) try name: if not found and not readonly:
237 // if Append mode, try creating it
238 // 2) if name is a relative pathname, and 'path' is not NULL:
239 // try every path prefix in 'path'
241 err
= tryopen(filename
, openmode
);
243 return (AUDIO_SUCCESS
);
244 if (GetAccess().Writeable() || (filename
[0] == '/')) {
245 // If file is non-existent and Append mode, try creating it.
246 if ((err
== AUDIO_UNIXERROR
) && (err
.sys
== ENOENT
) &&
247 GetAccess().Append() && hdrset()) {
250 return (RaiseError(err
));
253 // Try path as an environment variable name, else assume it is a path
254 str
= (path
== NULL
) ? NULL
: getenv(path
);
259 // Make a copy of the path, to parse it
260 wrk
= new char[strlen(str
) + 1];
261 (void) strcpy(wrk
, str
);
264 // Try each component as a path prefix
266 (prefix
!= NULL
) && (prefix
[0] != '\0');
268 str
= strchr(str
, ':');
271 pathname
= new char[strlen(prefix
) + flen
+ 2];
272 (void) sprintf(pathname
, "%s/%s", prefix
, filename
);
273 err
= tryopen(pathname
, openmode
);
276 case AUDIO_SUCCESS
: // found the file
278 return (RaiseError(err
));
279 // XXX - if file found but not audio, stop looking??
284 // Can't find file. Return the original error condition.
285 return (RaiseError(tryopen(filename
, openmode
)));
288 // Attempt to open the given audio file
289 AudioError
AudioFile::
291 const char *pathname
,
298 // If the name is changing, set the new one
299 if (pathname
!= GetName())
302 // Does the file exist?
303 if (stat(pathname
, &st
) < 0)
304 return (AUDIO_UNIXERROR
);
306 // If not a regular file, stop right there
307 if (!S_ISREG(st
.st_mode
))
308 return (AUDIO_ERR_BADFILEHDR
);
310 // Open the file and check that it's an audio file
311 desc
= open(GetName(), openmode
);
312 if ((desc
< 0) && (errno
== EOVERFLOW
)) {
313 return (RaiseError(AUDIO_UNIXERROR
, Fatal
,
314 (char *)"Large File"));
315 } else if (desc
< 0) {
316 return (AUDIO_UNIXERROR
);
319 // Set the file descriptor (this marks the file open)
322 err
= decode_filehdr();
323 if (err
!= AUDIO_SUCCESS
) {
329 // Save the length of the data and the size of the file header
330 origlen
= GetLength();
331 hdrsize
= (off_t
)lseek(desc
, (off_t
)0, SEEK_CUR
);
339 // If this is ReadOnly file, mmap() it. Don't worry if mmap() fails.
340 if (!GetAccess().Writeable()) {
344 * Can't mmap LITTLE_ENDIAN as they are converted in
347 if (localByteOrder() == BIG_ENDIAN
) {
348 if ((mapaddr
= (caddr_t
)mmap(0, (int)maplen
, PROT_READ
,
349 MAP_SHARED
, desc
, 0)) != (caddr_t
)-1) {
350 // set default access method
351 (void) madvise(mapaddr
, (unsigned int)maplen
,
352 (int)GetAccessType());
354 (void) RaiseError(AUDIO_UNIXERROR
, Warning
,
355 (char *)"Could not mmap() file");
364 return (AUDIO_SUCCESS
);
367 // set VM access hint for mmapped files
368 AudioError
AudioFile::
369 SetAccessType(VMAccess vmacc
)
372 return (AUDIO_ERR_NOEFFECT
);
376 return (AUDIO_ERR_NOEFFECT
);
379 (void) madvise(mapaddr
, (unsigned int)maplen
, (int)vmacc
);
382 return (AUDIO_SUCCESS
);
386 AudioError
AudioFile::
392 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
394 // Rewind the file and rewrite the header with the correct length
395 if (GetAccess().Writeable() && (origlen
!= GetLength())) {
398 if (GetHeader().Time_to_Bytes(GetLength()) !=
399 (lseek(getfd(), (off_t
)0, SEEK_END
) - hdrsize
)) {
401 "AudioFile:Close()...inconsistent length\n"),
405 // XXX - should be rewritten in C++
406 err
= (AudioError
) audio_rewrite_filesize(getfd(), FILE_AU
,
407 (uint_t
)GetHeader().Time_to_Bytes(GetLength()), 0, 0);
410 // Call the generic file close routine
411 err
= AudioUnixfile::Close();
414 munmap(mapaddr
, (int)maplen
);
419 // Init important values, in case the file is reopened
422 return (RaiseError(err
));
425 // Read data from underlying file into specified buffer.
426 // No data format translation takes place.
427 // The object's read position pointer is unaffected.
428 AudioError
AudioFile::
430 void* buf
, // destination buffer address
431 size_t& len
, // buffer length (updated)
432 Double
& pos
) // start position (updated)
439 // If the file is not mapped, call parent ReadData() and return
441 // Call the real routine
442 err
= AudioUnixfile::ReadData(buf
, len
, pos
);
443 // Update the cached seek pointer
448 // If the file is mmapped, do a memcpy() from the mapaddr
450 // Save buffer size and zero transfer count
454 // Cannot read if file is not open
455 if (!opened() || !GetAccess().Readable())
456 return (RaiseError(AUDIO_ERR_NOEFFECT
));
458 // Position must be valid
459 if (Undefined(pos
) || (pos
< 0.) || ((int)cnt
< 0))
460 return (RaiseError(AUDIO_ERR_BADARG
));
462 // Make sure we don't read off the end of file
463 offset
= GetHeader().Time_to_Bytes(pos
);
465 if ((offset
+ hdrsize
) >= maplen
) {
466 // trying to read past EOF
468 err
.sys
= AUDIO_COPY_INPUT_EOF
;
470 } else if ((offset
+ hdrsize
+ cnt
) > maplen
) {
471 // re-adjust cnt so it reads up to the end of file
472 cnt
= (size_t)(maplen
- (offset
+ hdrsize
));
475 // Zero-length reads are finished
476 if (GetHeader().Bytes_to_Bytes(cnt
) == 0) {
478 err
.sys
= AUDIO_COPY_ZERO_LIMIT
;
481 cp
= mapaddr
+ offset
+ hdrsize
;
482 memcpy((void*)buf
, (void*)cp
, cnt
);
485 // Return the updated byte count and position
487 pos
= GetHeader().Bytes_to_Time(offset
+ len
);
489 // Check to see if the endian is right. Note that special care
490 // doesn't need to be taken because of the mmap, since the data
491 // is copied into a separate buffer anyway.
492 coerceEndian((unsigned char *)buf
, len
, localByteOrder());
494 return (AUDIO_SUCCESS
);
497 // Write data to underlying file from specified buffer.
498 // No data format translation takes place.
499 // The object's write position pointer is unaffected.
500 AudioError
AudioFile::
502 void* buf
, // source buffer address
503 size_t& len
, // buffer length (updated)
504 Double
& pos
) // start position (updated)
508 // Call the real routine
509 err
= AudioUnixfile::WriteData(buf
, len
, pos
);
511 // Update the cached seek pointer
516 // Set the Unix file pointer to match a given file position.
517 AudioError
AudioFile::
519 Double pos
, // position to seek to
520 off_t
& offset
) // returned byte offset
522 offset
= GetHeader().Time_to_Bytes(pos
);
523 if (offset
!= seekpos
) {
524 if (lseek(getfd(), (off_t
)(hdrsize
+ offset
), SEEK_SET
) < 0)
525 return (RaiseError(AUDIO_UNIXERROR
, Warning
));
528 return (AUDIO_SUCCESS
);
531 // Set the Unix file pointer to match a given file position.
532 // If seek beyond end-of-file, NULL out intervening data.
533 AudioError
AudioFile::
535 Double pos
, // position to seek to
536 off_t
& offset
) // returned byte offset
538 // If append-only, can't seek backwards into file
539 if (GetAccess().Append() && (pos
< GetLength()))
540 return (RaiseError(AUDIO_ERR_NOEFFECT
, Warning
));
542 // If seek beyond eof, fill data
543 if (pos
> GetLength()) {
544 seekwrite(GetLength(), offset
); // seek to eof
546 // XXX - not implemented yet
548 return (AUDIO_SUCCESS
);
551 offset
= GetHeader().Time_to_Bytes(pos
);
552 if (offset
!= seekpos
) {
553 if (lseek(getfd(), (off_t
)(hdrsize
+ offset
), SEEK_SET
) < 0)
554 return (RaiseError(AUDIO_UNIXERROR
, Warning
));
557 return (AUDIO_SUCCESS
);
560 // Copy routine that handles mapped files
561 AudioError
AudioFile::
563 Audio
* to
, // audio object to copy to
577 // If this is NOT mmapped, or the destination is an AudioBuffer,
578 // use the default routine
579 if ((mapaddr
== 0) || to
->isBuffer()) {
580 return (Audio::AsyncCopy(to
, frompos
, topos
, limit
));
583 tohdr
= to
->GetHeader();
584 if (err
= tohdr
.Validate())
587 return (RaiseError(AUDIO_ERR_BADARG
));
588 svlim
= (size_t)tohdr
.Time_to_Bytes(limit
);
590 // Get maximum possible copy length
591 svfrom
= GetLength();
592 if ((frompos
>= svfrom
) || ((cnt
= (size_t)
593 GetHeader().Time_to_Bytes(svfrom
- frompos
)) == 0)) {
596 err
.sys
= AUDIO_COPY_INPUT_EOF
;
599 if (!Undefined(limit
) && (svlim
< cnt
))
604 offset
= (size_t)GetHeader().Time_to_Bytes(frompos
);
605 if ((offset
+ hdrsize
) >= maplen
) {
606 // trying to read past EOF
608 err
.sys
= AUDIO_COPY_INPUT_EOF
;
610 } else if ((offset
+ hdrsize
+ cnt
) > maplen
) {
611 // re-adjust cnt so it reads up to the end of file
612 cnt
= (size_t)(maplen
- (offset
+ hdrsize
));
615 // Zero-length reads are done
616 if (GetHeader().Bytes_to_Bytes(cnt
) == 0) {
618 err
.sys
= AUDIO_COPY_ZERO_LIMIT
;
622 // Write the data to the destination and update pointers/ctrs
626 bptr
= mapaddr
+ hdrsize
+ offset
;
627 err
= to
->WriteData(bptr
, cnt
, topos
);
628 limit
= topos
- svto
;
629 frompos
= svfrom
+ limit
;
631 // Report short writes
632 if (!err
&& (cnt
< svlim
))
633 err
.sys
= AUDIO_COPY_SHORT_OUTPUT
;