/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1993-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include // class Audio methods // Initialize monotonically increasing id counter int Audio::idctr = 0; // Constructor Audio:: Audio( const char *str): // name id(++idctr), refcnt(0), readpos(0.), writepos(0.), errorfunc(0) { char *s; s = (char *)((str == NULL) ? "" : str); name = new char[strlen(s) + 1]; (void) strcpy(name, s); #ifndef DEBUG // errorfunc is always set if compiling DEBUG; // otherwise, only if requested if (GetDebug() > 0) #endif errorfunc = AudioStderrMsg; PrintMsg(_MGET_("Audio object create"), InitMessage); } // Destructor Audio:: ~Audio() { // If there are outstanding references, there is a programming error if (refcnt < 0) { PrintMsg(_MGET_("Audio object multiple destroy"), InitFatal); } else if (refcnt > 0) { PrintMsg(_MGET_("Referenced Audio object destroyed"), InitFatal); } else { refcnt = -1; PrintMsg(_MGET_("Audio object destroy"), InitMessage); } delete name; } // Raise error code AudioError Audio:: RaiseError( AudioError code, // error code AudioSeverity sev, // error severity const char *msg) const // additional message { if (code == AUDIO_SUCCESS) return (code); if (errorfunc != 0) { // XXX - Userfunc return value ignored for now (void) (*errorfunc)(this, code, sev, msg); } if ((sev == Fatal) || (sev == InitFatal)) abort(); return (code); } // Print out messages void Audio:: PrintMsg( char *msg, // error message AudioSeverity sev) const // error severity { if (errorfunc != 0) { // XXX - Userfunc return value ignored for now (void) (*errorfunc)(this, AUDIO_NOERROR, sev, msg); } if ((sev == Fatal) || (sev == InitFatal)) { fprintf(stderr, _MGET_("** Fatal Error: %s\n"), msg); abort(); } } // Increment reference count void Audio:: Reference() { if (refcnt < 0) { PrintMsg(_MGET_("Reference to destroyed Audio object"), Fatal); } else { refcnt++; } } // Decrement reference count void Audio:: Dereference() { if (refcnt < 0) { PrintMsg(_MGET_("Dereference of destroyed Audio object"), Fatal); } else if (refcnt == 0) { PrintMsg(_MGET_("Audio object dereference underflow"), Fatal); } else if (--refcnt == 0) { // If this was the last reference, delete this; // blow the object away } } // Reset the stored name void Audio:: SetName( const char *str) // new name string { delete name; name = new char[strlen(str) + 1]; (void) strcpy(name, str); } // Set the current read/write position pointer Double Audio:: setpos( Double& pos, // field to update Double newpos, // new position Whence w) // Absolute || Relative || Relative_eof { if (w == Relative) // offset from current position newpos += pos; else if (w == Relative_eof) { // offset from end-of-file if (!Undefined(GetLength())) newpos += GetLength(); else return (AUDIO_UNKNOWN_TIME); } // If seek before start of file, set to start of file if (newpos < 0.) newpos = 0.; pos = newpos; return (pos); } // Set a new read position Double Audio:: SetReadPosition( Double pos, // new position or offset Whence w) // Absolute | Relative { return (setpos(readpos, pos, w)); } // Set a new write position Double Audio:: SetWritePosition( Double pos, // new position or offset Whence w) // Absolute | Relative { return (setpos(writepos, pos, w)); } // Default read routine reads from the current position AudioError Audio:: Read( void* buf, // buffer address size_t& len) // buffer length (updated) { // ReadData updates the position argument return (ReadData(buf, len, readpos)); } // Default write routine writes to the current position AudioError Audio:: Write( void* buf, // buffer address size_t& len) // buffer length (updated) { // WriteData updates the position argument return (WriteData(buf, len, writepos)); } // Default append routine should be specialized, if the object is fixed-length AudioError Audio:: AppendData( void* buf, // buffer address size_t& len, // buffer length (updated) Double& pos) // write position (updated) { // The default action is just to write the data. // Subclasses, like AudioBuffer, should specialize this method // to extend the object, if necessary. return (WriteData(buf, len, pos)); } // Copy out to the specified audio object. // Input and output positions default to the 'current' positions. AudioError Audio:: Copy( Audio* to) // audio object to copy to { Double frompos = AUDIO_UNKNOWN_TIME; Double topos = AUDIO_UNKNOWN_TIME; Double limit = AUDIO_UNKNOWN_TIME; return (Copy(to, frompos, topos, limit)); } // Default Copy out routine. Specify the destination audio object, // and src/dest start offsets. limit is either the time to copy or // AUDIO_UNKNOWN_TIME to copy to eof or error. // frompos and topos are updated with the final positions. // limit is updated with the amount of data actually copied. AudioError Audio:: Copy( Audio* to, // audio object to copy to Double& frompos, Double& topos, Double& limit) { Double len; Double svpos; AudioError err; // If positions are Undefined, try to set them properly if (Undefined(frompos)) frompos = ReadPosition(); if (Undefined(topos)) topos = to->WritePosition(); svpos = frompos; do { // Calculate remaining copy size if (Undefined(limit)) { len = limit; } else { len = limit - (frompos - svpos); if (len < 0.) len = 0.; } // Copy one segment err = AsyncCopy(to, frompos, topos, len); if (!err) { switch (err.sys) { default: case 0: break; // XXX - What do we do with short writes? // This routine is meant to block until all the // data has been copied. So copies to a pipe or // device should continue. However, copies to a // buffer (or extent or list?) will never go any // further. // For now, punt and return immediately. case AUDIO_COPY_SHORT_OUTPUT: goto outofloop; // If a zero-length transfer was requested, we're done case AUDIO_COPY_ZERO_LIMIT: goto outofloop; // If the input would block, we're done case AUDIO_COPY_SHORT_INPUT: goto outofloop; } } } while (err == AUDIO_SUCCESS); outofloop: // Calculate total transfer count limit = frompos - svpos; // Declare victory if anything was copied if (limit > 0.) return (AUDIO_SUCCESS); return (err); } // Default Data Copy out routine. Like Copy(), but only does one segment. // If either src or dest are set non-blocking, a partial transfer may occur. // Returns AUDIO_SUCCESS on normal completion, regardless of how much data // was actually transferred (err.sys: AUDIO_COPY_SHORT_INPUT if input would // block; AUDIO_COPY_ZERO_LIMIT if a zero-length copy was requested). // Returns AUDIO_SUCCESS (err.sys: AUDIO_COPY_SHORT_OUTPUT) if more data was // read than could be copied out (eg, if there was a short write to a // non-blocking output). Short writes result in the input pointer being // backed up to the right place in the input stream. // Returns AUDIO_EOF if input or output position beyond end-of-file. // // XXX - If the input cannot seek backwards, this routine will spin trying // to finish writing all input data to the output. We need to keep // partial data in a state structure. AudioError Audio:: AsyncCopy( Audio* to, // audio object to copy to Double& frompos, Double& topos, Double& limit) { caddr_t bptr; size_t bufsiz; size_t lim; Double svfrom; Double svto; AudioBuffer* tob; AudioHdr tohdr; AudioError err; // Validate basic arguments and state tohdr = to->GetHeader(); if (err = tohdr.Validate()) return (err); if (limit < 0.) return (RaiseError(AUDIO_ERR_BADARG)); lim = (size_t)tohdr.Time_to_Bytes(limit); // If the destination is an AudioBuffer, we can copy more directly if (to->isBuffer()) { tob = (AudioBuffer*) to; // Get the buffer address at the starting offset bptr = (caddr_t)tob->GetAddress(topos); bufsiz = bptr - (caddr_t)tob->GetAddress(); if ((bptr == NULL) || (tob->GetByteCount() <= bufsiz)) { limit = 0.; err = AUDIO_EOF; err.sys = AUDIO_COPY_OUTPUT_EOF; return (err); } bufsiz = tob->GetByteCount() - bufsiz; // Limit the data transfer by the limit argument if (!Undefined(limit) && (lim < bufsiz)) bufsiz = lim; // Read the data directly into buffer (void) tohdr.Bytes_to_Bytes(bufsiz); err = ReadData((void*) bptr, bufsiz, frompos); limit = tohdr.Bytes_to_Time(bufsiz); topos += limit; tob->SetLength(topos); return (err); } // XXX - temporary bogus implementation // XXX - max transfer buf will be 2 seconds of data (1 sec for stereo) if (tohdr.channels < 2) { bufsiz = (size_t)tohdr.Time_to_Bytes(2.0); } else { bufsiz = (size_t)tohdr.Time_to_Bytes(1.0); } if (!Undefined(limit) && (lim < bufsiz)) bufsiz = lim; limit = 0.; if ((bptr = new char[bufsiz]) == NULL) return (AUDIO_UNIXERROR); svfrom = frompos; err = ReadData((void*)bptr, bufsiz, frompos); if (!err) { svto = topos; lim = bufsiz; if (tohdr.Bytes_to_Bytes(bufsiz) != lim) { AUDIO_DEBUG((1, "Read returned a fraction of a sample frame?!\n")); lim = bufsiz; } if (bufsiz > 0) { err = to->WriteData(bptr, bufsiz, topos); limit = topos - svto; // If the write was short, back up the input pointer if (bufsiz < lim) { lim = bufsiz; if (tohdr.Bytes_to_Bytes(bufsiz) != lim) { AUDIO_DEBUG((1, "Write returned a fraction of a sample frame?!\n")); } frompos = svfrom + limit; if (!err) err.sys = AUDIO_COPY_SHORT_OUTPUT; } } } delete bptr; return (err); }