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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <string.h> 30 #include <stdio.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <memory.h> 36 #include <unistd.h> 37 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <sys/file.h> 41 #include <sys/t_lock.h> 42 43 #include <AudioDebug.h> 44 #include <AudioUnixfile.h> 45 #include <libaudio.h> 46 #include <audio_hdr.h> 47 #include <audio/au.h> 48 49 // class AudioUnixfile methods 50 51 // Constructor with pathname and mode arg 52 AudioUnixfile:: 53 AudioUnixfile( 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) 58 { 59 infostring[0] = '\0'; 60 } 61 62 // Destructor 63 AudioUnixfile:: 64 ~AudioUnixfile() 65 { 66 // If the file is open, close it 67 if (opened()) 68 (void) Close(); 69 70 // Deallocate the dynamic storage 71 delete infostring; 72 } 73 74 // Generic open with search path routine just calls default Open() 75 AudioError AudioUnixfile:: 76 OpenPath( 77 const char *) 78 { 79 return (Open()); 80 } 81 82 // Decode an audio file header 83 // This routine reads the audio file header and decodes it. 84 // 85 // This method should be specialized by subclasses that are not files, 86 // like devices for instance. 87 // 88 // XXX - this routine should be rewritten for C++ 89 AudioError AudioUnixfile:: 90 decode_filehdr() 91 { 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 95 au_filehdr_t fhdr; 96 char *ibuf; 97 int file_type; 98 int infosize; 99 int cnt; 100 struct stat st; 101 AudioError err; 102 103 // If fd is not open, or file header already decoded, skip it 104 if (!isfdset() || opened()) 105 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 106 107 // Stat the file, to see if it is a regular file 108 if (fstat(getfd(), &st) < 0) 109 return (RaiseError(AUDIO_UNIXERROR)); 110 111 // Make sure the file is not set for blocking i/o 112 saveblock = GetBlocking(); 113 if (!saveblock) 114 SetBlocking(TRUE); 115 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)); 121 } 122 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)); 128 129 // Allocate and read in the info field 130 ibuf = new char[infosize]; 131 cnt = read(getfd(), ibuf, infosize); 132 if (cnt != infosize) { 133 delete ibuf; 134 return (RaiseError(AUDIO_UNIXERROR)); 135 } 136 SetBlocking(saveblock); // Restore the saved blocking i/o state 137 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 146 // big endian. 147 148 err = SetHeader(hdr_local); 149 if (err != AUDIO_SUCCESS) { 150 delete ibuf; 151 return (RaiseError(err)); 152 } 153 SetInfostring(ibuf, infosize); 154 delete ibuf; 155 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))); 160 161 // Sanity check 162 if ((ohdr.data_size != AUDIO_UNKNOWN_SIZE) && 163 (GetLength() != GetHeader().Bytes_to_Time(ohdr.data_size))) 164 PrintMsg(_MGET_( 165 "AudioUnixfile: header/file size mismatch")); 166 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 169 // correct. 170 } else { 171 setlength(AUDIO_UNKNOWN_TIME); 172 } 173 174 // set flag for opened() test 175 filehdrset = TRUE; 176 177 return (AUDIO_SUCCESS); 178 } 179 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. 183 // 184 // This method should be specialized by subclasses that are not files, 185 // like devices for instance. 186 // 187 // XXX - this routine should be rewritten for C++ 188 AudioError AudioUnixfile:: 189 encode_filehdr() 190 { 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 194 AudioError err; 195 196 // If fd is not open, or file header already written, skip it 197 if (!isfdset() || opened()) 198 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 199 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)); 206 } 207 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; 215 else 216 ohdr.data_size = (uint_t)GetHeader().Time_to_Bytes(GetLength()); 217 218 /* Make sure the file is not set for blocking i/o */ 219 saveblock = GetBlocking(); 220 if (!saveblock) 221 SetBlocking(TRUE); 222 223 // XXX - Should use C++ output method 224 err = (AudioError) audio_write_filehdr(getfd(), &ohdr, FILE_AU, 225 infostring, infolength); 226 227 // set flag for opened() test 228 if (err == AUDIO_SUCCESS) 229 filehdrset = TRUE; 230 231 SetBlocking(saveblock); // Restore the saved blocking i/o state 232 return (RaiseError(err)); 233 } 234 235 // Set a file blocking/non-blocking 236 // This method should be subclassed by objects that always block (eg, files) 237 void AudioUnixfile:: 238 SetBlocking( 239 Boolean b) // FALSE to set non-blocking 240 { 241 int flag; 242 243 // If the file is open, set blocking/non-blocking now 244 if (isfdset()) { 245 flag = fcntl(getfd(), F_GETFL, 0); 246 if ((flag < 0) && (errno == EOVERFLOW || errno == EINVAL)) { 247 RaiseError(AUDIO_UNIXERROR, Fatal, 248 (char *)"Large File"); 249 } else if (b) { 250 flag &= ~(O_NDELAY | O_NONBLOCK); // set blocking 251 } else { 252 flag |= O_NONBLOCK; // set non-blocking 253 } 254 if (fcntl(getfd(), F_SETFL, flag) < 0) { 255 RaiseError(AUDIO_UNIXERROR, Warning); 256 } 257 } 258 // Set the blocking flag (this may affect the Open() behavior) 259 block = b; 260 } 261 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:: 266 GetInfostring( 267 int& len) const // returned length of string 268 { 269 len = infolength; 270 return (infostring); 271 } 272 273 // Set the info string 274 void AudioUnixfile:: 275 SetInfostring( 276 const char *str, // new info string 277 int len) // length of string 278 { 279 // If length defaulted, assume an ASCII string 280 if (len == -1) 281 len = strlen(str) + 1; 282 delete infostring; 283 infostring = new char[len]; 284 infolength = len; 285 (void) memcpy(infostring, str, len); 286 } 287 288 // Close file 289 AudioError AudioUnixfile:: 290 Close() 291 { 292 // If the file is open, close it 293 if (isfdset()) { 294 if (close(getfd()) < 0) 295 return (RaiseError(AUDIO_UNIXERROR)); 296 } else { 297 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 298 } 299 300 // Init important values, in case the file is reopened 301 setfd(-1); 302 filehdrset = FALSE; 303 (void) SetReadPosition((Double)0., Absolute); 304 (void) SetWritePosition((Double)0., Absolute); 305 return (AUDIO_SUCCESS); 306 } 307 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:: 312 ReadData( 313 void* buf, // destination buffer address 314 size_t& len, // buffer length (updated) 315 Double& pos) // start position (updated) 316 { 317 off_t offset; 318 off_t cnt; 319 AudioError err; 320 321 // Save buffer size and zero transfer count 322 cnt = (off_t)len; 323 len = 0; 324 325 // Cannot read if file is not open 326 if (!opened() || !mode.Readable()) 327 return (RaiseError(AUDIO_ERR_NOEFFECT)); 328 329 // Position must be valid 330 if (Undefined(pos) || (pos < 0.) || (cnt < 0)) 331 return (RaiseError(AUDIO_ERR_BADARG)); 332 333 // Position the file pointer to the right place 334 err = seekread(pos, offset); 335 if (err != AUDIO_SUCCESS) 336 return (err); 337 338 // Check for EOF 339 if (pos >= GetLength()) { 340 err = AUDIO_EOF; 341 err.sys = AUDIO_COPY_INPUT_EOF; 342 return (err); 343 } 344 345 // Zero-length reads are finished 346 if (GetHeader().Bytes_to_Bytes(cnt) == 0) { 347 err = AUDIO_SUCCESS; 348 err.sys = AUDIO_COPY_ZERO_LIMIT; 349 return (err); 350 } 351 352 // Read as much data as possible 353 cnt = read(fd, (char *)buf, (int)cnt); 354 if (cnt < 0) { 355 if (errno == EOVERFLOW) { 356 perror("read"); 357 exit(1); 358 } else if ((errno == EINTR) || 359 (((errno == EWOULDBLOCK) || (errno == EAGAIN)) && 360 !GetBlocking())) { 361 // Is this an interrupted or failed non-blocking request? 362 err = AUDIO_SUCCESS; 363 err.sys = AUDIO_COPY_SHORT_INPUT; 364 return (err); 365 } 366 return (RaiseError(AUDIO_UNIXERROR)); 367 } 368 369 // End-of-file? 370 if ((cnt == 0) && GetBlocking()) { 371 if (isDevice() || isPipe()) { 372 AUDIO_DEBUG((1, 373 "Zero-length blocking device/pipe read?!\n")); 374 } 375 err = AUDIO_EOF; 376 err.sys = AUDIO_COPY_INPUT_EOF; 377 return (err); 378 } 379 err = AUDIO_SUCCESS; 380 if (cnt == 0) { 381 err.sys = AUDIO_COPY_SHORT_INPUT; 382 } 383 384 // Return the updated byte count and position 385 len = (size_t)cnt; 386 if (GetHeader().Bytes_to_Bytes(cnt) != len) { 387 AUDIO_DEBUG((1, 388 "Read returned a partial sample frame?!\n")); 389 } 390 pos = GetHeader().Bytes_to_Time(offset + len); 391 392 // Check to see if the endian is right. 393 coerceEndian((unsigned char *)buf, len, localByteOrder()); 394 395 return (err); 396 } 397 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:: 402 WriteData( 403 void* buf, // source buffer address 404 size_t& len, // buffer length (updated) 405 Double& pos) // start position (updated) 406 { 407 off_t offset; 408 off_t cnt; 409 AudioError err; 410 411 // Save buffer size and zero transfer count 412 cnt = (off_t)len; 413 len = 0; 414 415 // Cannot write if file is not open 416 if (!opened() || !mode.Writeable()) 417 return (RaiseError(AUDIO_ERR_NOEFFECT)); 418 419 // Position must be valid 420 if (Undefined(pos) || (pos < 0.) || (cnt < 0)) 421 return (RaiseError(AUDIO_ERR_BADARG)); 422 423 // Zero-length writes are easy 424 if (GetHeader().Bytes_to_Bytes(cnt) == 0) { 425 err = AUDIO_SUCCESS; 426 err.sys = AUDIO_COPY_ZERO_LIMIT; 427 return (err); 428 } 429 430 // Position the file pointer to the right place 431 err = seekwrite(pos, offset); 432 if (err != AUDIO_SUCCESS) 433 return (err); 434 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); 441 442 // Write as much data as possible 443 err = AUDIO_SUCCESS; 444 cnt = write(fd, (char *)buf, (int)cnt); 445 if (cnt < 0) { 446 if (errno == EFBIG) { 447 perror("write"); 448 exit(1); 449 } else if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { 450 // Is this a failed non-blocking request? 451 err.sys = AUDIO_COPY_SHORT_OUTPUT; 452 return (err); 453 } 454 return (RaiseError(AUDIO_UNIXERROR)); 455 } 456 if (cnt == 0) 457 err.sys = AUDIO_COPY_SHORT_OUTPUT; 458 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); 462 463 // Return the updated byte count and position 464 len = (size_t)cnt; 465 pos = GetHeader().Bytes_to_Time(offset + len); 466 467 // If the current position is beyond old EOF, update the size 468 if (!Undefined(GetLength()) && (pos > GetLength())) { 469 setlength(pos); 470 } 471 472 return (AUDIO_SUCCESS); 473 } 474 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. 478 // 479 // This method should be specialized by subclasses that can actually seek, 480 // like regular files for instance. 481 // 482 AudioError AudioUnixfile:: 483 seekread( 484 Double pos, // position to seek to 485 off_t& offset) // returned byte offset 486 { 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 492 Double buflen; 493 AudioError err; 494 495 offset = GetHeader().Time_to_Bytes(pos); 496 pos -= ReadPosition(); 497 498 // If the seek is backwards, do nothing 499 if (pos < 0.) 500 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 501 502 // If the seek is to the current position, then do nothing. 503 icnt = GetHeader().Time_to_Bytes(pos); 504 if (icnt == 0) 505 return (AUDIO_SUCCESS); 506 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]; 517 if (bufp == 0) 518 return (RaiseError(AUDIO_UNIXERROR)); 519 } 520 // XXX - May have to realign to partial frame count! 521 522 saveblock = GetBlocking(); 523 if (!saveblock) 524 SetBlocking(TRUE); 525 526 // Loop until the seek is satisfied (or an error occurs). 527 do { 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) 532 break; 533 icnt -= (long)cnt; 534 } while (icnt > 0); 535 536 SetBlocking(saveblock); // Restore the saved blocking i/o state 537 delete bufp; // Free the temporary buffer 538 return (RaiseError(err)); 539 } 540 541 // Seek in output stream 542 // Ordinary streams (ie, pipes and devices) cannot be rewound. 543 // A forward seek in them writes NULL data. 544 // 545 // This method should be specialized by subclasses that can actually seek, 546 // like regular files for instance. 547 // 548 AudioError AudioUnixfile:: 549 seekwrite( 550 Double pos, // position to seek to 551 off_t& offset) // returned byte offset 552 { 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 558 Double buflen; 559 AudioError err; 560 561 offset = GetHeader().Time_to_Bytes(pos); 562 pos -= WritePosition(); 563 564 // If the seek is backwards, do nothing 565 if (pos < 0.) 566 return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); 567 568 // If the seek is to the current position, then do nothing. 569 ocnt = GetHeader().Time_to_Bytes(pos); 570 if (ocnt == 0) 571 return (AUDIO_SUCCESS); 572 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]; 582 if (bufp == 0) 583 return (RaiseError(AUDIO_UNIXERROR)); 584 } 585 586 // XXX - May have to realign to partial frame count! 587 saveblock = GetBlocking(); 588 if (!saveblock) 589 SetBlocking(TRUE); 590 591 // Loop until the seek is satisfied (or an error occurs). 592 do { 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) 597 break; 598 ocnt -= (long)cnt; 599 } while (ocnt > 0); 600 601 SetBlocking(saveblock); // Restore the saved blocking i/o state 602 delete bufp; // Free the temporary buffer 603 return (RaiseError(err)); 604 } 605