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