1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate * CDDL HEADER START
3*0Sstevel@tonic-gate *
4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
7*0Sstevel@tonic-gate * with the License.
8*0Sstevel@tonic-gate *
9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate * and limitations under the License.
13*0Sstevel@tonic-gate *
14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate *
20*0Sstevel@tonic-gate * CDDL HEADER END
21*0Sstevel@tonic-gate */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24*0Sstevel@tonic-gate * Use is subject to license terms.
25*0Sstevel@tonic-gate */
26*0Sstevel@tonic-gate
27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
28*0Sstevel@tonic-gate
29*0Sstevel@tonic-gate #include <stdlib.h>
30*0Sstevel@tonic-gate #include <stdio.h>
31*0Sstevel@tonic-gate #include <unistd.h>
32*0Sstevel@tonic-gate #include <errno.h>
33*0Sstevel@tonic-gate #include <malloc.h>
34*0Sstevel@tonic-gate #include <string.h>
35*0Sstevel@tonic-gate #include <fcntl.h>
36*0Sstevel@tonic-gate #include <sys/stat.h>
37*0Sstevel@tonic-gate #include <sys/types.h>
38*0Sstevel@tonic-gate #include <sys/mman.h>
39*0Sstevel@tonic-gate
40*0Sstevel@tonic-gate #include <AudioFile.h>
41*0Sstevel@tonic-gate #include <AudioLib.h>
42*0Sstevel@tonic-gate #include <AudioDebug.h>
43*0Sstevel@tonic-gate #include <libaudio.h>
44*0Sstevel@tonic-gate #include <audio_hdr.h>
45*0Sstevel@tonic-gate
46*0Sstevel@tonic-gate // XX64 This should go away when <sys/mman.h> gets fixed.
47*0Sstevel@tonic-gate extern "C" int madvise(caddr_t, size_t, int);
48*0Sstevel@tonic-gate
49*0Sstevel@tonic-gate // class AudioFile methods
50*0Sstevel@tonic-gate
51*0Sstevel@tonic-gate
52*0Sstevel@tonic-gate // Initialize temporary file params
53*0Sstevel@tonic-gate #define TMPDIR "/tmp"
54*0Sstevel@tonic-gate #define TMPFILE "/audiotoolXXXXXX"
55*0Sstevel@tonic-gate static char *tmpdir = NULL;
56*0Sstevel@tonic-gate static const char *tmpname = "(temporary file)";
57*0Sstevel@tonic-gate static const FileAccess tmpmode = ReadWrite;
58*0Sstevel@tonic-gate static const VMAccess defaccess = SequentialAccess;
59*0Sstevel@tonic-gate
60*0Sstevel@tonic-gate // Initialize default access mode, used when a filename is supplied
61*0Sstevel@tonic-gate const FileAccess AudioFile::defmode = ReadOnly;
62*0Sstevel@tonic-gate
63*0Sstevel@tonic-gate // Default audio file path prefix environment variable
64*0Sstevel@tonic-gate const char *AudioFile::AUDIO_PATH = "AUDIOPATH";
65*0Sstevel@tonic-gate
66*0Sstevel@tonic-gate
67*0Sstevel@tonic-gate // Constructor with no arguments opens a read/write temporary file
68*0Sstevel@tonic-gate AudioFile::
AudioFile()69*0Sstevel@tonic-gate AudioFile():
70*0Sstevel@tonic-gate AudioUnixfile(tmpname, tmpmode),
71*0Sstevel@tonic-gate hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0),
72*0Sstevel@tonic-gate vmaccess(defaccess)
73*0Sstevel@tonic-gate {
74*0Sstevel@tonic-gate }
75*0Sstevel@tonic-gate
76*0Sstevel@tonic-gate // Constructor with pathname and optional mode arg
77*0Sstevel@tonic-gate AudioFile::
AudioFile(const char * path,const FileAccess acc)78*0Sstevel@tonic-gate AudioFile(
79*0Sstevel@tonic-gate const char *path, // filename
80*0Sstevel@tonic-gate const FileAccess acc): // access mode
81*0Sstevel@tonic-gate AudioUnixfile(path, acc),
82*0Sstevel@tonic-gate hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0),
83*0Sstevel@tonic-gate vmaccess(defaccess)
84*0Sstevel@tonic-gate {
85*0Sstevel@tonic-gate }
86*0Sstevel@tonic-gate
87*0Sstevel@tonic-gate // Destructor must call the local Close() routine
88*0Sstevel@tonic-gate AudioFile::
~AudioFile()89*0Sstevel@tonic-gate ~AudioFile()
90*0Sstevel@tonic-gate {
91*0Sstevel@tonic-gate // If the file was open, close it
92*0Sstevel@tonic-gate if (opened())
93*0Sstevel@tonic-gate (void) Close();
94*0Sstevel@tonic-gate }
95*0Sstevel@tonic-gate
96*0Sstevel@tonic-gate // Set a default temporary file directory
97*0Sstevel@tonic-gate AudioError AudioFile::
SetTempPath(const char * path)98*0Sstevel@tonic-gate SetTempPath(
99*0Sstevel@tonic-gate const char *path)
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate struct stat st;
102*0Sstevel@tonic-gate
103*0Sstevel@tonic-gate // Verify intended path
104*0Sstevel@tonic-gate if ((stat(path, &st) < 0) ||
105*0Sstevel@tonic-gate !S_ISDIR(st.st_mode) ||
106*0Sstevel@tonic-gate (access(path, W_OK) < 0)) {
107*0Sstevel@tonic-gate errno = ENOTDIR;
108*0Sstevel@tonic-gate return (AUDIO_UNIXERROR);
109*0Sstevel@tonic-gate }
110*0Sstevel@tonic-gate
111*0Sstevel@tonic-gate if (tmpdir != NULL)
112*0Sstevel@tonic-gate (void) free(tmpdir);
113*0Sstevel@tonic-gate tmpdir = (char *)malloc(strlen(path) + 1);
114*0Sstevel@tonic-gate (void) strcpy(tmpdir, path);
115*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
116*0Sstevel@tonic-gate }
117*0Sstevel@tonic-gate
118*0Sstevel@tonic-gate
119*0Sstevel@tonic-gate // Create a named file according to the current mode setting
120*0Sstevel@tonic-gate AudioError AudioFile::
createfile(const char * path)121*0Sstevel@tonic-gate createfile(
122*0Sstevel@tonic-gate const char *path) // pathname or 0
123*0Sstevel@tonic-gate {
124*0Sstevel@tonic-gate char *tmpf;
125*0Sstevel@tonic-gate char *tmpstr;
126*0Sstevel@tonic-gate int openmode;
127*0Sstevel@tonic-gate int desc;
128*0Sstevel@tonic-gate AudioError err;
129*0Sstevel@tonic-gate
130*0Sstevel@tonic-gate // Convert the open mode to an int argument for open()
131*0Sstevel@tonic-gate openmode = GetAccess();
132*0Sstevel@tonic-gate
133*0Sstevel@tonic-gate // Was the header properly set?
134*0Sstevel@tonic-gate if (!hdrset())
135*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_BADHDR));
136*0Sstevel@tonic-gate
137*0Sstevel@tonic-gate // Can't create if already opened or if mode or name not set
138*0Sstevel@tonic-gate if ((openmode == -1) || opened() || (strlen(path) == 0))
139*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_NOEFFECT));
140*0Sstevel@tonic-gate
141*0Sstevel@tonic-gate // If temporary file, create and unlink it.
142*0Sstevel@tonic-gate if (strcmp(path, tmpname) == 0) {
143*0Sstevel@tonic-gate // Construct the temporary file path
144*0Sstevel@tonic-gate tmpstr = (char *)malloc(1 + strlen(TMPFILE) +
145*0Sstevel@tonic-gate strlen((tmpdir == NULL) ? TMPDIR : tmpdir));
146*0Sstevel@tonic-gate (void) sprintf(tmpstr, "%s%s",
147*0Sstevel@tonic-gate (tmpdir == NULL) ? TMPDIR : tmpdir, TMPFILE);
148*0Sstevel@tonic-gate tmpf = mktemp(tmpstr);
149*0Sstevel@tonic-gate
150*0Sstevel@tonic-gate // Open the temp file and unlink it
151*0Sstevel@tonic-gate err = createfile(tmpf);
152*0Sstevel@tonic-gate if ((err == AUDIO_SUCCESS) && (unlink(tmpf) < 0)) {
153*0Sstevel@tonic-gate (void) Close();
154*0Sstevel@tonic-gate err = RaiseError(AUDIO_UNIXERROR, Warning);
155*0Sstevel@tonic-gate }
156*0Sstevel@tonic-gate (void) free(tmpstr);
157*0Sstevel@tonic-gate return (err);
158*0Sstevel@tonic-gate }
159*0Sstevel@tonic-gate
160*0Sstevel@tonic-gate // Create the file
161*0Sstevel@tonic-gate desc = open(path, openmode | O_CREAT | O_TRUNC, 0666);
162*0Sstevel@tonic-gate if ((desc < 0) && (errno == EOVERFLOW)) {
163*0Sstevel@tonic-gate return (RaiseError(AUDIO_UNIXERROR, Fatal,
164*0Sstevel@tonic-gate (char *)"Large File"));
165*0Sstevel@tonic-gate } else if (desc < 0) {
166*0Sstevel@tonic-gate return (RaiseError(AUDIO_UNIXERROR));
167*0Sstevel@tonic-gate }
168*0Sstevel@tonic-gate
169*0Sstevel@tonic-gate // Set the file descriptor (this marks the file open)
170*0Sstevel@tonic-gate setfd(desc);
171*0Sstevel@tonic-gate
172*0Sstevel@tonic-gate // Write the file header with current (usually unknown) size
173*0Sstevel@tonic-gate err = encode_filehdr();
174*0Sstevel@tonic-gate if (err != AUDIO_SUCCESS) {
175*0Sstevel@tonic-gate setfd(-1);
176*0Sstevel@tonic-gate (void) close(desc); // If error, remove file
177*0Sstevel@tonic-gate (void) unlink(path);
178*0Sstevel@tonic-gate return (err);
179*0Sstevel@tonic-gate }
180*0Sstevel@tonic-gate
181*0Sstevel@tonic-gate // Save the length that got written, then set it to zero
182*0Sstevel@tonic-gate origlen = GetLength();
183*0Sstevel@tonic-gate setlength(0.);
184*0Sstevel@tonic-gate
185*0Sstevel@tonic-gate // Set the size of the file header
186*0Sstevel@tonic-gate hdrsize = lseek(desc, (off_t)0, SEEK_CUR);
187*0Sstevel@tonic-gate if (hdrsize < 0) {
188*0Sstevel@tonic-gate setfd(-1);
189*0Sstevel@tonic-gate (void) close(desc); // If error, remove file
190*0Sstevel@tonic-gate (void) unlink(path);
191*0Sstevel@tonic-gate return (err);
192*0Sstevel@tonic-gate }
193*0Sstevel@tonic-gate seekpos = 0;
194*0Sstevel@tonic-gate
195*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
196*0Sstevel@tonic-gate }
197*0Sstevel@tonic-gate
198*0Sstevel@tonic-gate // Create a file whose name is already set, according to the mode setting
199*0Sstevel@tonic-gate AudioError AudioFile::
Create()200*0Sstevel@tonic-gate Create()
201*0Sstevel@tonic-gate {
202*0Sstevel@tonic-gate return (createfile(GetName()));
203*0Sstevel@tonic-gate }
204*0Sstevel@tonic-gate
205*0Sstevel@tonic-gate // Open a file whose name is set
206*0Sstevel@tonic-gate AudioError AudioFile::
Open()207*0Sstevel@tonic-gate Open()
208*0Sstevel@tonic-gate {
209*0Sstevel@tonic-gate return (OpenPath(NULL));
210*0Sstevel@tonic-gate }
211*0Sstevel@tonic-gate
212*0Sstevel@tonic-gate // Open a file, using the specified path prefixes
213*0Sstevel@tonic-gate AudioError AudioFile::
OpenPath(const char * path)214*0Sstevel@tonic-gate OpenPath(
215*0Sstevel@tonic-gate const char *path)
216*0Sstevel@tonic-gate {
217*0Sstevel@tonic-gate char *filename;
218*0Sstevel@tonic-gate int flen;
219*0Sstevel@tonic-gate char *prefix;
220*0Sstevel@tonic-gate char *str;
221*0Sstevel@tonic-gate char *wrk;
222*0Sstevel@tonic-gate char *pathname;
223*0Sstevel@tonic-gate int openmode;
224*0Sstevel@tonic-gate AudioError err;
225*0Sstevel@tonic-gate
226*0Sstevel@tonic-gate // Convert the open mode to an int argument for open()
227*0Sstevel@tonic-gate openmode = GetAccess();
228*0Sstevel@tonic-gate filename = GetName();
229*0Sstevel@tonic-gate flen = strlen(filename);
230*0Sstevel@tonic-gate
231*0Sstevel@tonic-gate // Can't open if already opened or if mode or name not set
232*0Sstevel@tonic-gate if ((openmode == -1) || opened() || (strlen(filename) == 0))
233*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_NOEFFECT));
234*0Sstevel@tonic-gate
235*0Sstevel@tonic-gate // Search path:
236*0Sstevel@tonic-gate // 1) try name: if not found and not readonly:
237*0Sstevel@tonic-gate // if Append mode, try creating it
238*0Sstevel@tonic-gate // 2) if name is a relative pathname, and 'path' is not NULL:
239*0Sstevel@tonic-gate // try every path prefix in 'path'
240*0Sstevel@tonic-gate
241*0Sstevel@tonic-gate err = tryopen(filename, openmode);
242*0Sstevel@tonic-gate if (!err)
243*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
244*0Sstevel@tonic-gate if (GetAccess().Writeable() || (filename[0] == '/')) {
245*0Sstevel@tonic-gate // If file is non-existent and Append mode, try creating it.
246*0Sstevel@tonic-gate if ((err == AUDIO_UNIXERROR) && (err.sys == ENOENT) &&
247*0Sstevel@tonic-gate GetAccess().Append() && hdrset()) {
248*0Sstevel@tonic-gate return (Create());
249*0Sstevel@tonic-gate }
250*0Sstevel@tonic-gate return (RaiseError(err));
251*0Sstevel@tonic-gate }
252*0Sstevel@tonic-gate
253*0Sstevel@tonic-gate // Try path as an environment variable name, else assume it is a path
254*0Sstevel@tonic-gate str = (path == NULL) ? NULL : getenv(path);
255*0Sstevel@tonic-gate if (str == NULL)
256*0Sstevel@tonic-gate str = (char *)path;
257*0Sstevel@tonic-gate
258*0Sstevel@tonic-gate if (str != NULL) {
259*0Sstevel@tonic-gate // Make a copy of the path, to parse it
260*0Sstevel@tonic-gate wrk = new char[strlen(str) + 1];
261*0Sstevel@tonic-gate (void) strcpy(wrk, str);
262*0Sstevel@tonic-gate str = wrk;
263*0Sstevel@tonic-gate
264*0Sstevel@tonic-gate // Try each component as a path prefix
265*0Sstevel@tonic-gate for (prefix = str;
266*0Sstevel@tonic-gate (prefix != NULL) && (prefix[0] != '\0');
267*0Sstevel@tonic-gate prefix = str) {
268*0Sstevel@tonic-gate str = strchr(str, ':');
269*0Sstevel@tonic-gate if (str != NULL)
270*0Sstevel@tonic-gate *str++ = '\0';
271*0Sstevel@tonic-gate pathname = new char[strlen(prefix) + flen + 2];
272*0Sstevel@tonic-gate (void) sprintf(pathname, "%s/%s", prefix, filename);
273*0Sstevel@tonic-gate err = tryopen(pathname, openmode);
274*0Sstevel@tonic-gate delete pathname;
275*0Sstevel@tonic-gate switch (err) {
276*0Sstevel@tonic-gate case AUDIO_SUCCESS: // found the file
277*0Sstevel@tonic-gate delete wrk;
278*0Sstevel@tonic-gate return (RaiseError(err));
279*0Sstevel@tonic-gate // XXX - if file found but not audio, stop looking??
280*0Sstevel@tonic-gate }
281*0Sstevel@tonic-gate }
282*0Sstevel@tonic-gate delete wrk;
283*0Sstevel@tonic-gate }
284*0Sstevel@tonic-gate // Can't find file. Return the original error condition.
285*0Sstevel@tonic-gate return (RaiseError(tryopen(filename, openmode)));
286*0Sstevel@tonic-gate }
287*0Sstevel@tonic-gate
288*0Sstevel@tonic-gate // Attempt to open the given audio file
289*0Sstevel@tonic-gate AudioError AudioFile::
tryopen(const char * pathname,int openmode)290*0Sstevel@tonic-gate tryopen(
291*0Sstevel@tonic-gate const char *pathname,
292*0Sstevel@tonic-gate int openmode)
293*0Sstevel@tonic-gate {
294*0Sstevel@tonic-gate struct stat st;
295*0Sstevel@tonic-gate int desc;
296*0Sstevel@tonic-gate AudioError err;
297*0Sstevel@tonic-gate
298*0Sstevel@tonic-gate // If the name is changing, set the new one
299*0Sstevel@tonic-gate if (pathname != GetName())
300*0Sstevel@tonic-gate SetName(pathname);
301*0Sstevel@tonic-gate
302*0Sstevel@tonic-gate // Does the file exist?
303*0Sstevel@tonic-gate if (stat(pathname, &st) < 0)
304*0Sstevel@tonic-gate return (AUDIO_UNIXERROR);
305*0Sstevel@tonic-gate
306*0Sstevel@tonic-gate // If not a regular file, stop right there
307*0Sstevel@tonic-gate if (!S_ISREG(st.st_mode))
308*0Sstevel@tonic-gate return (AUDIO_ERR_BADFILEHDR);
309*0Sstevel@tonic-gate
310*0Sstevel@tonic-gate // Open the file and check that it's an audio file
311*0Sstevel@tonic-gate desc = open(GetName(), openmode);
312*0Sstevel@tonic-gate if ((desc < 0) && (errno == EOVERFLOW)) {
313*0Sstevel@tonic-gate return (RaiseError(AUDIO_UNIXERROR, Fatal,
314*0Sstevel@tonic-gate (char *)"Large File"));
315*0Sstevel@tonic-gate } else if (desc < 0) {
316*0Sstevel@tonic-gate return (AUDIO_UNIXERROR);
317*0Sstevel@tonic-gate }
318*0Sstevel@tonic-gate
319*0Sstevel@tonic-gate // Set the file descriptor (this marks the file open)
320*0Sstevel@tonic-gate setfd(desc);
321*0Sstevel@tonic-gate
322*0Sstevel@tonic-gate err = decode_filehdr();
323*0Sstevel@tonic-gate if (err != AUDIO_SUCCESS) {
324*0Sstevel@tonic-gate (void) close(desc);
325*0Sstevel@tonic-gate setfd(-1);
326*0Sstevel@tonic-gate return (err);
327*0Sstevel@tonic-gate }
328*0Sstevel@tonic-gate
329*0Sstevel@tonic-gate // Save the length of the data and the size of the file header
330*0Sstevel@tonic-gate origlen = GetLength();
331*0Sstevel@tonic-gate hdrsize = (off_t)lseek(desc, (off_t)0, SEEK_CUR);
332*0Sstevel@tonic-gate if (hdrsize < 0) {
333*0Sstevel@tonic-gate (void) close(desc);
334*0Sstevel@tonic-gate setfd(-1);
335*0Sstevel@tonic-gate return (err);
336*0Sstevel@tonic-gate }
337*0Sstevel@tonic-gate seekpos = 0;
338*0Sstevel@tonic-gate
339*0Sstevel@tonic-gate // If this is ReadOnly file, mmap() it. Don't worry if mmap() fails.
340*0Sstevel@tonic-gate if (!GetAccess().Writeable()) {
341*0Sstevel@tonic-gate maplen = st.st_size;
342*0Sstevel@tonic-gate
343*0Sstevel@tonic-gate /*
344*0Sstevel@tonic-gate * Can't mmap LITTLE_ENDIAN as they are converted in
345*0Sstevel@tonic-gate * place.
346*0Sstevel@tonic-gate */
347*0Sstevel@tonic-gate if (localByteOrder() == BIG_ENDIAN) {
348*0Sstevel@tonic-gate if ((mapaddr = (caddr_t)mmap(0, (int)maplen, PROT_READ,
349*0Sstevel@tonic-gate MAP_SHARED, desc, 0)) != (caddr_t)-1) {
350*0Sstevel@tonic-gate // set default access method
351*0Sstevel@tonic-gate (void) madvise(mapaddr, (unsigned int)maplen,
352*0Sstevel@tonic-gate (int)GetAccessType());
353*0Sstevel@tonic-gate } else {
354*0Sstevel@tonic-gate (void) RaiseError(AUDIO_UNIXERROR, Warning,
355*0Sstevel@tonic-gate (char *)"Could not mmap() file");
356*0Sstevel@tonic-gate mapaddr = 0;
357*0Sstevel@tonic-gate maplen = 0;
358*0Sstevel@tonic-gate }
359*0Sstevel@tonic-gate } else {
360*0Sstevel@tonic-gate mapaddr = 0;
361*0Sstevel@tonic-gate maplen = 0;
362*0Sstevel@tonic-gate }
363*0Sstevel@tonic-gate }
364*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
365*0Sstevel@tonic-gate }
366*0Sstevel@tonic-gate
367*0Sstevel@tonic-gate // set VM access hint for mmapped files
368*0Sstevel@tonic-gate AudioError AudioFile::
SetAccessType(VMAccess vmacc)369*0Sstevel@tonic-gate SetAccessType(VMAccess vmacc)
370*0Sstevel@tonic-gate {
371*0Sstevel@tonic-gate if (!opened()) {
372*0Sstevel@tonic-gate return (AUDIO_ERR_NOEFFECT);
373*0Sstevel@tonic-gate }
374*0Sstevel@tonic-gate
375*0Sstevel@tonic-gate if (mapaddr == 0) {
376*0Sstevel@tonic-gate return (AUDIO_ERR_NOEFFECT);
377*0Sstevel@tonic-gate }
378*0Sstevel@tonic-gate
379*0Sstevel@tonic-gate (void) madvise(mapaddr, (unsigned int)maplen, (int)vmacc);
380*0Sstevel@tonic-gate vmaccess = vmacc;
381*0Sstevel@tonic-gate
382*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
383*0Sstevel@tonic-gate }
384*0Sstevel@tonic-gate
385*0Sstevel@tonic-gate // Close the file
386*0Sstevel@tonic-gate AudioError AudioFile::
Close()387*0Sstevel@tonic-gate Close()
388*0Sstevel@tonic-gate {
389*0Sstevel@tonic-gate AudioError err;
390*0Sstevel@tonic-gate
391*0Sstevel@tonic-gate if (!opened())
392*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
393*0Sstevel@tonic-gate
394*0Sstevel@tonic-gate // Rewind the file and rewrite the header with the correct length
395*0Sstevel@tonic-gate if (GetAccess().Writeable() && (origlen != GetLength())) {
396*0Sstevel@tonic-gate
397*0Sstevel@tonic-gate // sanity check
398*0Sstevel@tonic-gate if (GetHeader().Time_to_Bytes(GetLength()) !=
399*0Sstevel@tonic-gate (lseek(getfd(), (off_t)0, SEEK_END) - hdrsize)) {
400*0Sstevel@tonic-gate PrintMsg(_MGET_(
401*0Sstevel@tonic-gate "AudioFile:Close()...inconsistent length\n"),
402*0Sstevel@tonic-gate Fatal);
403*0Sstevel@tonic-gate }
404*0Sstevel@tonic-gate
405*0Sstevel@tonic-gate // XXX - should be rewritten in C++
406*0Sstevel@tonic-gate err = (AudioError) audio_rewrite_filesize(getfd(), FILE_AU,
407*0Sstevel@tonic-gate (uint_t)GetHeader().Time_to_Bytes(GetLength()), 0, 0);
408*0Sstevel@tonic-gate }
409*0Sstevel@tonic-gate
410*0Sstevel@tonic-gate // Call the generic file close routine
411*0Sstevel@tonic-gate err = AudioUnixfile::Close();
412*0Sstevel@tonic-gate
413*0Sstevel@tonic-gate if (mapaddr) {
414*0Sstevel@tonic-gate munmap(mapaddr, (int)maplen);
415*0Sstevel@tonic-gate mapaddr = 0;
416*0Sstevel@tonic-gate maplen = 0;
417*0Sstevel@tonic-gate }
418*0Sstevel@tonic-gate
419*0Sstevel@tonic-gate // Init important values, in case the file is reopened
420*0Sstevel@tonic-gate hdrsize = 0;
421*0Sstevel@tonic-gate seekpos = 0;
422*0Sstevel@tonic-gate return (RaiseError(err));
423*0Sstevel@tonic-gate }
424*0Sstevel@tonic-gate
425*0Sstevel@tonic-gate // Read data from underlying file into specified buffer.
426*0Sstevel@tonic-gate // No data format translation takes place.
427*0Sstevel@tonic-gate // The object's read position pointer is unaffected.
428*0Sstevel@tonic-gate AudioError AudioFile::
ReadData(void * buf,size_t & len,Double & pos)429*0Sstevel@tonic-gate ReadData(
430*0Sstevel@tonic-gate void* buf, // destination buffer address
431*0Sstevel@tonic-gate size_t& len, // buffer length (updated)
432*0Sstevel@tonic-gate Double& pos) // start position (updated)
433*0Sstevel@tonic-gate {
434*0Sstevel@tonic-gate off_t offset;
435*0Sstevel@tonic-gate size_t cnt;
436*0Sstevel@tonic-gate caddr_t cp;
437*0Sstevel@tonic-gate AudioError err;
438*0Sstevel@tonic-gate
439*0Sstevel@tonic-gate // If the file is not mapped, call parent ReadData() and return
440*0Sstevel@tonic-gate if (mapaddr == 0) {
441*0Sstevel@tonic-gate // Call the real routine
442*0Sstevel@tonic-gate err = AudioUnixfile::ReadData(buf, len, pos);
443*0Sstevel@tonic-gate // Update the cached seek pointer
444*0Sstevel@tonic-gate seekpos += len;
445*0Sstevel@tonic-gate return (err);
446*0Sstevel@tonic-gate }
447*0Sstevel@tonic-gate
448*0Sstevel@tonic-gate // If the file is mmapped, do a memcpy() from the mapaddr
449*0Sstevel@tonic-gate
450*0Sstevel@tonic-gate // Save buffer size and zero transfer count
451*0Sstevel@tonic-gate cnt = (size_t)len;
452*0Sstevel@tonic-gate len = 0;
453*0Sstevel@tonic-gate
454*0Sstevel@tonic-gate // Cannot read if file is not open
455*0Sstevel@tonic-gate if (!opened() || !GetAccess().Readable())
456*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_NOEFFECT));
457*0Sstevel@tonic-gate
458*0Sstevel@tonic-gate // Position must be valid
459*0Sstevel@tonic-gate if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0))
460*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_BADARG));
461*0Sstevel@tonic-gate
462*0Sstevel@tonic-gate // Make sure we don't read off the end of file
463*0Sstevel@tonic-gate offset = GetHeader().Time_to_Bytes(pos);
464*0Sstevel@tonic-gate
465*0Sstevel@tonic-gate if ((offset + hdrsize) >= maplen) {
466*0Sstevel@tonic-gate // trying to read past EOF
467*0Sstevel@tonic-gate err = AUDIO_EOF;
468*0Sstevel@tonic-gate err.sys = AUDIO_COPY_INPUT_EOF;
469*0Sstevel@tonic-gate return (err);
470*0Sstevel@tonic-gate } else if ((offset + hdrsize + cnt) > maplen) {
471*0Sstevel@tonic-gate // re-adjust cnt so it reads up to the end of file
472*0Sstevel@tonic-gate cnt = (size_t)(maplen - (offset + hdrsize));
473*0Sstevel@tonic-gate }
474*0Sstevel@tonic-gate
475*0Sstevel@tonic-gate // Zero-length reads are finished
476*0Sstevel@tonic-gate if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
477*0Sstevel@tonic-gate err = AUDIO_SUCCESS;
478*0Sstevel@tonic-gate err.sys = AUDIO_COPY_ZERO_LIMIT;
479*0Sstevel@tonic-gate return (err);
480*0Sstevel@tonic-gate } else {
481*0Sstevel@tonic-gate cp = mapaddr + offset + hdrsize;
482*0Sstevel@tonic-gate memcpy((void*)buf, (void*)cp, cnt);
483*0Sstevel@tonic-gate }
484*0Sstevel@tonic-gate
485*0Sstevel@tonic-gate // Return the updated byte count and position
486*0Sstevel@tonic-gate len = cnt;
487*0Sstevel@tonic-gate pos = GetHeader().Bytes_to_Time(offset + len);
488*0Sstevel@tonic-gate
489*0Sstevel@tonic-gate // Check to see if the endian is right. Note that special care
490*0Sstevel@tonic-gate // doesn't need to be taken because of the mmap, since the data
491*0Sstevel@tonic-gate // is copied into a separate buffer anyway.
492*0Sstevel@tonic-gate coerceEndian((unsigned char *)buf, len, localByteOrder());
493*0Sstevel@tonic-gate
494*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
495*0Sstevel@tonic-gate }
496*0Sstevel@tonic-gate
497*0Sstevel@tonic-gate // Write data to underlying file from specified buffer.
498*0Sstevel@tonic-gate // No data format translation takes place.
499*0Sstevel@tonic-gate // The object's write position pointer is unaffected.
500*0Sstevel@tonic-gate AudioError AudioFile::
WriteData(void * buf,size_t & len,Double & pos)501*0Sstevel@tonic-gate WriteData(
502*0Sstevel@tonic-gate void* buf, // source buffer address
503*0Sstevel@tonic-gate size_t& len, // buffer length (updated)
504*0Sstevel@tonic-gate Double& pos) // start position (updated)
505*0Sstevel@tonic-gate {
506*0Sstevel@tonic-gate AudioError err;
507*0Sstevel@tonic-gate
508*0Sstevel@tonic-gate // Call the real routine
509*0Sstevel@tonic-gate err = AudioUnixfile::WriteData(buf, len, pos);
510*0Sstevel@tonic-gate
511*0Sstevel@tonic-gate // Update the cached seek pointer
512*0Sstevel@tonic-gate seekpos += len;
513*0Sstevel@tonic-gate return (err);
514*0Sstevel@tonic-gate }
515*0Sstevel@tonic-gate
516*0Sstevel@tonic-gate // Set the Unix file pointer to match a given file position.
517*0Sstevel@tonic-gate AudioError AudioFile::
seekread(Double pos,off_t & offset)518*0Sstevel@tonic-gate seekread(
519*0Sstevel@tonic-gate Double pos, // position to seek to
520*0Sstevel@tonic-gate off_t& offset) // returned byte offset
521*0Sstevel@tonic-gate {
522*0Sstevel@tonic-gate offset = GetHeader().Time_to_Bytes(pos);
523*0Sstevel@tonic-gate if (offset != seekpos) {
524*0Sstevel@tonic-gate if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0)
525*0Sstevel@tonic-gate return (RaiseError(AUDIO_UNIXERROR, Warning));
526*0Sstevel@tonic-gate seekpos = offset;
527*0Sstevel@tonic-gate }
528*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
529*0Sstevel@tonic-gate }
530*0Sstevel@tonic-gate
531*0Sstevel@tonic-gate // Set the Unix file pointer to match a given file position.
532*0Sstevel@tonic-gate // If seek beyond end-of-file, NULL out intervening data.
533*0Sstevel@tonic-gate AudioError AudioFile::
seekwrite(Double pos,off_t & offset)534*0Sstevel@tonic-gate seekwrite(
535*0Sstevel@tonic-gate Double pos, // position to seek to
536*0Sstevel@tonic-gate off_t& offset) // returned byte offset
537*0Sstevel@tonic-gate {
538*0Sstevel@tonic-gate // If append-only, can't seek backwards into file
539*0Sstevel@tonic-gate if (GetAccess().Append() && (pos < GetLength()))
540*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_NOEFFECT, Warning));
541*0Sstevel@tonic-gate
542*0Sstevel@tonic-gate // If seek beyond eof, fill data
543*0Sstevel@tonic-gate if (pos > GetLength()) {
544*0Sstevel@tonic-gate seekwrite(GetLength(), offset); // seek to eof
545*0Sstevel@tonic-gate
546*0Sstevel@tonic-gate // XXX - not implemented yet
547*0Sstevel@tonic-gate
548*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
549*0Sstevel@tonic-gate }
550*0Sstevel@tonic-gate
551*0Sstevel@tonic-gate offset = GetHeader().Time_to_Bytes(pos);
552*0Sstevel@tonic-gate if (offset != seekpos) {
553*0Sstevel@tonic-gate if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0)
554*0Sstevel@tonic-gate return (RaiseError(AUDIO_UNIXERROR, Warning));
555*0Sstevel@tonic-gate seekpos = offset;
556*0Sstevel@tonic-gate }
557*0Sstevel@tonic-gate return (AUDIO_SUCCESS);
558*0Sstevel@tonic-gate }
559*0Sstevel@tonic-gate
560*0Sstevel@tonic-gate // Copy routine that handles mapped files
561*0Sstevel@tonic-gate AudioError AudioFile::
AsyncCopy(Audio * to,Double & frompos,Double & topos,Double & limit)562*0Sstevel@tonic-gate AsyncCopy(
563*0Sstevel@tonic-gate Audio* to, // audio object to copy to
564*0Sstevel@tonic-gate Double& frompos,
565*0Sstevel@tonic-gate Double& topos,
566*0Sstevel@tonic-gate Double& limit)
567*0Sstevel@tonic-gate {
568*0Sstevel@tonic-gate caddr_t bptr;
569*0Sstevel@tonic-gate size_t offset;
570*0Sstevel@tonic-gate size_t cnt;
571*0Sstevel@tonic-gate size_t svlim;
572*0Sstevel@tonic-gate Double svfrom;
573*0Sstevel@tonic-gate Double svto;
574*0Sstevel@tonic-gate AudioHdr tohdr;
575*0Sstevel@tonic-gate AudioError err;
576*0Sstevel@tonic-gate
577*0Sstevel@tonic-gate // If this is NOT mmapped, or the destination is an AudioBuffer,
578*0Sstevel@tonic-gate // use the default routine
579*0Sstevel@tonic-gate if ((mapaddr == 0) || to->isBuffer()) {
580*0Sstevel@tonic-gate return (Audio::AsyncCopy(to, frompos, topos, limit));
581*0Sstevel@tonic-gate }
582*0Sstevel@tonic-gate
583*0Sstevel@tonic-gate tohdr = to->GetHeader();
584*0Sstevel@tonic-gate if (err = tohdr.Validate())
585*0Sstevel@tonic-gate return (err);
586*0Sstevel@tonic-gate if (limit < 0.)
587*0Sstevel@tonic-gate return (RaiseError(AUDIO_ERR_BADARG));
588*0Sstevel@tonic-gate svlim = (size_t)tohdr.Time_to_Bytes(limit);
589*0Sstevel@tonic-gate
590*0Sstevel@tonic-gate // Get maximum possible copy length
591*0Sstevel@tonic-gate svfrom = GetLength();
592*0Sstevel@tonic-gate if ((frompos >= svfrom) || ((cnt = (size_t)
593*0Sstevel@tonic-gate GetHeader().Time_to_Bytes(svfrom - frompos)) == 0)) {
594*0Sstevel@tonic-gate limit = 0.;
595*0Sstevel@tonic-gate err = AUDIO_EOF;
596*0Sstevel@tonic-gate err.sys = AUDIO_COPY_INPUT_EOF;
597*0Sstevel@tonic-gate return (err);
598*0Sstevel@tonic-gate }
599*0Sstevel@tonic-gate if (!Undefined(limit) && (svlim < cnt))
600*0Sstevel@tonic-gate cnt = svlim;
601*0Sstevel@tonic-gate
602*0Sstevel@tonic-gate limit = 0.;
603*0Sstevel@tonic-gate
604*0Sstevel@tonic-gate offset = (size_t)GetHeader().Time_to_Bytes(frompos);
605*0Sstevel@tonic-gate if ((offset + hdrsize) >= maplen) {
606*0Sstevel@tonic-gate // trying to read past EOF
607*0Sstevel@tonic-gate err = AUDIO_EOF;
608*0Sstevel@tonic-gate err.sys = AUDIO_COPY_INPUT_EOF;
609*0Sstevel@tonic-gate return (err);
610*0Sstevel@tonic-gate } else if ((offset + hdrsize + cnt) > maplen) {
611*0Sstevel@tonic-gate // re-adjust cnt so it reads up to the end of file
612*0Sstevel@tonic-gate cnt = (size_t)(maplen - (offset + hdrsize));
613*0Sstevel@tonic-gate }
614*0Sstevel@tonic-gate
615*0Sstevel@tonic-gate // Zero-length reads are done
616*0Sstevel@tonic-gate if (GetHeader().Bytes_to_Bytes(cnt) == 0) {
617*0Sstevel@tonic-gate err = AUDIO_SUCCESS;
618*0Sstevel@tonic-gate err.sys = AUDIO_COPY_ZERO_LIMIT;
619*0Sstevel@tonic-gate return (err);
620*0Sstevel@tonic-gate }
621*0Sstevel@tonic-gate
622*0Sstevel@tonic-gate // Write the data to the destination and update pointers/ctrs
623*0Sstevel@tonic-gate svfrom = frompos;
624*0Sstevel@tonic-gate svto = topos;
625*0Sstevel@tonic-gate svlim = cnt;
626*0Sstevel@tonic-gate bptr = mapaddr + hdrsize + offset;
627*0Sstevel@tonic-gate err = to->WriteData(bptr, cnt, topos);
628*0Sstevel@tonic-gate limit = topos - svto;
629*0Sstevel@tonic-gate frompos = svfrom + limit;
630*0Sstevel@tonic-gate
631*0Sstevel@tonic-gate // Report short writes
632*0Sstevel@tonic-gate if (!err && (cnt < svlim))
633*0Sstevel@tonic-gate err.sys = AUDIO_COPY_SHORT_OUTPUT;
634*0Sstevel@tonic-gate return (err);
635*0Sstevel@tonic-gate }
636