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