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:: 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:: 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:: 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:: 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:: 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:: 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:: 264 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:: 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:: 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:: 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:: 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:: 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:: 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