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 (c) 1993-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #include <string.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <Audio.h> 31 #include <AudioDebug.h> 32 #include <AudioBuffer.h> 33 34 // class Audio methods 35 36 37 // Initialize monotonically increasing id counter 38 int 39 Audio::idctr = 0; 40 41 // Constructor 42 Audio:: 43 Audio( 44 const char *str): // name 45 id(++idctr), refcnt(0), readpos(0.), writepos(0.), errorfunc(0) 46 { 47 char *s; 48 49 s = (char *)((str == NULL) ? "" : str); 50 name = new char[strlen(s) + 1]; 51 (void) strcpy(name, s); 52 53 #ifndef DEBUG 54 // errorfunc is always set if compiling DEBUG; 55 // otherwise, only if requested 56 if (GetDebug() > 0) 57 #endif 58 errorfunc = AudioStderrMsg; 59 PrintMsg(_MGET_("Audio object create"), InitMessage); 60 } 61 62 // Destructor 63 Audio:: 64 ~Audio() 65 { 66 // If there are outstanding references, there is a programming error 67 if (refcnt < 0) { 68 PrintMsg(_MGET_("Audio object multiple destroy"), InitFatal); 69 } else if (refcnt > 0) { 70 PrintMsg(_MGET_("Referenced Audio object destroyed"), 71 InitFatal); 72 } else { 73 refcnt = -1; 74 PrintMsg(_MGET_("Audio object destroy"), InitMessage); 75 } 76 delete name; 77 } 78 79 // Raise error code 80 AudioError Audio:: 81 RaiseError( 82 AudioError code, // error code 83 AudioSeverity sev, // error severity 84 const char *msg) const // additional message 85 { 86 if (code == AUDIO_SUCCESS) 87 return (code); 88 89 if (errorfunc != 0) { 90 // XXX - Userfunc return value ignored for now 91 (void) (*errorfunc)(this, code, sev, msg); 92 } 93 if ((sev == Fatal) || (sev == InitFatal)) 94 abort(); 95 return (code); 96 } 97 98 // Print out messages 99 void Audio:: 100 PrintMsg( 101 char *msg, // error message 102 AudioSeverity sev) const // error severity 103 { 104 if (errorfunc != 0) { 105 // XXX - Userfunc return value ignored for now 106 (void) (*errorfunc)(this, AUDIO_NOERROR, sev, msg); 107 } 108 109 if ((sev == Fatal) || (sev == InitFatal)) { 110 fprintf(stderr, _MGET_("** Fatal Error: %s\n"), msg); 111 abort(); 112 } 113 } 114 115 // Increment reference count 116 void Audio:: 117 Reference() 118 { 119 if (refcnt < 0) { 120 PrintMsg(_MGET_("Reference to destroyed Audio object"), Fatal); 121 } else { 122 refcnt++; 123 } 124 } 125 126 // Decrement reference count 127 void Audio:: 128 Dereference() 129 { 130 if (refcnt < 0) { 131 PrintMsg(_MGET_("Dereference of destroyed Audio object"), 132 Fatal); 133 } else if (refcnt == 0) { 134 PrintMsg(_MGET_("Audio object dereference underflow"), Fatal); 135 } else if (--refcnt == 0) { // If this was the last reference, 136 delete this; // blow the object away 137 } 138 } 139 140 // Reset the stored name 141 void Audio:: 142 SetName( 143 const char *str) // new name string 144 { 145 delete name; 146 name = new char[strlen(str) + 1]; 147 (void) strcpy(name, str); 148 } 149 150 151 // Set the current read/write position pointer 152 Double Audio:: 153 setpos( 154 Double& pos, // field to update 155 Double newpos, // new position 156 Whence w) // Absolute || Relative || Relative_eof 157 { 158 if (w == Relative) // offset from current position 159 newpos += pos; 160 else if (w == Relative_eof) { // offset from end-of-file 161 if (!Undefined(GetLength())) 162 newpos += GetLength(); 163 else 164 return (AUDIO_UNKNOWN_TIME); 165 } 166 167 // If seek before start of file, set to start of file 168 if (newpos < 0.) 169 newpos = 0.; 170 pos = newpos; 171 return (pos); 172 } 173 174 // Set a new read position 175 Double Audio:: 176 SetReadPosition( 177 Double pos, // new position or offset 178 Whence w) // Absolute | Relative 179 { 180 return (setpos(readpos, pos, w)); 181 } 182 183 // Set a new write position 184 Double Audio:: 185 SetWritePosition( 186 Double pos, // new position or offset 187 Whence w) // Absolute | Relative 188 { 189 return (setpos(writepos, pos, w)); 190 } 191 192 // Default read routine reads from the current position 193 AudioError Audio:: 194 Read( 195 void* buf, // buffer address 196 size_t& len) // buffer length (updated) 197 { 198 // ReadData updates the position argument 199 return (ReadData(buf, len, readpos)); 200 } 201 202 // Default write routine writes to the current position 203 AudioError Audio:: 204 Write( 205 void* buf, // buffer address 206 size_t& len) // buffer length (updated) 207 { 208 // WriteData updates the position argument 209 return (WriteData(buf, len, writepos)); 210 } 211 212 // Default append routine should be specialized, if the object is fixed-length 213 AudioError Audio:: 214 AppendData( 215 void* buf, // buffer address 216 size_t& len, // buffer length (updated) 217 Double& pos) // write position (updated) 218 { 219 // The default action is just to write the data. 220 // Subclasses, like AudioBuffer, should specialize this method 221 // to extend the object, if necessary. 222 return (WriteData(buf, len, pos)); 223 } 224 225 // Copy out to the specified audio object. 226 // Input and output positions default to the 'current' positions. 227 AudioError Audio:: 228 Copy( 229 Audio* to) // audio object to copy to 230 { 231 Double frompos = AUDIO_UNKNOWN_TIME; 232 Double topos = AUDIO_UNKNOWN_TIME; 233 Double limit = AUDIO_UNKNOWN_TIME; 234 235 return (Copy(to, frompos, topos, limit)); 236 } 237 238 // Default Copy out routine. Specify the destination audio object, 239 // and src/dest start offsets. limit is either the time to copy or 240 // AUDIO_UNKNOWN_TIME to copy to eof or error. 241 // frompos and topos are updated with the final positions. 242 // limit is updated with the amount of data actually copied. 243 AudioError Audio:: 244 Copy( 245 Audio* to, // audio object to copy to 246 Double& frompos, 247 Double& topos, 248 Double& limit) 249 { 250 Double len; 251 Double svpos; 252 AudioError err; 253 254 // If positions are Undefined, try to set them properly 255 if (Undefined(frompos)) 256 frompos = ReadPosition(); 257 if (Undefined(topos)) 258 topos = to->WritePosition(); 259 260 svpos = frompos; 261 do { 262 // Calculate remaining copy size 263 if (Undefined(limit)) { 264 len = limit; 265 } else { 266 len = limit - (frompos - svpos); 267 if (len < 0.) 268 len = 0.; 269 } 270 // Copy one segment 271 err = AsyncCopy(to, frompos, topos, len); 272 if (!err) { 273 switch (err.sys) { 274 default: 275 case 0: 276 break; 277 278 // XXX - What do we do with short writes? 279 // This routine is meant to block until all the 280 // data has been copied. So copies to a pipe or 281 // device should continue. However, copies to a 282 // buffer (or extent or list?) will never go any 283 // further. 284 // For now, punt and return immediately. 285 case AUDIO_COPY_SHORT_OUTPUT: 286 goto outofloop; 287 288 // If a zero-length transfer was requested, we're done 289 case AUDIO_COPY_ZERO_LIMIT: 290 goto outofloop; 291 292 // If the input would block, we're done 293 case AUDIO_COPY_SHORT_INPUT: 294 goto outofloop; 295 } 296 } 297 } while (err == AUDIO_SUCCESS); 298 outofloop: 299 // Calculate total transfer count 300 limit = frompos - svpos; 301 302 // Declare victory if anything was copied 303 if (limit > 0.) 304 return (AUDIO_SUCCESS); 305 return (err); 306 } 307 308 // Default Data Copy out routine. Like Copy(), but only does one segment. 309 // If either src or dest are set non-blocking, a partial transfer may occur. 310 // Returns AUDIO_SUCCESS on normal completion, regardless of how much data 311 // was actually transferred (err.sys: AUDIO_COPY_SHORT_INPUT if input would 312 // block; AUDIO_COPY_ZERO_LIMIT if a zero-length copy was requested). 313 // Returns AUDIO_SUCCESS (err.sys: AUDIO_COPY_SHORT_OUTPUT) if more data was 314 // read than could be copied out (eg, if there was a short write to a 315 // non-blocking output). Short writes result in the input pointer being 316 // backed up to the right place in the input stream. 317 // Returns AUDIO_EOF if input or output position beyond end-of-file. 318 // 319 // XXX - If the input cannot seek backwards, this routine will spin trying 320 // to finish writing all input data to the output. We need to keep 321 // partial data in a state structure. 322 AudioError Audio:: 323 AsyncCopy( 324 Audio* to, // audio object to copy to 325 Double& frompos, 326 Double& topos, 327 Double& limit) 328 { 329 caddr_t bptr; 330 size_t bufsiz; 331 size_t lim; 332 Double svfrom; 333 Double svto; 334 AudioBuffer* tob; 335 AudioHdr tohdr; 336 AudioError err; 337 338 // Validate basic arguments and state 339 tohdr = to->GetHeader(); 340 if (err = tohdr.Validate()) 341 return (err); 342 if (limit < 0.) 343 return (RaiseError(AUDIO_ERR_BADARG)); 344 lim = (size_t)tohdr.Time_to_Bytes(limit); 345 346 // If the destination is an AudioBuffer, we can copy more directly 347 if (to->isBuffer()) { 348 tob = (AudioBuffer*) to; 349 350 // Get the buffer address at the starting offset 351 bptr = (caddr_t)tob->GetAddress(topos); 352 bufsiz = bptr - (caddr_t)tob->GetAddress(); 353 if ((bptr == NULL) || (tob->GetByteCount() <= bufsiz)) { 354 limit = 0.; 355 err = AUDIO_EOF; 356 err.sys = AUDIO_COPY_OUTPUT_EOF; 357 return (err); 358 } 359 bufsiz = tob->GetByteCount() - bufsiz; 360 361 // Limit the data transfer by the limit argument 362 if (!Undefined(limit) && (lim < bufsiz)) 363 bufsiz = lim; 364 365 // Read the data directly into buffer 366 (void) tohdr.Bytes_to_Bytes(bufsiz); 367 err = ReadData((void*) bptr, bufsiz, frompos); 368 limit = tohdr.Bytes_to_Time(bufsiz); 369 topos += limit; 370 tob->SetLength(topos); 371 return (err); 372 } 373 374 // XXX - temporary bogus implementation 375 // XXX - max transfer buf will be 2 seconds of data (1 sec for stereo) 376 if (tohdr.channels < 2) { 377 bufsiz = (size_t)tohdr.Time_to_Bytes(2.0); 378 } else { 379 bufsiz = (size_t)tohdr.Time_to_Bytes(1.0); 380 } 381 if (!Undefined(limit) && (lim < bufsiz)) 382 bufsiz = lim; 383 384 limit = 0.; 385 if ((bptr = new char[bufsiz]) == NULL) 386 return (AUDIO_UNIXERROR); 387 388 svfrom = frompos; 389 err = ReadData((void*)bptr, bufsiz, frompos); 390 if (!err) { 391 svto = topos; 392 lim = bufsiz; 393 if (tohdr.Bytes_to_Bytes(bufsiz) != lim) { 394 AUDIO_DEBUG((1, 395 "Read returned a fraction of a sample frame?!\n")); 396 lim = bufsiz; 397 } 398 if (bufsiz > 0) { 399 err = to->WriteData(bptr, bufsiz, topos); 400 limit = topos - svto; 401 402 // If the write was short, back up the input pointer 403 if (bufsiz < lim) { 404 lim = bufsiz; 405 if (tohdr.Bytes_to_Bytes(bufsiz) != lim) { 406 AUDIO_DEBUG((1, 407 "Write returned a fraction of a sample frame?!\n")); 408 } 409 frompos = svfrom + limit; 410 if (!err) 411 err.sys = AUDIO_COPY_SHORT_OUTPUT; 412 } 413 } 414 } 415 delete[] bptr; 416 return (err); 417 } 418