/* * 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(); err = tohdr.Validate(); if (err != AUDIO_SUCCESS) 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); } /* * 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) 1992-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include "../include/AudioDebug.h" #include "../include/AudioBuffer.h" #include "../include/zmalloc.h" // class AudioBuffer methods // Constructor with optional hdr, size, and name arguments AudioBuffer:: AudioBuffer( double len, // buffer length, in seconds const char *local_name): // name AudioStream(local_name), buflen(len), zflag(0), bufsize(0), bufaddr(0) { } // Destructor AudioBuffer:: ~AudioBuffer() { (void) SetSize(0.); // deallocate the buffer } // XXX - the following functions are good candidates for inlining // Return TRUE if the stream is 'open' Boolean AudioBuffer:: opened() const { // A buffer is open if it is allocated and has a valid header return (hdrset() && (GetAddress() != 0)); } #define MIN_ZBUFFER (8192 * 10) // only for large buffers // Allocate buffer. Size and header must be set. AudioError AudioBuffer:: alloc() { long size; size_t cnt; unsigned int ncpy; void* tmpbuf; // this is going to be the size we're setting the buffer // to (buflen field). it's set by calling SetSize(). size = GetHeader().Time_to_Bytes(GetSize()); // this is actual current size, in bytes, of the allocated // buffer (the bufsize field). cnt = GetByteCount(); AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - change from %d to %d bytes\n", getid(), cnt, size)); bufsize = 0; if (size == 0) { // Zero size deletes the buffer if (bufaddr != 0) { if (zflag != 0) { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - zfree mmapped buffer\n", getid())); (void) zfree((char *)bufaddr); } else { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - free malloc'd buffer\n", getid())); (void) free((char *)bufaddr); } zflag = 0; } bufaddr = 0; } else if (size < 0) { // Ridiculous size AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - bad size\n", getid())); return (RaiseError(AUDIO_ERR_BADARG)); } else if (bufaddr == 0) { // Allocate a new buffer if (size > MIN_ZBUFFER) { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - zmalloc new buffer\n", getid())); bufaddr = (void*) zmalloc((unsigned int)size); zflag = 1; } else { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - malloc new buffer\n", getid())); bufaddr = (void*) malloc((unsigned int)size); zflag = 0; } if (bufaddr == 0) { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - buffer alloc failed\n", getid())); return (RaiseError(AUDIO_UNIXERROR)); } } else { // A buffer was already allocated. // Change its size, preserving as much data as possible. if ((cnt <= MIN_ZBUFFER) && (size <= MIN_ZBUFFER) && (zflag == 0)) { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - realloc to change size\n", getid())); bufaddr = (void*) realloc((char *)bufaddr, (unsigned int)size); } else { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - zmalloc new buffer\n", getid())); tmpbuf = bufaddr; bufaddr = (void*) zmalloc((unsigned int)size); // copy over as much of the old data as will fit if (bufaddr != 0) { ncpy = (cnt < size) ? (unsigned int)cnt : (unsigned int)size; AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - trasnfer %d bytes\n", getid(), ncpy)); (void) memcpy(bufaddr, tmpbuf, ncpy); } if ((cnt > MIN_ZBUFFER) && (zflag != 0)) { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - zfree old buffer\n", getid())); (void) zfree((char *)tmpbuf); } else { AUDIO_DEBUG((5, "%d: AudioBuffer::alloc - free old buffer\n", getid())); (void) free((char *)tmpbuf); } zflag = 1; } if (bufaddr == 0) { return (RaiseError(AUDIO_UNIXERROR)); } } bufsize = (size_t)size; return (AUDIO_SUCCESS); } // Return the buffer address void* AudioBuffer:: GetAddress() const { return (GetAddress(0.)); } // Return the buffer address at a given time offset // Returns NULL if no buffer, or the position is not within the buffer. void* AudioBuffer:: GetAddress( Double pos) const { char *addr; AudioHdr hdr_local; AudioHdr(AudioBuffer::*hfunc)()const; addr = (char *)bufaddr; if ((addr == 0) || (pos < 0.) || (pos >= buflen)) return (NULL); // If no offset, it's ok if the header hasn't been set yet if (pos == 0.) return ((void*) addr); // Get the header and make sure it's valid // This convoluted hfunc works around non-const function problems hfunc = (AudioHdr(AudioBuffer::*)() const)&AudioBuffer::GetHeader; hdr_local = (this->*hfunc)(); if (hdr_local.Validate()) return (NULL); addr += hdr_local.Time_to_Bytes(pos); // One more validation, to be paranoid before handing out this address if (addr >= ((char *)bufaddr + bufsize)) return (NULL); return ((void*) addr); } // Return the buffer size, in bytes // (as opposed to 'length' which indicates how much data is in the buffer) size_t AudioBuffer:: GetByteCount() const { return (bufsize); } // Return the buffer size, in seconds // (as opposed to 'length' which indicates how much data is in the buffer) Double AudioBuffer:: GetSize() const { return (buflen); } // Set the buffer size, allocating the buffer as necessary AudioError AudioBuffer:: SetSize( Double len) // new size, in seconds { // If no change in size, do nothing if (len == buflen) return (AUDIO_SUCCESS); // If header not set, store the size for later buflen = len; if (!hdrset()) { return (AUDIO_SUCCESS); } // If shrinking buffer, note this if (buflen < GetLength()) SetLength(buflen); return (alloc()); } // Set the data header // If no buffer allocated, allocate one now (if size is set). // If buffer allocated, fiddle the sizes to account for new header type. AudioError AudioBuffer:: SetHeader( const AudioHdr& h) // header to copy { AudioError err; // Validate, then update the header err = h.Validate(); if (err) return (RaiseError(err)); (void) AudioStream::updateheader(h); // If no size set, done for now if (buflen == 0.) return (AUDIO_SUCCESS); // If no buffer allocated, allocate one now if (GetAddress() == 0) return (alloc()); // If buffer allocated, change size to match new header buflen = h.Bytes_to_Time(GetByteCount()); return (AUDIO_SUCCESS); } // Set the buffer length (ie, the amount of data written to the buffer) void AudioBuffer:: SetLength( Double len) // new length { if (!hdrset() || (len < 0.)) // no-op if not ready return; if (!opened() && (len > 0.)) return; if (Undefined(len) || (len > GetSize())) { // Limit to the size of the buffer setlength(GetSize()); } else { setlength(len); } } // Copy data from local buffer into specified buffer. // No data format translation takes place. // The object's read position is not updated. AudioError AudioBuffer:: ReadData( void* buf, // destination buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { off_t resid; off_t cnt; off_t offset; AudioError err; // Copy length, zero return value cnt = (off_t)len; len = 0; // Cannot read if buffer or header not valid if (!opened()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Position must be valid if ((pos < 0.) || (cnt < 0)) return (RaiseError(AUDIO_ERR_BADARG)); // If the starting offset is at or beyond EOF, return eof flag if (pos >= GetLength()) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } // Limit transfer to remaining room in buffer offset = GetHeader().Time_to_Bytes(pos); resid = GetHeader().Time_to_Bytes(GetLength()) - offset; if (resid <= 0) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } if (cnt > resid) cnt = resid; // Fix the alignment to make sure we're not splitting frames err = AUDIO_SUCCESS; if (GetHeader().Bytes_to_Bytes(cnt) > 0) { // Copy as much data as possible memcpy((char *)buf, (char *)((off_t)GetAddress() + offset), (int)cnt); } else { err.sys = AUDIO_COPY_ZERO_LIMIT; } // Return the updated transfer size and position len = (size_t)cnt; pos = GetHeader().Bytes_to_Time(offset + cnt); // Check to see if the endian is right. coerceEndian((unsigned char *)buf, len, localByteOrder()); return (err); } // Copy data to local buffer from specified buffer. // No data format translation takes place. // The object's write position is not updated. AudioError AudioBuffer:: WriteData( void* buf, // source buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { off_t resid; off_t cnt; off_t offset; AudioError err; // Copy length, zero return value cnt = (off_t)len; len = 0; // Cannot write if buffer or header not valid if (!opened()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Position must be valid if ((pos < 0.) || (cnt < 0)) return (RaiseError(AUDIO_ERR_BADARG)); // If the starting offset beyond end of buffer, return short write flag if (pos >= GetSize()) { err = AUDIO_EOF; err.sys = AUDIO_COPY_OUTPUT_EOF; return (err); } // Limit transfer to remaining room in buffer offset = GetHeader().Time_to_Bytes(pos); resid = (off_t)bufsize - offset; if (resid <= 0) { err = AUDIO_EOF; err.sys = AUDIO_COPY_OUTPUT_EOF; return (err); } if (cnt > resid) cnt = resid; // Fix the alignment to make sure we're not splitting frames err = AUDIO_SUCCESS; if (GetHeader().Bytes_to_Bytes(cnt) > 0) { // Copy as much data as possible memcpy((char *)((off_t)GetAddress() + offset), (char *)buf, (int)cnt); } else { err.sys = AUDIO_COPY_ZERO_LIMIT; } // Return the updated transfer size and position len = (size_t)cnt; pos = GetHeader().Bytes_to_Time(offset + cnt); // The end of a write to a buffer always becomes the buffer EOF setlength(pos); return (err); } // AppendData is just like WriteData, except that it guarantees to extend // the buffer if it is not big enough. // The object's write position is not updated. AudioError AudioBuffer:: AppendData( void* buf, // source buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { Double local_length; AudioError err; // Cannot write if header not valid if (!hdrset()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Position must be valid if (pos < 0.) return (RaiseError(AUDIO_ERR_BADARG)); // If the ending offset is beyond end of buffer, extend it local_length = pos + GetHeader().Bytes_to_Time(len); if (local_length > GetSize()) { err = SetSize(local_length); if (err != AUDIO_SUCCESS) return (err); } return (WriteData(buf, len, pos)); } // Copy routine to copy direct to destination AudioError AudioBuffer:: AsyncCopy( Audio* to, // audio object to copy to Double& frompos, Double& topos, Double& limit) { caddr_t bptr; size_t cnt; size_t svcnt; Double svfrom; Double svto; Double lim; AudioHdr tohdr; AudioError err; // Cannot write if buffer or header not valid if (!opened()) return (RaiseError(AUDIO_ERR_NOEFFECT)); tohdr = to->GetHeader(); if (limit < 0.) return (RaiseError(AUDIO_ERR_BADARG)); // Get maximum possible copy length svfrom = GetLength(); if (frompos >= svfrom) { limit = 0.; err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } lim = svfrom - frompos; if (!Undefined(limit) && (limit < lim)) lim = limit; limit = 0.; bptr = (caddr_t)GetAddress(frompos); if (bptr == 0) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } cnt = (size_t)GetHeader().Time_to_Bytes(lim); if (cnt == 0) { err = AUDIO_SUCCESS; err.sys = AUDIO_COPY_ZERO_LIMIT; return (err); } // Add a bunch of paranoid checks svcnt = (size_t)GetAddress() + (size_t)GetByteCount(); if ((bptr + cnt) > (caddr_t)svcnt) { // re-adjust cnt so it reads up to the end of file cnt = (size_t)((caddr_t)svcnt - bptr); } if (GetHeader().Bytes_to_Bytes(cnt) == 0) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } // Write the data to the destination and update pointers/ctrs svfrom = frompos; svto = topos; svcnt = cnt; err = to->WriteData(bptr, cnt, topos); limit = topos - svto; frompos = svfrom + limit; // Report short writes if (!err && (cnt < svcnt)) { err.sys = AUDIO_COPY_SHORT_OUTPUT; } return (err); } /* * 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) 1990-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include // Generic Audio functions // Data format translation occurs transparently AudioError AudioCopy( Audio* from, // input source Audio* to) // output sink { Double frompos = 0.; Double topos = 0.; Double limit = AUDIO_UNKNOWN_TIME; return (AudioCopy(from, to, frompos, topos, limit)); } // Copy a data stream // Data format translation occurs transparently AudioError AudioCopy( Audio* from, // input source Audio* to, // output sink Double& frompos, // input position (updated) Double& topos, // output position (updated) Double& limit) // amount to copy (updated) { return (from->Copy(to, frompos, topos, limit)); } // Copy one segment of a data stream // Data format translation occurs transparently AudioError AudioAsyncCopy( Audio* from, // input source Audio* to, // output sink Double& frompos, // input position (updated) Double& topos, // output position (updated) Double& limit) // amount to copy (updated) { return (from->AsyncCopy(to, frompos, topos, limit)); } /* * 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. */ // XXX - all this either goes away or gets repackaged #include #include #include #include // Global debugging level variable int Audio_debug; // Get debug level int GetDebug() { return (Audio_debug); } // Set debug level void SetDebug( int val) // new level { Audio_debug = val; } // Default error printing routine Boolean AudioStderrMsg( const Audio* cp, // object pointer AudioError code, // error code AudioSeverity sev, // error severity const char *str) // additional message string { int id; char *name; id = cp->getid(); switch (sev) { default: name = cp->GetName(); break; case InitMessage: // virtual function table not ready case InitFatal: name = cp->Audio::GetName(); break; } switch (sev) { case InitMessage: case Message: if (Audio_debug > 1) (void) fprintf(stderr, _MGET_("%d: %s (%s) %s\n"), id, str, name, code.msg()); return (TRUE); case Warning: (void) fprintf(stderr, _MGET_("Warning: %s: %s %s\n"), name, code.msg(), str); if (Audio_debug > 2) abort(); return (TRUE); case Error: (void) fprintf(stderr, _MGET_("Error: %s: %s %s\n"), name, code.msg(), str); if (Audio_debug > 1) abort(); return (FALSE); case Consistency: (void) fprintf(stderr, _MGET_("Audio Consistency Error: %s: %s %s\n"), name, str, code.msg()); if (Audio_debug > 0) abort(); return (FALSE); case InitFatal: case Fatal: (void) fprintf(stderr, _MGET_("Audio Internal Error: %s: %s %s\n"), name, str, code.msg()); if (Audio_debug > 0) abort(); return (FALSE); } return (TRUE); } #ifdef DEBUG void AudioDebugMsg( int level, char *fmt, ...) { va_list ap; if (Audio_debug >= level) { va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } } #endif /* * 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 // class Audio methods // Convert error code to string char *AudioError:: msg() { if (code == AUDIO_NOERROR) return (char *)(""); if (code == AUDIO_UNIXERROR) { if (sys == 0) { sys = errno; } if (sys >= 0) { return (strerror(sys)); } else { return (_MGET_("Unknown UNIX error")); } } // XXX - these must jive with what's in audio_errno.h switch (code) { case 0: /* AUDIO_SUCCESS = 0 */ return (_MGET_("Audio operation successful")); case 1: /* AUDIO_ERR_BADHDR = 1 */ return (_MGET_("Invalid audio header")); case 2: /* AUDIO_ERR_BADFILEHDR = 2 */ return (_MGET_("Invalid audio file header")); case 3: /* AUDIO_ERR_BADARG = 3 */ return (_MGET_("Invalid argument or value")); case 4: /* AUDIO_ERR_NOEFFECT = 4 */ return (_MGET_("Audio operation not performed")); case 5: /* AUDIO_ERR_ENCODING = 5 */ return (_MGET_("Unknown audio encoding format")); case 6: /* AUDIO_ERR_INTERRUPTED = 6 */ return (_MGET_("Audio operation interrupted")); case 7: /* AUDIO_EOF = 7 */ return (_MGET_("Audio end-of-file")); case 8: /* AUDIO_ERR_HDRINVAL = 8 */ return (_MGET_("Unsupported audio data format")); case 9: /* AUDIO_ERR_PRECISION = 9 */ return (_MGET_("Unsupported audio data precision")); case 10: /* AUDIO_ERR_NOTDEVICE = 10 */ return (_MGET_("Not an audio device")); case 11: /* AUDIO_ERR_DEVICEBUSY = 11 */ return (_MGET_("Audio device is busy")); case 12: /* AUDIO_ERR_BADFRAME = 12 */ return (_MGET_("Partial sample frame")); case 13: /* AUDIO_ERR_FORMATLOCK = 13 */ return (_MGET_("Audio format cannot be changed")); case 14: /* AUDIO_ERR_DEVOVERFLOW = 14 */ return (_MGET_("Audio device overrun")); default: return (_MGET_("Unknown audio error")); } } /* * 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 // class AudioExtent methods // class AudioExtent Constructor AudioExtent:: AudioExtent( Audio* obj, // audio object to point to double s, // start time double e): // end time Audio("[extent]"), ref(obj) { ref->Reference(); // reference audio object SetStart(s); // set start/end times SetEnd(e); } // class AudioExtent Destructor AudioExtent:: ~AudioExtent() { ref->Dereference(); // clear audio object reference } // Get referenced object Audio* AudioExtent:: GetRef() const { return (ref); } // Set referenced object void AudioExtent:: SetRef( Audio* r) // new audio object { if (ref == r) // object is not changing return; ref->Dereference(); // dereference previous object if (r != 0) { ref = r; ref->Reference(); // reference new object } else { PrintMsg(_MGET_("AudioExtent:...NULL Audio object reference"), Fatal); } } // Get start time Double AudioExtent:: GetStart() const { return (start); } // Set start time void AudioExtent:: SetStart( Double s) // start time, in seconds { if (Undefined(s) || (s < 0.)) start = 0.; else start = s; } // Get end time Double AudioExtent:: GetEnd() const { // If determinate endpoint, return it if (!Undefined(end)) return (end); // Otherwise, return the endpoint of the underlying object return (ref->GetLength()); } // Set end time void AudioExtent:: SetEnd( Double e) // end time, in seconds { Double len; // If known endpoint and object has known size, do not exceed size if (!Undefined(e)) { len = ref->GetLength(); if (!Undefined(len) && (e > len)) e = len; } end = e; } // Get the length of an audio extent Double AudioExtent:: GetLength() const { Double x; // If extent end is indeterminate, use the end of the target object x = GetEnd(); // If the object length is indeterminate, then the length is if (Undefined(x)) return (x); return (x - start); } // Construct a name for the list char *AudioExtent:: GetName() const { // XXX - construct a better name return (ref->GetName()); } // Get the audio header for the current read position AudioHdr AudioExtent:: GetHeader() { return (ref->GetDHeader(ReadPosition() + start)); } // Get the audio header for the given position AudioHdr AudioExtent:: GetHeader( Double pos) // position { return (ref->GetDHeader(pos + start)); } // Copy data from extent into specified buffer. // No data format translation takes place. // The object's read position is not updated. // // Since the extent could refer to a list of extents of differing encodings, // clients should always use GetHeader() in combination with ReadData() AudioError AudioExtent:: ReadData( void* buf, // destination buffer address size_t& len, // buffer size (updated) Double& pos) // start position (updated) { size_t cnt; off_t buflen; Double off; Double newpos; AudioError err; // Save buffer size and zero transfer count cnt = len; len = 0; // Position must be valid if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0)) return (RaiseError(AUDIO_ERR_BADARG)); // If the end is determinate, check start position and length off = pos + start; newpos = GetEnd(); if (!Undefined(newpos)) { // If starting beyond eof, give up now if (off >= newpos) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } // If the read would extend beyond end-of-extent, shorten it buflen = GetHeader(pos).Time_to_Bytes((Double)(newpos - off)); if (buflen == 0) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } if (buflen < cnt) cnt = (size_t)buflen; } // Zero-length reads are easy if (cnt == 0) { err = AUDIO_SUCCESS; err.sys = AUDIO_COPY_ZERO_LIMIT; return (err); } // Save the offset, read data, and update the returned position newpos = off; len = cnt; err = ref->ReadData(buf, len, newpos); pos = (newpos - start); // XXX - calculate bytes and convert? return (err); } // Write to AudioExtent is (currently) prohibited AudioError AudioExtent:: WriteData( void*, // destination buffer address size_t& len, // buffer size (updated) Double&) // start position (updated) { len = 0; return (RaiseError(AUDIO_ERR_NOEFFECT)); } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // class AudioFile methods // Initialize temporary file params #define TMPDIR "/tmp" #define TMPFILE "/audiotoolXXXXXX" static char *tmpdir = NULL; static const char *tmpname = "(temporary file)"; static const FileAccess tmpmode = ReadWrite; static const VMAccess defaccess = SequentialAccess; // Initialize default access mode, used when a filename is supplied const FileAccess AudioFile::defmode = ReadOnly; // Default audio file path prefix environment variable const char *AudioFile::AUDIO_PATH = "AUDIOPATH"; // Constructor with no arguments opens a read/write temporary file AudioFile:: AudioFile(): AudioUnixfile(tmpname, tmpmode), hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0), vmaccess(defaccess) { } // Constructor with pathname and optional mode arg AudioFile:: AudioFile( const char *path, // filename const FileAccess acc): // access mode AudioUnixfile(path, acc), hdrsize(0), seekpos(0), origlen(0.), mapaddr(0), maplen(0), vmaccess(defaccess) { } // Destructor must call the local Close() routine AudioFile:: ~AudioFile() { // If the file was open, close it if (opened()) (void) Close(); } // Set a default temporary file directory AudioError AudioFile:: SetTempPath( const char *path) { struct stat st; // Verify intended path if ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode) || (access(path, W_OK) < 0)) { errno = ENOTDIR; return (AUDIO_UNIXERROR); } if (tmpdir != NULL) (void) free(tmpdir); tmpdir = (char *)malloc(strlen(path) + 1); (void) strcpy(tmpdir, path); return (AUDIO_SUCCESS); } // Create a named file according to the current mode setting AudioError AudioFile:: createfile( const char *path) // pathname or 0 { char *tmpf; char *tmpstr; int openmode; int desc; AudioError err; // Convert the open mode to an int argument for open() openmode = GetAccess(); // Was the header properly set? if (!hdrset()) return (RaiseError(AUDIO_ERR_BADHDR)); // Can't create if already opened or if mode or name not set if ((openmode == -1) || opened() || (strlen(path) == 0)) return (RaiseError(AUDIO_ERR_NOEFFECT)); // If temporary file, create and unlink it. if (strcmp(path, tmpname) == 0) { // Construct the temporary file path tmpstr = (char *)malloc(1 + strlen(TMPFILE) + strlen((tmpdir == NULL) ? TMPDIR : tmpdir)); (void) sprintf(tmpstr, "%s%s", (tmpdir == NULL) ? TMPDIR : tmpdir, TMPFILE); tmpf = mktemp(tmpstr); // Open the temp file and unlink it err = createfile(tmpf); if ((err == AUDIO_SUCCESS) && (unlink(tmpf) < 0)) { (void) Close(); err = RaiseError(AUDIO_UNIXERROR, Warning); } (void) free(tmpstr); return (err); } // Create the file desc = open(path, openmode | O_CREAT | O_TRUNC, 0666); if ((desc < 0) && (errno == EOVERFLOW)) { return (RaiseError(AUDIO_UNIXERROR, Fatal, (char *)"Large File")); } else if (desc < 0) { return (RaiseError(AUDIO_UNIXERROR)); } // Set the file descriptor (this marks the file open) setfd(desc); // Write the file header with current (usually unknown) size err = encode_filehdr(); if (err != AUDIO_SUCCESS) { setfd(-1); (void) close(desc); // If error, remove file (void) unlink(path); return (err); } // Save the length that got written, then set it to zero origlen = GetLength(); setlength(0.); // Set the size of the file header hdrsize = lseek(desc, (off_t)0, SEEK_CUR); if (hdrsize < 0) { setfd(-1); (void) close(desc); // If error, remove file (void) unlink(path); return (err); } seekpos = 0; return (AUDIO_SUCCESS); } // Create a file whose name is already set, according to the mode setting AudioError AudioFile:: Create() { return (createfile(GetName())); } // Open a file whose name is set AudioError AudioFile:: Open() { return (OpenPath(NULL)); } // Open a file, using the specified path prefixes AudioError AudioFile:: OpenPath( const char *path) { char *filename; int flen; char *prefix; char *str; char *wrk; char *pathname; int openmode; AudioError err; // Convert the open mode to an int argument for open() openmode = GetAccess(); filename = GetName(); flen = strlen(filename); // Can't open if already opened or if mode or name not set if ((openmode == -1) || opened() || (strlen(filename) == 0)) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Search path: // 1) try name: if not found and not readonly: // if Append mode, try creating it // 2) if name is a relative pathname, and 'path' is not NULL: // try every path prefix in 'path' err = tryopen(filename, openmode); if (!err) return (AUDIO_SUCCESS); if (GetAccess().Writeable() || (filename[0] == '/')) { // If file is non-existent and Append mode, try creating it. if ((err == AUDIO_UNIXERROR) && (err.sys == ENOENT) && GetAccess().Append() && hdrset()) { return (Create()); } return (RaiseError(err)); } // Try path as an environment variable name, else assume it is a path str = (path == NULL) ? NULL : getenv(path); if (str == NULL) str = (char *)path; if (str != NULL) { // Make a copy of the path, to parse it wrk = new char[strlen(str) + 1]; (void) strcpy(wrk, str); str = wrk; // Try each component as a path prefix for (prefix = str; (prefix != NULL) && (prefix[0] != '\0'); prefix = str) { str = strchr(str, ':'); if (str != NULL) *str++ = '\0'; pathname = new char[strlen(prefix) + flen + 2]; (void) sprintf(pathname, "%s/%s", prefix, filename); err = tryopen(pathname, openmode); delete[] pathname; switch (err) { case AUDIO_SUCCESS: // found the file delete[] wrk; return (RaiseError(err)); // XXX - if file found but not audio, stop looking?? } } delete[] wrk; } // Can't find file. Return the original error condition. return (RaiseError(tryopen(filename, openmode))); } // Attempt to open the given audio file AudioError AudioFile:: tryopen( const char *pathname, int openmode) { struct stat st; int desc; AudioError err; // If the name is changing, set the new one if (pathname != GetName()) SetName(pathname); // Does the file exist? if (stat(pathname, &st) < 0) return (AUDIO_UNIXERROR); // If not a regular file, stop right there if (!S_ISREG(st.st_mode)) return (AUDIO_ERR_BADFILEHDR); // Open the file and check that it's an audio file desc = open(GetName(), openmode); if ((desc < 0) && (errno == EOVERFLOW)) { return (RaiseError(AUDIO_UNIXERROR, Fatal, (char *)"Large File")); } else if (desc < 0) { return (AUDIO_UNIXERROR); } // Set the file descriptor (this marks the file open) setfd(desc); err = decode_filehdr(); if (err != AUDIO_SUCCESS) { (void) close(desc); setfd(-1); return (err); } // Save the length of the data and the size of the file header origlen = GetLength(); hdrsize = (off_t)lseek(desc, (off_t)0, SEEK_CUR); if (hdrsize < 0) { (void) close(desc); setfd(-1); return (err); } seekpos = 0; // If this is ReadOnly file, mmap() it. Don't worry if mmap() fails. if (!GetAccess().Writeable()) { maplen = st.st_size; /* * Can't mmap LITTLE_ENDIAN as they are converted in * place. */ if (localByteOrder() == BIG_ENDIAN) { if ((mapaddr = (caddr_t)mmap(0, (int)maplen, PROT_READ, MAP_SHARED, desc, 0)) != (caddr_t)-1) { // set default access method (void) madvise(mapaddr, (unsigned int)maplen, (int)GetAccessType()); } else { (void) RaiseError(AUDIO_UNIXERROR, Warning, (char *)"Could not mmap() file"); mapaddr = 0; maplen = 0; } } else { mapaddr = 0; maplen = 0; } } return (AUDIO_SUCCESS); } // set VM access hint for mmapped files AudioError AudioFile:: SetAccessType(VMAccess vmacc) { if (!opened()) { return (AUDIO_ERR_NOEFFECT); } if (mapaddr == 0) { return (AUDIO_ERR_NOEFFECT); } (void) madvise(mapaddr, (unsigned int)maplen, (int)vmacc); vmaccess = vmacc; return (AUDIO_SUCCESS); } // Close the file AudioError AudioFile:: Close() { AudioError err; if (!opened()) return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); // Rewind the file and rewrite the header with the correct length if (GetAccess().Writeable() && (origlen != GetLength())) { // sanity check if (GetHeader().Time_to_Bytes(GetLength()) != (lseek(getfd(), (off_t)0, SEEK_END) - hdrsize)) { PrintMsg(_MGET_( "AudioFile:Close()...inconsistent length\n"), Fatal); } // XXX - should be rewritten in C++ err = (AudioError) audio_rewrite_filesize(getfd(), FILE_AU, (uint_t)GetHeader().Time_to_Bytes(GetLength()), 0, 0); } // Call the generic file close routine err = AudioUnixfile::Close(); if (mapaddr) { munmap(mapaddr, (int)maplen); mapaddr = 0; maplen = 0; } // Init important values, in case the file is reopened hdrsize = 0; seekpos = 0; return (RaiseError(err)); } // Read data from underlying file into specified buffer. // No data format translation takes place. // The object's read position pointer is unaffected. AudioError AudioFile:: ReadData( void* buf, // destination buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { off_t offset; size_t cnt; caddr_t cp; AudioError err; // If the file is not mapped, call parent ReadData() and return if (mapaddr == 0) { // Call the real routine err = AudioUnixfile::ReadData(buf, len, pos); // Update the cached seek pointer seekpos += len; return (err); } // If the file is mmapped, do a memcpy() from the mapaddr // Save buffer size and zero transfer count cnt = (size_t)len; len = 0; // Cannot read if file is not open if (!opened() || !GetAccess().Readable()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Position must be valid if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0)) return (RaiseError(AUDIO_ERR_BADARG)); // Make sure we don't read off the end of file offset = GetHeader().Time_to_Bytes(pos); if ((offset + hdrsize) >= maplen) { // trying to read past EOF err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } else if ((offset + hdrsize + cnt) > maplen) { // re-adjust cnt so it reads up to the end of file cnt = (size_t)(maplen - (offset + hdrsize)); } // Zero-length reads are finished if (GetHeader().Bytes_to_Bytes(cnt) == 0) { err = AUDIO_SUCCESS; err.sys = AUDIO_COPY_ZERO_LIMIT; return (err); } else { cp = mapaddr + offset + hdrsize; memcpy((void*)buf, (void*)cp, cnt); } // Return the updated byte count and position len = cnt; pos = GetHeader().Bytes_to_Time(offset + len); // Check to see if the endian is right. Note that special care // doesn't need to be taken because of the mmap, since the data // is copied into a separate buffer anyway. coerceEndian((unsigned char *)buf, len, localByteOrder()); return (AUDIO_SUCCESS); } // Write data to underlying file from specified buffer. // No data format translation takes place. // The object's write position pointer is unaffected. AudioError AudioFile:: WriteData( void* buf, // source buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { AudioError err; // Call the real routine err = AudioUnixfile::WriteData(buf, len, pos); // Update the cached seek pointer seekpos += len; return (err); } // Set the Unix file pointer to match a given file position. AudioError AudioFile:: seekread( Double pos, // position to seek to off_t& offset) // returned byte offset { offset = GetHeader().Time_to_Bytes(pos); if (offset != seekpos) { if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0) return (RaiseError(AUDIO_UNIXERROR, Warning)); seekpos = offset; } return (AUDIO_SUCCESS); } // Set the Unix file pointer to match a given file position. // If seek beyond end-of-file, NULL out intervening data. AudioError AudioFile:: seekwrite( Double pos, // position to seek to off_t& offset) // returned byte offset { // If append-only, can't seek backwards into file if (GetAccess().Append() && (pos < GetLength())) return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); // If seek beyond eof, fill data if (pos > GetLength()) { seekwrite(GetLength(), offset); // seek to eof // XXX - not implemented yet return (AUDIO_SUCCESS); } offset = GetHeader().Time_to_Bytes(pos); if (offset != seekpos) { if (lseek(getfd(), (off_t)(hdrsize + offset), SEEK_SET) < 0) return (RaiseError(AUDIO_UNIXERROR, Warning)); seekpos = offset; } return (AUDIO_SUCCESS); } // Copy routine that handles mapped files AudioError AudioFile:: AsyncCopy( Audio* to, // audio object to copy to Double& frompos, Double& topos, Double& limit) { caddr_t bptr; size_t offset; size_t cnt; size_t svlim; Double svfrom; Double svto; AudioHdr tohdr; AudioError err; // If this is NOT mmapped, or the destination is an AudioBuffer, // use the default routine if ((mapaddr == 0) || to->isBuffer()) { return (Audio::AsyncCopy(to, frompos, topos, limit)); } tohdr = to->GetHeader(); err = tohdr.Validate(); if (err != AUDIO_SUCCESS) return (err); if (limit < 0.) return (RaiseError(AUDIO_ERR_BADARG)); svlim = (size_t)tohdr.Time_to_Bytes(limit); // Get maximum possible copy length svfrom = GetLength(); if ((frompos >= svfrom) || ((cnt = (size_t) GetHeader().Time_to_Bytes(svfrom - frompos)) == 0)) { limit = 0.; err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } if (!Undefined(limit) && (svlim < cnt)) cnt = svlim; limit = 0.; offset = (size_t)GetHeader().Time_to_Bytes(frompos); if ((offset + hdrsize) >= maplen) { // trying to read past EOF err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } else if ((offset + hdrsize + cnt) > maplen) { // re-adjust cnt so it reads up to the end of file cnt = (size_t)(maplen - (offset + hdrsize)); } // Zero-length reads are done if (GetHeader().Bytes_to_Bytes(cnt) == 0) { err = AUDIO_SUCCESS; err.sys = AUDIO_COPY_ZERO_LIMIT; return (err); } // Write the data to the destination and update pointers/ctrs svfrom = frompos; svto = topos; svlim = cnt; bptr = mapaddr + hdrsize + offset; err = to->WriteData(bptr, cnt, topos); limit = topos - svto; frompos = svfrom + limit; // Report short writes if (!err && (cnt < svlim)) err.sys = AUDIO_COPY_SHORT_OUTPUT; return (err); } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #define irint(d) ((int)d) // initialize constants for instananeous gain normalization const double AudioGain::LoSigInstantRange = .008; const double AudioGain::HiSigInstantRange = .48; // initialize constants for weighted gain normalization const double AudioGain::NoSigWeight = .0000; const double AudioGain::LoSigWeightRange = .001; const double AudioGain::HiSigWeightRange = .050; // u-law max value converted to floating point const double AudioGain::PeakSig = .9803765; // XXX - patchable dc time constant: TC = 1 / (sample rate / DCfreq) int DCfreq = 500; const double AudioGain::DCtimeconstant = .1; // patchable debugging flag int debug_agc = 0; // Constructor AudioGain:: AudioGain(): clipcnt(0), DCaverage(0.), instant_gain(0.), weighted_peaksum(0.), weighted_sum(0.), weighted_avgsum(0.), weighted_cnt(0), gain_cache(NULL) { } // Destructor AudioGain:: ~AudioGain() { if (gain_cache != NULL) { delete gain_cache; } } // Return TRUE if we can handle this data type Boolean AudioGain:: CanConvert( const AudioHdr& hdr) const { return (float_convert.CanConvert(hdr)); } // Return latest instantaneous gain double AudioGain:: InstantGain() { return ((double)instant_gain); } // Return latest weighted gain double AudioGain:: WeightedGain() { double g; // Accumulated sum is averaged by the cache size and number of sums if ((weighted_cnt > 0) && (gain_cache_size > 0.)) { g = weighted_avgsum / gain_cache_size; g /= weighted_cnt; g -= NoSigWeight; if (g > HiSigWeightRange) { g = 1.; } else if (g < 0.) { g = 0.; } else { g /= HiSigWeightRange; } } else { g = 0.; } return (g); } // Return latest weighted peak // Clears the weighted peak for next calculation. double AudioGain:: WeightedPeak() { double g; // Peak sum is averaged by the cache size if (gain_cache_size > 0.) { g = weighted_peaksum / gain_cache_size; g -= NoSigWeight; if (g > HiSigWeightRange) { g = 1.; } else if (g < 0.) { g = 0.; } else { g /= HiSigWeightRange; } } else { g = 0.; } weighted_peaksum = 0.; return (g); } // Return TRUE if signal clipped during last processed buffer Boolean AudioGain:: Clipped() { Boolean clipped; clipped = (clipcnt > 0); return (clipped); } // Flush gain state void AudioGain:: Flush() { clipcnt = 0; DCaverage = 0.; instant_gain = 0.; weighted_peaksum = 0.; weighted_sum = 0.; weighted_avgsum = 0.; weighted_cnt = 0; if (gain_cache != NULL) { delete gain_cache; gain_cache = NULL; } } // Process an input buffer according to the specified flags // The input buffer is consumed if the reference count is zero! AudioError AudioGain:: Process( AudioBuffer* inbuf, int type) { AudioHdr newhdr; AudioError err; if (inbuf == NULL) return (AUDIO_ERR_BADARG); if (Undefined(inbuf->GetLength())) { err = AUDIO_ERR_BADARG; process_error: // report error and toss the buffer if it is not referenced inbuf->RaiseError(err); inbuf->Reference(); inbuf->Dereference(); return (err); } // Set up to convert to floating point; verify all header formats newhdr = inbuf->GetHeader(); if (!float_convert.CanConvert(newhdr)) { err = AUDIO_ERR_HDRINVAL; goto process_error; } newhdr.encoding = FLOAT; newhdr.bytes_per_unit = 8; if ((err = newhdr.Validate()) || !float_convert.CanConvert(newhdr)) { err = AUDIO_ERR_HDRINVAL; goto process_error; } // Convert to floating-point up front, if necessary if (inbuf->GetHeader() != newhdr) { err = float_convert.Convert(inbuf, newhdr); if (err) goto process_error; } // Reference the resulting buffer to make sure it gets ditched later inbuf->Reference(); // run through highpass filter to reject DC process_dcfilter(inbuf); if (type & AUDIO_GAIN_INSTANT) process_instant(inbuf); if (type & AUDIO_GAIN_WEIGHTED) process_weighted(inbuf); inbuf->Dereference(); return (AUDIO_SUCCESS); } // Run the buffer through a simple, dc filter. // Buffer is assumed to be floating-point double PCM void AudioGain:: process_dcfilter( AudioBuffer* inbuf) { int i; Boolean lastpeak; double val; double dcweight; double timeconstant; AudioHdr inhdr; double *inptr; size_t frames; inhdr = inbuf->GetHeader(); inptr = (double *)inbuf->GetAddress(); frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength()); clipcnt = 0; lastpeak = FALSE; // Time constant corresponds to the number of samples for 500Hz timeconstant = 1. / (inhdr.sample_rate / (double)DCfreq); dcweight = 1. - timeconstant; // loop through the input buffer, rewriting with weighted data // XXX - should deal with multi-channel data! // XXX - for now, check first channel only for (i = 0; i < frames; i++, inptr += inhdr.channels) { val = *inptr; // Two max values in a row constitutes clipping if ((val >= PeakSig) || (val <= -PeakSig)) { if (lastpeak) { clipcnt++; } else { lastpeak = TRUE; } } else { lastpeak = FALSE; } // Add in this value to weighted average DCaverage = (DCaverage * dcweight) + (val * timeconstant); val -= DCaverage; if (val > 1.) val = 1.; else if (val < -1.) val = -1.; *inptr = val; } } // Calculate a single energy value averaged from the input buffer // Buffer is assumed to be floating-point double PCM void AudioGain:: process_instant( AudioBuffer* inbuf) { int i; double val; double sum; double sv; AudioHdr inhdr; double *inptr; size_t frames; inhdr = inbuf->GetHeader(); inptr = (double *)inbuf->GetAddress(); frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength()); // loop through the input buffer, calculating gain // XXX - should deal with multi-channel data! // XXX - for now, check first channel only sum = 0.; for (i = 0; i < frames; i++, inptr += inhdr.channels) { // Get absolute value sum += fabs(*inptr); } sum /= (double)frames; // calculate level meter value (between 0 & 1) val = log10(1. + (9. * sum)); sv = val; // Normalize to within a reasonable range val -= LoSigInstantRange; if (val > HiSigInstantRange) { val = 1.; } else if (val < 0.) { val = 0.; } else { val /= HiSigInstantRange; } instant_gain = val; if (debug_agc != 0) { printf("audio_amplitude: avg = %7.5f log value = %7.5f, " "adjusted = %7.5f\n", sum, sv, val); } } // Calculate a weighted gain for agc computations // Buffer is assumed to be floating-point double PCM void AudioGain:: process_weighted( AudioBuffer* inbuf) { int i; double val; double nosig; AudioHdr inhdr; double *inptr; size_t frames; Double sz; inhdr = inbuf->GetHeader(); inptr = (double *)inbuf->GetAddress(); frames = (size_t)inhdr.Time_to_Samples(inbuf->GetLength()); sz = (Double) frames; // Allocate gain cache...all calls will hopefully be the same length if (gain_cache == NULL) { gain_cache = new double[frames]; for (i = 0; i < frames; i++) { gain_cache[i] = 0.; } gain_cache_size = sz; } else if (sz > gain_cache_size) { frames = (size_t)irint(gain_cache_size); } // Scale up the 'no signal' level to avoid a divide in the inner loop nosig = NoSigWeight * gain_cache_size; // For each sample: // calculate the sum of squares for a window around the sample; // save the peak sum of squares; // keep a running average of the sum of squares // // XXX - should deal with multi-channel data! // XXX - for now, check first channel only for (i = 0; i < frames; i++, inptr += inhdr.channels) { val = *inptr; val *= val; weighted_sum += val; weighted_sum -= gain_cache[i]; gain_cache[i] = val; // save value to subtract later if (weighted_sum > weighted_peaksum) weighted_peaksum = weighted_sum; // save peak // Only count this sample towards the average if it is // above threshold (this attempts to keep the volume // from pumping up when there is no input signal). if (weighted_sum > nosig) { weighted_avgsum += weighted_sum; weighted_cnt++; } } } /* * 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) 1992-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include // class AudioHdr basic methods // This routine uses the byteorder network utilities to tell whether the // current process uses network byte order or not. AudioEndian AudioHdr::localByteOrder() const { short sTestHost; short sTestNetwork; static AudioEndian ae = UNDEFINED_ENDIAN; if (ae == UNDEFINED_ENDIAN) { sTestHost = MAXSHORT; sTestNetwork = htons(sTestHost); if (sTestNetwork != sTestHost) { ae = LITTLE_ENDIAN; } else { ae = BIG_ENDIAN; } } return (ae); } // Clear a header structure void AudioHdr:: Clear() { sample_rate = 0; samples_per_unit = 0; bytes_per_unit = 0; channels = 0; encoding = NONE; } // Return error code (TRUE) if header is inconsistent or unrecognizable // XXX - how do we support extensions? AudioError AudioHdr:: Validate() const { // Check for uninitialized fields if ((bytes_per_unit < 1) || (samples_per_unit < 1) || (sample_rate < 1) || (channels < 1)) return (AUDIO_ERR_BADHDR); switch (encoding) { case NONE: return (AUDIO_ERR_BADHDR); case LINEAR: if (bytes_per_unit > 4) return (AUDIO_ERR_PRECISION); if (samples_per_unit != 1) return (AUDIO_ERR_HDRINVAL); break; case FLOAT: if ((bytes_per_unit != 4) && (bytes_per_unit != 8)) return (AUDIO_ERR_PRECISION); if (samples_per_unit != 1) return (AUDIO_ERR_HDRINVAL); break; case ULAW: case ALAW: case G722: if (bytes_per_unit != 1) return (AUDIO_ERR_PRECISION); if (samples_per_unit != 1) return (AUDIO_ERR_HDRINVAL); break; case G721: case DVI: // G.721 is a 4-bit encoding if ((bytes_per_unit != 1) || (samples_per_unit != 2)) return (AUDIO_ERR_PRECISION); break; case G723: // G.723 has 3-bit and 5-bit flavors // 5-bit is currently unsupported if ((bytes_per_unit != 3) || (samples_per_unit != 8)) return (AUDIO_ERR_PRECISION); break; } return (AUDIO_SUCCESS); } // Convert a byte count into a floating-point time value, in seconds, // using the encoding specified in the audio header. Double AudioHdr:: Bytes_to_Time( off_t cnt) const // byte count { if ((cnt == AUDIO_UNKNOWN_SIZE) || (Validate() != AUDIO_SUCCESS)) return (AUDIO_UNKNOWN_TIME); // round off to nearest sample frame! cnt -= (cnt % (bytes_per_unit * channels)); return (Double) ((double)cnt / ((double)(channels * bytes_per_unit * sample_rate) / (double)samples_per_unit)); } // Convert a floating-point time value, in seconds, to a byte count for // the audio encoding in the audio header. Make sure that the byte count // or offset does not span a sample frame. off_t AudioHdr:: Time_to_Bytes( Double sec) const // time, in seconds { off_t offset; if (Undefined(sec) || (Validate() != AUDIO_SUCCESS)) return (AUDIO_UNKNOWN_SIZE); offset = (off_t)(0.5 + (sec * ((double)(channels * bytes_per_unit * sample_rate) / (double)samples_per_unit))); // Round down to the start of the nearest sample frame offset -= (offset % (bytes_per_unit * channels)); return (offset); } // Round a byte count down to a sample frame boundary. off_t AudioHdr:: Bytes_to_Bytes( off_t& cnt) const { if (Validate() != AUDIO_SUCCESS) return (AUDIO_UNKNOWN_SIZE); // Round down to the start of the nearest sample frame cnt -= (cnt % (bytes_per_unit * channels)); return (cnt); } // Round a byte count down to a sample frame boundary. size_t AudioHdr:: Bytes_to_Bytes( size_t& cnt) const { if (Validate() != AUDIO_SUCCESS) return (AUDIO_UNKNOWN_SIZE); // Round down to the start of the nearest sample frame cnt -= (cnt % (bytes_per_unit * channels)); return (cnt); } // Convert a count of sample frames into a floating-point time value, // in seconds, using the encoding specified in the audio header. Double AudioHdr:: Samples_to_Time( unsigned long cnt) const // sample frame count { if ((cnt == AUDIO_UNKNOWN_SIZE) || (Validate() != AUDIO_SUCCESS)) return (AUDIO_UNKNOWN_TIME); return ((Double)(((double)cnt * (double)samples_per_unit) / (double)sample_rate)); } // Convert a floating-point time value, in seconds, to a count of sample frames // for the audio encoding in the audio header. unsigned long AudioHdr:: Time_to_Samples( Double sec) const // time, in seconds { if (Undefined(sec) || (Validate() != AUDIO_SUCCESS)) return (AUDIO_UNKNOWN_SIZE); // Round down to sample frame boundary return ((unsigned long) (AUDIO_MINFLOAT + (((double)sec * (double)sample_rate) / (double)samples_per_unit))); } // Return the number of bytes in a sample frame for the audio encoding. unsigned int AudioHdr:: FrameLength() const { return (bytes_per_unit * channels); } /* * 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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 RackTop Systems. */ #include #include #include #include #include #define irint(d) ((int)(d)) // Convert a string to lowercase and return an allocated copy of it. // XXX - There really should be a string-insensitive 8-bit compare routine. static char * to_lowercase( char *str) { unsigned char *oldstr; unsigned char *newstr; int i; oldstr = (unsigned char *) str; newstr = new unsigned char [strlen(str) + 1]; for (i = 0; ; i++) { if (isupper(oldstr[i])) newstr[i] = tolower(oldstr[i]); else newstr[i] = oldstr[i]; if (oldstr[i] == '\0') break; } return ((char *)newstr); } // class AudioHdr parsing methods // Return a string containing the sample rate char *AudioHdr:: RateString() const { char *str; int ratek; int rateh; int prec; str = new char[32]; ratek = sample_rate / 1000; rateh = sample_rate % 1000; if (rateh == 0) { (void) sprintf(str, "%dkHz", ratek); } else { // scale down to print minimum digits after the decimal point prec = 3; if ((rateh % 10) == 0) { prec--; rateh /= 10; } if ((rateh % 10) == 0) { prec--; rateh /= 10; } (void) sprintf(str, "%d.%0*dkHz", ratek, prec, rateh); } return (str); } // Return a string containing the number of channels char *AudioHdr:: ChannelString() const { char *str; str = new char[32]; switch (channels) { case 1: (void) sprintf(str, "mono"); break; case 2: (void) sprintf(str, "stereo"); break; case 4: (void) sprintf(str, "quad"); break; default: (void) sprintf(str, "%d-channel", channels); break; } return (str); } // Return a string containing the encoding char *AudioHdr:: EncodingString() const { char *str; Double prec; int iprec; str = new char[64]; if ((samples_per_unit == 0) || (bytes_per_unit == 0) || (encoding == NONE)) { (void) sprintf(str, "???"); } else { // First encode precision iprec = (bytes_per_unit * 8) / samples_per_unit; prec = ((Double)bytes_per_unit * 8.) / (Double)samples_per_unit; if (prec == (Double) iprec) { (void) sprintf(str, "%d-bit ", iprec); } else { (void) sprintf(str, "%.1f-bit ", double(prec)); } // Then encode format switch (encoding) { case ULAW: // XXX - See bug 1121000 // XXX - (void) strcat(str, "μ-law"); (void) strcat(str, "u-law"); break; case ALAW: (void) strcat(str, "A-law"); break; case LINEAR: (void) strcat(str, "linear"); break; case FLOAT: (void) strcat(str, "float"); break; case G721: (void) strcat(str, "G.721 ADPCM"); break; case G722: (void) strcat(str, "G.722 ADPCM"); break; case G723: (void) strcat(str, "G.723 ADPCM"); break; case DVI: (void) strcat(str, "DVI ADPCM"); break; default: (void) strcat(str, "???"); break; } } return (str); } // Return a string containing the entire audio encoding char *AudioHdr:: FormatString() const { char *str; char *rate; char *chan; char *enc; str = new char[4 * 32]; enc = EncodingString(); rate = RateString(); chan = ChannelString(); (void) sprintf(str, "%s, %s, %s", enc, rate, chan); delete rate; delete chan; delete enc; return (str); } // Parse a string containing the sample rate AudioError AudioHdr:: RateParse( char *str) { static char *lib_khz = NULL; static char *lib_hz = NULL; double r; int rate; char khzbuf[16]; char *khz; if (str == NULL) return (AUDIO_ERR_BADARG); // Init i18n string translations if (lib_khz == NULL) { lib_khz = to_lowercase(_MGET_("khz")); lib_hz = to_lowercase(_MGET_("hz")); } // Scan for a number followed by an optional khz designator switch (sscanf(str, " %lf %15s", &r, khzbuf)) { case 2: // Process 'khz', if present, and fall through khz = to_lowercase(khzbuf); if ((strcmp(khz, "khz") == 0) || (strcmp(khz, "khertz") == 0) || (strcmp(khz, "kilohertz") == 0) || (strcmp(khz, "k") == 0) || (strcoll(khz, lib_khz) == 0)) { r *= 1000.; } else if ((strcmp(khz, "hz") != 0) && (strcmp(khz, "hertz") != 0) && (strcoll(khz, lib_hz) != 0)) { delete khz; return (AUDIO_ERR_BADARG); } delete khz; /* FALLTHROUGH */ case 1: rate = irint(r); break; default: return (AUDIO_ERR_BADARG); } // Check for reasonable bounds if ((rate <= 0) || (rate > 500000)) { return (AUDIO_ERR_BADARG); } sample_rate = (unsigned int) rate; return (AUDIO_SUCCESS); } // Parse a string containing the number of channels AudioError AudioHdr:: ChannelParse( char *str) { static char *lib_chan = NULL; static char *lib_mono = NULL; static char *lib_stereo = NULL; char cstrbuf[16]; char *cstr; char xtra[4]; int chan; // Init i18n string translations if (lib_chan == NULL) { lib_chan = to_lowercase(_MGET_("channel")); lib_mono = to_lowercase(_MGET_("mono")); lib_stereo = to_lowercase(_MGET_("stereo")); } // Parse a number, followed by optional "-channel" switch (sscanf(str, " %d %15s", &chan, cstrbuf)) { case 2: cstr = to_lowercase(cstrbuf); if ((strcmp(cstr, "-channel") != 0) && (strcmp(cstr, "-chan") != 0) && (strcoll(cstr, lib_chan) != 0)) { delete cstr; return (AUDIO_ERR_BADARG); } delete cstr; case 1: break; default: // If no number, look for reasonable keywords if (sscanf(str, " %15s %1s", cstrbuf, xtra) != 1) { return (AUDIO_ERR_BADARG); } cstr = to_lowercase(cstrbuf); if ((strcmp(cstr, "mono") == 0) || (strcmp(cstr, "monaural") == 0) || (strcoll(cstr, lib_mono) == 0)) { chan = 1; } else if ((strcmp(cstr, "stereo") == 0) || (strcmp(cstr, "dual") == 0) || (strcoll(cstr, lib_stereo) == 0)) { chan = 2; } else if ((strcmp(cstr, "quad") == 0) || (strcmp(cstr, "quadrophonic") == 0)) { chan = 4; } else { delete cstr; return (AUDIO_ERR_BADARG); } delete cstr; } if ((chan <= 0) || (chan > 256)) { return (AUDIO_ERR_BADARG); } channels = (unsigned int) chan; return (AUDIO_SUCCESS); } // Parse a string containing the audio encoding AudioError AudioHdr:: EncodingParse( char *str) { static char *lib_bit = NULL; static char *lib_ulaw = NULL; static char *lib_Alaw = NULL; static char *lib_linear = NULL; int i; char *p; char estrbuf[64]; char *estr; char xtrabuf[32]; char *xtra; char *xp; char buf[BUFSIZ]; char *cp; double prec; // Init i18n string translations if (lib_bit == NULL) { lib_bit = to_lowercase(_MGET_("bit")); lib_ulaw = to_lowercase(_MGET_("u-law")); lib_Alaw = to_lowercase(_MGET_("A-law")); lib_linear = to_lowercase(_MGET_("linear8")); lib_linear = to_lowercase(_MGET_("linear")); } // first copy and remove leading spaces (void) strncpy(buf, str, BUFSIZ); for (cp = buf; *cp == ' '; cp++) continue; // Delimit the precision. If there is one, parse it. prec = 0.; p = strchr(cp, ' '); if (p != NULL) { *p++ = '\0'; i = sscanf(cp, " %lf %15s", &prec, xtrabuf); if (i == 0) { return (AUDIO_ERR_BADARG); } if (i == 2) { // convert to lowercase and skip leading "-", if any xtra = to_lowercase(xtrabuf); xp = (xtra[0] == '-') ? &xtra[1] : &xtra[0]; if ((strcmp(xp, "bit") != 0) && (strcoll(xp, lib_bit) != 0)) { delete xtra; return (AUDIO_ERR_BADARG); } delete xtra; } if ((prec <= 0.) || (prec > 512.)) { return (AUDIO_ERR_BADARG); } // Don't be fooled by "8 bit" i = sscanf(p, " %15s", xtrabuf); if (i == 1) { // convert to lowercase and skip leading "-", if any xtra = to_lowercase(xtrabuf); xp = (xtra[0] == '-') ? &xtra[1] : &xtra[0]; if ((strcmp(xp, "bit") == 0) || (strcoll(xp, lib_bit) == 0)) { xp = strchr(p, ' '); if (xp != NULL) p = xp; else p += strlen(xtrabuf); } delete xtra; } } else { p = cp; } i = sscanf(p, " %31s %31s", estrbuf, xtrabuf); // If "adpcm" appended with a space, concatenate it if (i == 2) { xtra = to_lowercase(xtrabuf); if (strcmp(xtra, "adpcm") == 0) { (void) strcat(estrbuf, xtra); i = 1; } delete xtra; } if (i == 1) { estr = to_lowercase(estrbuf); if ((strcmp(estr, "ulaw") == 0) || (strcmp(estr, "u-law") == 0) || /* * NB: These characters are literal latin-1 MICRO SIGN * maintained for compatibility */ (strcmp(estr, "\xb5law") == 0) || (strcmp(estr, "\xb5-law") == 0) || (strcmp(estr, "mulaw") == 0) || (strcmp(estr, "mu-law") == 0) || (strcoll(estr, lib_ulaw) == 0)) { if ((prec != 0.) && (prec != 8.)) return (AUDIO_ERR_BADARG); encoding = ULAW; samples_per_unit = 1; bytes_per_unit = 1; } else if ((strcmp(estr, "alaw") == 0) || (strcmp(estr, "a-law") == 0) || (strcoll(estr, lib_Alaw) == 0)) { if ((prec != 0.) && (prec != 8.)) return (AUDIO_ERR_BADARG); encoding = ALAW; samples_per_unit = 1; bytes_per_unit = 1; } else if ((strcmp(estr, "linear") == 0) || (strcmp(estr, "lin") == 0) || (strcmp(estr, "pcm") == 0) || (strcoll(estr, lib_linear) == 0)) { if ((prec != 0.) && (prec != 8.) && (prec != 16.) && (prec != 24.) && (prec != 32.)) return (AUDIO_ERR_BADARG); if (prec == 0.) prec = 16.; encoding = LINEAR; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "linear8") == 0) || (strcmp(estr, "lin8") == 0) || (strcmp(estr, "pcm8") == 0)) { if ((prec != 0.) && (prec != 8.)) return (AUDIO_ERR_BADARG); prec = 8.; encoding = LINEAR; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "linear16") == 0) || (strcmp(estr, "lin16") == 0) || (strcmp(estr, "pcm16") == 0)) { if ((prec != 0.) && (prec != 16.)) return (AUDIO_ERR_BADARG); prec = 16.; encoding = LINEAR; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "linear24") == 0) || (strcmp(estr, "lin24") == 0) || (strcmp(estr, "pcm24") == 0)) { if ((prec != 0.) && (prec != 24.)) return (AUDIO_ERR_BADARG); prec = 24.; encoding = LINEAR; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "linear32") == 0) || (strcmp(estr, "lin32") == 0) || (strcmp(estr, "pcm32") == 0)) { if ((prec != 0.) && (prec != 32.)) return (AUDIO_ERR_BADARG); prec = 32.; encoding = LINEAR; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "float") == 0) || (strcmp(estr, "floatingpoint") == 0) || (strcmp(estr, "floating-point") == 0)) { if ((prec != 0.) && (prec != 32.) && (prec != 64.)) return (AUDIO_ERR_BADARG); if (prec == 0.) prec = 64.; encoding = FLOAT; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "float32") == 0) || (strcmp(estr, "floatingpoint32") == 0) || (strcmp(estr, "floating-point32") == 0)) { if ((prec != 0.) && (prec != 32.)) return (AUDIO_ERR_BADARG); prec = 32.; encoding = FLOAT; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "float64") == 0) || (strcmp(estr, "double") == 0) || (strcmp(estr, "floatingpoint64") == 0) || (strcmp(estr, "floating-point64") == 0)) { if ((prec != 0.) && (prec != 64.)) return (AUDIO_ERR_BADARG); prec = 64.; encoding = FLOAT; samples_per_unit = 1; bytes_per_unit = irint(prec / 8.); } else if ((strcmp(estr, "g.721") == 0) || (strcmp(estr, "g721") == 0) || (strcmp(estr, "g.721adpcm") == 0) || (strcmp(estr, "g721adpcm") == 0)) { if ((prec != 0.) && (prec != 4.)) return (AUDIO_ERR_BADARG); encoding = G721; samples_per_unit = 2; bytes_per_unit = 1; } else if ((strcmp(estr, "g.722") == 0) || (strcmp(estr, "g722") == 0) || (strcmp(estr, "g.722adpcm") == 0) || (strcmp(estr, "g722adpcm") == 0)) { if ((prec != 0.) && (prec != 8.)) return (AUDIO_ERR_BADARG); encoding = G722; samples_per_unit = 1; bytes_per_unit = 1; } else if ((strcmp(estr, "g.723") == 0) || (strcmp(estr, "g723") == 0) || (strcmp(estr, "g.723adpcm") == 0) || (strcmp(estr, "g723adpcm") == 0)) { if ((prec != 0.) && (prec != 3.) && (prec != 5.)) return (AUDIO_ERR_BADARG); if (prec == 0.) prec = 3.; encoding = G723; samples_per_unit = 8; bytes_per_unit = irint(prec); } else if ((strcmp(estr, "g.723-3") == 0) || (strcmp(estr, "g.723_3") == 0) || (strcmp(estr, "g.723.3") == 0) || (strcmp(estr, "g723-3") == 0) || (strcmp(estr, "g723_3") == 0) || (strcmp(estr, "g723.3") == 0)) { if ((prec != 0.) && (prec != 3.)) return (AUDIO_ERR_BADARG); prec = 3.; encoding = G723; samples_per_unit = 8; bytes_per_unit = irint(prec); } else if ((strcmp(estr, "g.723-5") == 0) || (strcmp(estr, "g.723_5") == 0) || (strcmp(estr, "g.723.5") == 0) || (strcmp(estr, "g723-5") == 0) || (strcmp(estr, "g723_5") == 0) || (strcmp(estr, "g723.5") == 0)) { if ((prec != 0.) && (prec != 5.)) return (AUDIO_ERR_BADARG); prec = 5.; encoding = G723; samples_per_unit = 8; bytes_per_unit = irint(prec); } else if ((strcmp(estr, "dvi") == 0) || (strcmp(estr, "dviadpcm") == 0)) { if ((prec != 0.) && (prec != 4.)) return (AUDIO_ERR_BADARG); encoding = DVI; samples_per_unit = 2; bytes_per_unit = 1; } else { delete estr; return (AUDIO_ERR_BADARG); } delete estr; } else { return (AUDIO_ERR_BADARG); } return (AUDIO_SUCCESS); } // Parse a string containing the comma-separated audio encoding // Format is: "enc, chan, rate" // XXX - some countries use comma instead of decimal point // so there may be a problem with "44,1 khz" AudioError AudioHdr:: FormatParse( char *str) { char *pstr; char *ptr; char *p; AudioHdr newhdr; AudioError err; pstr = new char[strlen(str) + 1]; (void) strcpy(pstr, str); ptr = pstr; // Delimit and parse the precision string p = strchr(ptr, ','); if (p == NULL) p = strchr(ptr, ' '); if (p == NULL) { err = AUDIO_ERR_BADARG; goto errret; } *p++ = '\0'; err = newhdr.EncodingParse(ptr); // Delimit and parse the sample rate string if (!err) { ptr = p; p = strchr(ptr, ','); if (p == NULL) p = strchr(ptr, ' '); if (p == NULL) { err = AUDIO_ERR_BADARG; goto errret; } *p++ = '\0'; err = newhdr.RateParse(ptr); } // Finally, parse the channels string if (!err) { err = newhdr.ChannelParse(p); } // Validate the resulting header if (!err) err = newhdr.Validate(); if (!err) *this = newhdr; errret: delete[] pstr; return (err); } /* * 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) 1990-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include // Generic Audio functions // Open an audio file readonly, and return an AudioList referencing it. AudioError Audio_OpenInputFile( const char *path, // input filename Audio*& ap) // returned AudioList pointer { AudioFile* inf; AudioList* lp; AudioError err; // Open file and decode the header inf = new AudioFile(path, (FileAccess)ReadOnly); if (inf == 0) return (AUDIO_UNIXERROR); err = inf->Open(); if (err) { delete inf; return (err); } // Create a list object and set it up to reference the file lp = new AudioList; if (lp == 0) { delete inf; return (AUDIO_UNIXERROR); } lp->Insert(inf); ap = lp; return (AUDIO_SUCCESS); } // Create an output file and copy an input stream to it. // If an error occurs during output, leave a partially written file. AudioError Audio_WriteOutputFile( const char *path, // output filename const AudioHdr& hdr, // output data header Audio* input) // input data stream { AudioFile* outf; AudioError err; // Create output file object outf = new AudioFile(path, (FileAccess)WriteOnly); if (outf == 0) return (AUDIO_UNIXERROR); // Set audio file header and create file if ((err = outf->SetHeader(hdr)) || (err = outf->Create())) { delete outf; return (err); } // Copy data to file err = AudioCopy(input, outf); // Close output file and clean up. If error, leave partial file. delete outf; return (err); } /* * 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 // class AudioList methods // class AudioListEntry Constructor AudioList::AudioListEntry:: AudioListEntry( Audio* obj): // audio object to point to aptr(0), next(0), prev(0) { // A NULL object is only valid in dummy entries, such as list heads newptr(obj); } // class AudioListEntry Destructor AudioList::AudioListEntry:: ~AudioListEntry() { newptr(0); if (next != 0) { next->prev = prev; } if (prev != 0) { prev->next = next; } } // Set a new extent pointer in an AudioListEntry void AudioList::AudioListEntry:: newptr( Audio* newa) // new object { if (aptr != 0) aptr->Dereference(); aptr = newa; if (aptr != 0) aptr->Reference(); } // Link object into list // Link in a new AudioListEntry void AudioList::AudioListEntry:: link( AudioListEntry* after) // link after this one { // Link object into list prev = after; next = after->next; after->next = this; if (next != 0) next->prev = this; } // Split an AudioListEntry at the specified offset void AudioList::AudioListEntry:: split( Double pos) // split offset { AudioExtent* e1; AudioExtent* e2; AudioListEntry* newp; // Create two extents referencing this object e1 = new AudioExtent(aptr, 0., pos); e2 = new AudioExtent(aptr, pos, AUDIO_UNKNOWN_TIME); // Set the current entry to the first extent and append the second newptr(e1); newp = new AudioListEntry(e2); newp->link(this); } // class AudioList Constructor AudioList:: AudioList( const char *local_name): // name string Audio(local_name), head(0) { } // class AudioList Destructor AudioList:: ~AudioList() { // Delete all entries in the list while (first() != 0) delete first(); } // Get the first entry in the list AudioList::AudioListEntry* AudioList:: first() const { return (head.next); } // Get the extent and offset corresponding to a given position // Return FALSE if no extents in list or position is beyond eof Boolean AudioList:: getposition( Double& pos, // target position (updated) AudioListEntry*& ep) const // returned extent pointer { Double length; // Position must be specified if (Undefined(pos)) return (FALSE); // Get the first extent in the list ep = first(); while (ep != 0) { // Get length of extent length = ep->aptr->GetLength(); if (Undefined(length)) { // Can't determine sizes beyond this return (TRUE); } // If the remaining offset is inside the current extent if (length > pos) return (TRUE); // Move on to the next extent pos -= length; ep = ep->next; } return (FALSE); } // Get the total length of the audio list Double AudioList:: GetLength() const { AudioListEntry* ep; Double sum; Double x; for (sum = 0., ep = first(); ep != 0; ep = ep->next) { // Accumulate times for each extent // Indeterminate extents screw up the calculation x = ep->aptr->GetLength(); if (Undefined(x)) return (x); sum += x; } return (sum); } // Construct a name for the list char *AudioList:: GetName() const { // XXX - construct a better name return (Audio::GetName()); } // Get the audio header for the current read position AudioHdr AudioList:: GetHeader() { return (GetHeader(ReadPosition())); } // Get the audio header for the given position AudioHdr AudioList:: GetHeader( Double pos) // position { AudioListEntry* ep; // Get the extent pointer for the given position if (!getposition(pos, ep)) { AudioHdr h; if (pos != 0.) { PrintMsg(_MGET_( "AudioHdr:GetHeader()...position is beyond eof"), Warning); return (h); } if ((ep = first()) != 0) return (ep->aptr->GetHeader()); return (h); } // Get the header for the proper offset in the extent return (ep->aptr->GetDHeader(pos)); } // Copy data from list into specified buffer. // No data format translation takes place. // The object's read position is not updated. // // Since list could contain extents of differing encodings, // clients should always use GetHeader() in combination with ReadData() AudioError AudioList:: ReadData( void* buf, // destination buffer address size_t& len, // buffer size (updated) Double& pos) // start position (updated) { AudioListEntry* ep; size_t cnt; Double off; Double newpos; AudioError err; // Save buffer size cnt = len; // Position must be valid if (Undefined(pos) || (pos < 0.) || ((int)cnt < 0)) return (RaiseError(AUDIO_ERR_BADARG)); // Loop until data is returned or error // XXX - THIS IS WRONG! THE HEADER COULD CHANGE! do { // Get the extent/offset for read position; clear return count len = 0; off = pos; if (!getposition(off, ep)) { err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } // Save the offset and read some data newpos = off; len = cnt; err = ep->aptr->ReadData(buf, len, newpos); // If no eof on this list entry, or no more data, we're done if ((err != AUDIO_EOF) || (err.sys != AUDIO_COPY_INPUT_EOF) || (ep->next == 0)) { break; } // Advance to next list entry // XXX - Is this problemmatic, too? pos += ep->aptr->GetLength() - off; } while (TRUE); // Update the byte count and position pos += (newpos - off); // XXX - recalculate? return (err); } // Write to AudioList is (currently) prohibited AudioError AudioList:: WriteData( void*, // destination buffer address size_t& len, // buffer size (updated) Double&) // start position (updated) { len = 0; return (RaiseError(AUDIO_ERR_NOEFFECT)); } // Insert an entry at the start AudioError AudioList:: Insert( Audio* obj) // object to insert { Double pos; // insertion offset, in seconds return (Insert(obj, pos = 0.)); } // Insert an entry at a specified position AudioError AudioList:: Insert( Audio* obj, // object to insert Double pos) // insertion offset, in seconds { AudioListEntry *ep; AudioListEntry *prev; // Find the insertion point if (first() == 0) { prev = &head; // this is the first extent } else { if (!getposition(pos, prev)) { if (pos == 0.) { // Append extent to end of list return (Append(obj)); } else { return (RaiseError(AUDIO_ERR_BADARG)); } } else if (pos != 0.) { // The insertion is in an extent, split it in two prev->split(pos); } else { // Insert before the current position prev = prev->prev; } } // Create object and link into list ep = new AudioListEntry(obj); ep->link(prev); return (AUDIO_SUCCESS); } // Append an entry to a list AudioError AudioList:: Append( Audio* obj) // object to append { AudioListEntry *ep; AudioListEntry *prev; // Find the last extent in the list for (prev = &head; prev->next != 0; prev = prev->next) continue; // Create object and link into list ep = new AudioListEntry(obj); ep->link(prev); return (AUDIO_SUCCESS); } // Copy routine for lists AudioError AudioList:: AsyncCopy( Audio* to, // audio object to copy to Double& frompos, // input pos (updated) Double& topos, // output pos (updated) Double& limit) // amt to copy (updated) { AudioListEntry* ep; Double svlim; Double newpos; Double off; AudioError err; svlim = limit; // Loop until data is returned or error // XXX - THIS IS WRONG! THE HEADER COULD CHANGE! do { // Get the extent and offset for the read position off = frompos; if (!getposition(off, ep)) { // nothing written, limit should reflect this limit = 0.0; err = AUDIO_EOF; err.sys = AUDIO_COPY_INPUT_EOF; return (err); } // Save the offset and do a copy newpos = off; limit = svlim; err = ep->aptr->AsyncCopy(to, newpos, topos, limit); // If no eof on this list entry, or no more data, we're done if ((err != AUDIO_EOF) || (err.sys != AUDIO_COPY_INPUT_EOF) || (ep->next == 0)) { break; } // Advance to next list entry // XXX - Is this problemmatic, too? frompos += ep->aptr->GetLength() - off; } while (TRUE); // Update the byte count and position frompos += (newpos - off); // XXX - recalculate? return (err); } /* * 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) 1990-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include // class AudioPipe methods // Constructor with file descriptor, mode, and optional name AudioPipe:: AudioPipe( const int desc, // file descriptor const FileAccess acc, // access mode const char *name_local): // name AudioUnixfile(name_local, acc) { setfd(desc); } // The create routine for pipes writes a file header AudioError AudioPipe:: Create() { AudioError err; // Was the header properly set? err = GetHeader().Validate(); if (err != AUDIO_SUCCESS) return (RaiseError(err)); // Open fd supplied by constructor if (!isfdset()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Write the file header with current (usually unknown) size err = encode_filehdr(); if (err != AUDIO_SUCCESS) { (void) close(getfd()); // If error, remove file setfd(-1); return (err); } // Set the actual output length to zero setlength(0.); return (AUDIO_SUCCESS); } // The open routine for pipes decodes the header AudioError AudioPipe:: Open() { AudioError err; // The constructor should have supplied a valid fd if (!isfdset()) return (RaiseError(AUDIO_ERR_NOEFFECT)); // Decode a file header err = decode_filehdr(); if (err != AUDIO_SUCCESS) { (void) close(getfd()); setfd(-1); return (err); } return (AUDIO_SUCCESS); } // Read data from underlying pipe into specified buffer. // No data format translation takes place. // Since there's no going back, the object's read position pointer is updated. AudioError AudioPipe:: ReadData( void* buf, // destination buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { AudioError err; char *tbuf; // current buffer pointer size_t remain; // number of bytes remaining size_t cnt; // accumulated number of bytes read tbuf = (char *)buf; remain = len; cnt = 0; // Pipes return short reads. If non-blocking i/o, try to read all. do { // Call the real routine err = AudioUnixfile::ReadData((void*)tbuf, remain, pos); // Update the object's read position if (!err) { (void) SetReadPosition(pos, Absolute); if (remain == 0) break; cnt += remain; tbuf += remain; remain = len - cnt; } } while (!err && (remain > 0) && GetBlocking()); len = cnt; if (len > 0) return (AUDIO_SUCCESS); return (err); } // Write data to underlying file from specified buffer. // No data format translation takes place. // Since there's no going back, the object's write position pointer is updated. AudioError AudioPipe:: WriteData( void* buf, // source buffer address size_t& len, // buffer length (updated) Double& pos) // start position (updated) { AudioError err; // Call the real routine err = AudioUnixfile::WriteData(buf, len, pos); // Update the object's write position if (err == AUDIO_SUCCESS) (void) SetWritePosition(pos, Absolute); return (err); } /* * 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 1991-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include // class AudioPipe methods // Constructor with file descriptor, mode, and optional name AudioRawPipe:: AudioRawPipe( const int desc, // file descriptor const FileAccess acc, // access mode const AudioHdr& hdr_local, // header const char *name_local, // name const off_t off // offset ):AudioPipe(desc, acc, name_local), offset(off) { isopened = FALSE; setfd(desc); SetHeader(hdr_local); } // The create routine for pipes writes a file header AudioError AudioRawPipe:: Create() { AudioError err; // Was the header properly set? err = GetHeader().Validate(); if (err != AUDIO_SUCCESS) return (RaiseError(err)); // Open fd supplied by constructor if (!isfdset() || opened()) { return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); } // set flag for opened() test isopened = TRUE; // Set the actual output length to zero setlength(0.); return (AUDIO_SUCCESS); } // The open routine for raw pipes validates the header and // init's the read pos to offset and sets the opened flag. AudioError AudioRawPipe:: Open() { AudioError err; struct stat st; // The constructor should have supplied a valid fd // If fd is not open, or file header already decoded, skip it if (!isfdset() || opened()) return (RaiseError(AUDIO_ERR_NOEFFECT, Warning)); // Stat the file, to see if it is a regular file if (fstat(getfd(), &st) < 0) return (RaiseError(AUDIO_UNIXERROR)); // check validity of file header err = GetHeader().Validate(); if (err != AUDIO_SUCCESS) { (void) close(getfd()); setfd(-1); return (err); } // Only trust the file size for regular files if (S_ISREG(st.st_mode)) { // for raw files - no hdr, so it's the whole file minus // the offset. setlength(GetHeader().Bytes_to_Time(st.st_size - offset)); } else { // don't know ... setlength(AUDIO_UNKNOWN_TIME); } // set flag for opened() test isopened = TRUE; err = SetOffset(offset); // reset logical position to 0.0, since this is, in effect, // the beginning of the file. SetReadPosition(0.0, Absolute); return (err); } Boolean AudioRawPipe:: opened() const { return (isopened); } AudioError AudioRawPipe:: SetOffset(off_t val) { off_t setting = 0; AudioError err; // only read only files for now if (GetAccess().Writeable()) { return (AUDIO_ERR_NOEFFECT); } // only allow this if we haven't read anything yet (i.e. current // position is 0). if (ReadPosition() != 0.) { return (AUDIO_ERR_NOEFFECT); } if ((err = seekread(GetHeader().Bytes_to_Time(val), setting)) != AUDIO_SUCCESS) { return (err); } // this should *never* happen 'cause seekread just sets setting // to GetHeader().Time_to_Bytes.... if (setting != val) { // don't really know what error is apropos for this. return (AUDIO_ERR_BADFRAME); } offset = val; return (AUDIO_SUCCESS); } off_t AudioRawPipe:: GetOffset() const { return (offset); } /* * 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) 1990-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include // class AudioStream methods // Constructor AudioStream:: AudioStream( const char *path): // pathname Audio(path), length(AUDIO_UNKNOWN_TIME) { } // Set the header structure, even if it is already set AudioError AudioStream:: updateheader( const AudioHdr& h) // new header to set { AudioError err; // Validate the header before stuffing it in err = h.Validate(); if (err != AUDIO_SUCCESS) return (RaiseError(err)); // Copy in the new header hdr = h; return (AUDIO_SUCCESS); } // Set the header structure AudioError AudioStream:: SetHeader( const AudioHdr& h) // new header to set { // Once the header is set and the file is open, it cannot be changed // XXX - hdrset test might be redundant? if (hdrset() && opened()) return (RaiseError(AUDIO_ERR_NOEFFECT)); return (updateheader(h)); } // Check the endian nature of the data, and change if necessary. AudioError AudioStream:: coerceEndian(unsigned char *buf, size_t len, AudioEndian endian) { // If the stream isn't endian sensitive, don't bother. if (! isEndianSensitive()) return (AUDIO_SUCCESS); if (hdr.endian == endian) { #ifdef DEBUG AUDIO_DEBUG((1, "AudioStream: endian swap not needed, byte" "order OK.\n")); #endif return (AUDIO_SUCCESS); } // The endians don't match, lets swap bytes. unsigned char chTemp; for (int i = 0; i < len - 1; i += 2) { chTemp = buf[i]; buf[i] = buf[i + 1]; buf[i+1] = chTemp; } #ifdef DEBUG AUDIO_DEBUG((1, "AudioStream: converting endian.\n")); // printf("AudioStream: converting endian.\n"); #endif return (AUDIO_SUCCESS); } // This routine knows if the current format is endian sensitive. Boolean AudioStream::isEndianSensitive() const { // Only these encodings have endian problems. if (hdr.encoding == LINEAR || hdr.encoding == FLOAT) return (TRUE); return (FALSE); } /* * 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 // This is a conversion class for channel conversions // It handles mono->multi-channel and multi-channel->mono (mixing) // class AudioTypeChannel methods // Constructor AudioTypeChannel:: AudioTypeChannel() { } // Destructor AudioTypeChannel:: ~AudioTypeChannel() { } // Test conversion possibilities. // Return TRUE if conversion to/from the specified type is possible. Boolean AudioTypeChannel:: CanConvert( AudioHdr /* h */) const // target header { // XXX - This is misleading. Multi-channel->mono conversions // must be linear format, but mono->multi-channel is // ok in any format. return (TRUE); } // Convert buffer to the specified type // May replace the buffer with a new one, if necessary AudioError AudioTypeChannel:: Convert( AudioBuffer*& inbuf, // data buffer to process AudioHdr outhdr) // target header { AudioBuffer* outbuf; AudioHdr inhdr; AudioHdr newhdr; Double length; size_t nsamps; size_t nbytes; int i; int j; int k; int chans; char *cin; char *cout; short *sin; short *sout; AudioError err; long smix; inhdr = inbuf->GetHeader(); length = inbuf->GetLength(); // Make sure we're not being asked to do the impossible or trivial if ((err = inhdr.Validate())) return (err); if ((inhdr.sample_rate != outhdr.sample_rate) || (inhdr.encoding != outhdr.encoding) || (inhdr.samples_per_unit != outhdr.samples_per_unit) || (inhdr.bytes_per_unit != outhdr.bytes_per_unit)) return (AUDIO_ERR_HDRINVAL); if (inhdr.channels == outhdr.channels) return (AUDIO_SUCCESS); if ((inhdr.channels != 1) && (outhdr.channels != 1)) return (AUDIO_ERR_HDRINVAL); if (Undefined(length)) return (AUDIO_ERR_BADARG); // setup header for output buffer newhdr = inhdr; newhdr.channels = outhdr.channels; // XXX - If multi-channel -> mono, must be linear to mix // We need to test for this before trying the conversion! if ((inhdr.channels > 1) && (newhdr.channels == 1)) { if ((inhdr.encoding != LINEAR) || (inhdr.bytes_per_unit > 2)) return (AUDIO_ERR_HDRINVAL); } // Allocate a new buffer outbuf = new AudioBuffer(length, "(Channel conversion buffer)"); if (outbuf == 0) return (AUDIO_UNIXERROR); err = outbuf->SetHeader(newhdr); if (err != AUDIO_SUCCESS) { delete outbuf; return (err); } // Get the number of sample frames and the size of each nsamps = (size_t)inhdr.Time_to_Samples(length); nbytes = (size_t)inhdr.FrameLength(); chans = inhdr.channels; // multi-channel -> mono conversion if ((chans > 1) && (newhdr.channels == 1)) { switch (inhdr.bytes_per_unit) { case 1: cin = (char *)inbuf->GetAddress(); cout = (char *)outbuf->GetAddress(); for (i = 0; i < nsamps; i++) { smix = 0; for (j = 0; j < chans; j++) { smix += *cin++; } if (smix < -0x7f) { smix = -0x7f; } else if (smix > 0x7f) { smix = 0x7f; } *cout++ = (char)smix; } break; case 2: sin = (short *)inbuf->GetAddress(); sout = (short *)outbuf->GetAddress(); for (i = 0; i < nsamps; i++) { smix = 0; for (j = 0; j < chans; j++) { smix += *sin++; } if (smix < -0x7fff) { smix = -0x7fff; } else if (smix > 0x7fff) { smix = 0x7fff; } *sout++ = (short)smix; } break; default: err = AUDIO_ERR_HDRINVAL; } } else if ((chans == 1) && (newhdr.channels > 1)) { // mono -> multi-channel chans = newhdr.channels; cin = (char *)inbuf->GetAddress(); cout = (char *)outbuf->GetAddress(); // XXX - this could be optimized by special-casing stuff for (i = 0; i < nsamps; i++) { for (j = 0; j < chans; j++) { for (k = 0; k < nbytes; k++) *cout++ = cin[k]; } cin += nbytes; } } if (err) { if (outbuf != inbuf) delete outbuf; return (err); } // This will delete the buffer inbuf->Reference(); inbuf->Dereference(); // Set the valid data length outbuf->SetLength(length); inbuf = outbuf; return (AUDIO_SUCCESS); } AudioError AudioTypeChannel:: Flush( AudioBuffer*& /* buf */) { return (AUDIO_SUCCESS); } /* * 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) 1992-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include // class AudioTypeG72X methods // G.721 & G.723 compress/decompress // Constructor AudioTypeG72X:: AudioTypeG72X() { initialized = FALSE; } // Destructor AudioTypeG72X:: ~AudioTypeG72X() { } // Test conversion possibilities. // Return TRUE if conversion to/from the specified type is possible. Boolean AudioTypeG72X:: CanConvert( AudioHdr h) const // target header { // g72x conversion code handles mono 16-bit pcm, ulaw, alaw if (h.channels != 1) return (FALSE); switch (h.encoding) { case LINEAR: if ((h.samples_per_unit != 1) || (h.bytes_per_unit != 2)) return (FALSE); break; case ALAW: case ULAW: if ((h.samples_per_unit != 1) || (h.bytes_per_unit != 1)) return (FALSE); break; case G721: if ((h.samples_per_unit != 2) || (h.bytes_per_unit != 1)) return (FALSE); break; case G723: if (h.samples_per_unit != 8) return (FALSE); // XXX - 5-bit G.722 not supported yet if (h.bytes_per_unit != 3) return (FALSE); break; case FLOAT: default: return (FALSE); } return (TRUE); } // Convert buffer to the specified type // May replace the buffer with a new one, if necessary AudioError AudioTypeG72X:: Convert( AudioBuffer*& inbuf, // data buffer to process AudioHdr outhdr) // target header { AudioBuffer* outbuf; AudioHdr inhdr; Audio_hdr chdr; // C struct for g72x convert code Double length; Double pad; size_t nbytes; int cnt; unsigned char *inptr; unsigned char *outptr; AudioError err; inhdr = inbuf->GetHeader(); length = inbuf->GetLength(); if (Undefined(length)) { return (AUDIO_ERR_BADARG); } // Make sure we're not being asked to do the impossible if ((err = inhdr.Validate()) || (err = outhdr.Validate())) { return (err); } if (!CanConvert(inhdr) || !CanConvert(outhdr) || (inhdr.sample_rate != outhdr.sample_rate) || (inhdr.channels != outhdr.channels)) return (AUDIO_ERR_HDRINVAL); // if conversion is a no-op, just return success if ((inhdr.encoding == outhdr.encoding) && (inhdr.bytes_per_unit == outhdr.bytes_per_unit)) { return (AUDIO_SUCCESS); } // Add some padding to the output buffer pad = outhdr.Samples_to_Time( 4 * outhdr.bytes_per_unit * outhdr.channels); // Allocate a new buffer outbuf = new AudioBuffer(length + pad, "(G72x conversion buffer)"); if (outbuf == 0) return (AUDIO_UNIXERROR); err = outbuf->SetHeader(outhdr); if (err != AUDIO_SUCCESS) { delete outbuf; return (err); } // Convert from the input type to the output type inptr = (unsigned char *)inbuf->GetAddress(); outptr = (unsigned char *)outbuf->GetAddress(); nbytes = (size_t)inhdr.Time_to_Bytes(length); if (nbytes == 0) goto cleanup; switch (inhdr.encoding) { case ALAW: case ULAW: case LINEAR: switch (outhdr.encoding) { case G721: chdr = (Audio_hdr)inhdr; if (!initialized) { g721_init_state(&g72x_state); initialized = TRUE; } err = g721_encode((void*)inptr, nbytes, &chdr, outptr, &cnt, &g72x_state); length = outhdr.Bytes_to_Time(cnt); break; case G723: chdr = (Audio_hdr)inhdr; if (!initialized) { g723_init_state(&g72x_state); initialized = TRUE; } err = g723_encode((void*)inptr, nbytes, &chdr, outptr, &cnt, &g72x_state); length = outhdr.Bytes_to_Time(cnt); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case G721: switch (outhdr.encoding) { case ALAW: case ULAW: case LINEAR: chdr = (Audio_hdr)outhdr; if (!initialized) { g721_init_state(&g72x_state); initialized = TRUE; } err = g721_decode(inptr, nbytes, &chdr, (void*)outptr, &cnt, &g72x_state); length = outhdr.Samples_to_Time(cnt); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case G723: switch (outhdr.encoding) { case ALAW: case ULAW: case LINEAR: chdr = (Audio_hdr)outhdr; if (!initialized) { g723_init_state(&g72x_state); initialized = TRUE; } err = g723_decode(inptr, nbytes, &chdr, (void*)outptr, &cnt, &g72x_state); length = outhdr.Samples_to_Time(cnt); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } if (err) { if (outbuf != inbuf) delete outbuf; return (err); } cleanup: // This will delete the buffer inbuf->Reference(); inbuf->Dereference(); // Set the valid data length outbuf->SetLength(length); inbuf = outbuf; return (AUDIO_SUCCESS); } // Flush out any leftover state, appending to supplied buffer AudioError AudioTypeG72X:: Flush( AudioBuffer*& outbuf) { AudioHdr h; Double pos; size_t cnt; AudioError err; unsigned char tmpbuf[32]; if (!initialized) return (AUDIO_SUCCESS); initialized = FALSE; if (outbuf == NULL) return (AUDIO_SUCCESS); h = outbuf->GetHeader(); switch (h.encoding) { case G721: case G723: switch (h.encoding) { case G721: err = g721_encode(NULL, 0, NULL, tmpbuf, (int *)&cnt, &g72x_state); break; case G723: err = g723_encode(NULL, 0, NULL, tmpbuf, (int *)&cnt, &g72x_state); break; } // Copy to the supplied buffer if (cnt > 0) { pos = outbuf->GetLength(); err = outbuf->AppendData(tmpbuf, cnt, pos); if (err) return (err); } break; default: break; } return (AUDIO_SUCCESS); } /* * 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) 1992-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include // This is a conversion class for channel multiplex/demultiplex // class AudioTypeMux methods // Constructor AudioTypeMux:: AudioTypeMux() { } // Destructor AudioTypeMux:: ~AudioTypeMux() { } // Test conversion possibilities. // Return TRUE if conversion to/from the specified type is possible. Boolean AudioTypeMux:: CanConvert( AudioHdr /* h */) const // target header { // XXX - The test is whether we're converting 1->many or many->1 // This routine needs a to/from argument. // XXX - What if the format doesn't have fixed-size sample units? return (TRUE); } // Multiplex or demultiplex. // The buffer pointer should be a NULL-terminated array of buffers if 1-channel AudioError AudioTypeMux:: Convert( AudioBuffer*& inbuf, // data buffer to process AudioHdr outhdr) // target header { AudioBuffer* outbuf; AudioBuffer** multibuf; AudioHdr inhdr; Double length; unsigned int channels; size_t nsamps; size_t nbytes; size_t unitsz; unsigned char **inptrs; unsigned char *in; unsigned char *out; int i; int j; int k; AudioError err; channels = outhdr.channels; if (channels == 1) { inhdr = inbuf->GetHeader(); // Demux multi-channel data length = inbuf->GetLength(); } else { multibuf = (AudioBuffer**) inbuf; // Mux multiple buffers inhdr = multibuf[0]->GetHeader(); length = multibuf[0]->GetLength(); } // Make sure we're not being asked to do the impossible or trivial if ((err = inhdr.Validate())) return (err); if ((inhdr.sample_rate != outhdr.sample_rate) || (inhdr.encoding != outhdr.encoding) || (inhdr.samples_per_unit != outhdr.samples_per_unit) || (inhdr.bytes_per_unit != outhdr.bytes_per_unit)) return (AUDIO_ERR_HDRINVAL); if (inhdr.channels == outhdr.channels) return (AUDIO_SUCCESS); if ((inhdr.channels != 1) && (outhdr.channels != 1)) return (AUDIO_ERR_HDRINVAL); if (Undefined(length)) return (AUDIO_ERR_BADARG); // Get the number of sample frames and the size of each nsamps = (size_t)inhdr.Time_to_Samples(length); nbytes = (size_t)inhdr.FrameLength(); unitsz = (size_t)inhdr.bytes_per_unit; // Figure out if we're multiplexing or demultiplexing if (channels == 1) { // Demultiplex multi-channel data into several mono channels // Allocate buffer pointer array and each buffer channels = inhdr.channels; multibuf = (AudioBuffer**) calloc((channels + 1), sizeof (AudioBuffer*)); for (i = 0; i < channels; i++) { multibuf[i] = new AudioBuffer(length, "(Demultiplex conversion buffer)"); if (multibuf[i] == 0) { err = AUDIO_UNIXERROR; goto cleanup; } err = multibuf[i]->SetHeader(outhdr); if (err != AUDIO_SUCCESS) { delete multibuf[i]; cleanup: while (--i >= 0) { delete multibuf[i]; } delete multibuf; return (err); } } multibuf[i] = NULL; for (i = 0; i < channels; i++) { // Get output pointer and input channel pointer out = (unsigned char *)multibuf[i]->GetAddress(); in = (unsigned char *)inbuf->GetAddress(); in += (i * unitsz); // Copy a sample unit and bump the input pointer for (j = 0; j < nsamps; j++) { for (k = 0; k < unitsz; k++) { *out++ = *in++; } in += ((channels - 1) * unitsz); } // Set the valid data length multibuf[i]->SetLength(length); } // Release the input buffer inbuf->Reference(); inbuf->Dereference(); // Return the array pointer (callers beware!) inbuf = (AudioBuffer*) multibuf; } else { // Multiplex several mono channels into multi-channel data // Allocate an output buffer outbuf = new AudioBuffer(length, "(Multiplex conversion buffer)"); if (outbuf == 0) return (AUDIO_UNIXERROR); err = outbuf->SetHeader(outhdr); if (err != AUDIO_SUCCESS) { delete outbuf; return (err); } // Verify the input pointer is an array of buffer pointers multibuf = (AudioBuffer**) inbuf; for (channels = 0; ; channels++) { // Look for NULL termination if (multibuf[channels] == NULL) break; if (!multibuf[channels]->isBuffer()) return (AUDIO_ERR_BADARG); } if (channels != outhdr.channels) return (AUDIO_ERR_BADARG); // Allocate a bunch of input pointers inptrs = (unsigned char **) calloc(channels, sizeof (unsigned char *)); for (i = 0; i < channels; i++) { inptrs[i] = (unsigned char *) multibuf[i]->GetAddress(); } // Get output pointer out = (unsigned char *)outbuf->GetAddress(); for (i = 0; i < nsamps; i++) { // Copy a sample frame from each input buffer for (j = 0; j < channels; j++) { in = inptrs[j]; for (k = 0; k < nbytes; k++) { *out++ = *in++; } inptrs[j] = in; } } // Set the valid data length outbuf->SetLength(length); // Release the input buffers and pointer arrays for (i = 0; i < channels; i++) { multibuf[i]->Reference(); multibuf[i]->Dereference(); multibuf[i] = NULL; } delete multibuf; delete inptrs; // Set the valid data length and return the new pointer outbuf->SetLength(length); inbuf = outbuf; } return (AUDIO_SUCCESS); } AudioError AudioTypeMux:: Flush( AudioBuffer*& /* buf */) { return (AUDIO_SUCCESS); } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #define irint(d) ((int)d) // class AudioTypePcm methods // Constructor AudioTypePcm:: AudioTypePcm() { // Set up fixed header values; the rest are negotiable hdr.Clear(); hdr.samples_per_unit = 1; hdr.encoding = LINEAR; } // Test conversion possibilities. // Return TRUE if conversion to/from the specified type is possible. Boolean AudioTypePcm:: CanConvert( AudioHdr h) const // target header { if (h.samples_per_unit != 1) return (FALSE); switch (h.encoding) { case LINEAR: switch (h.bytes_per_unit) { case 1: case 2: case 4: break; default: return (FALSE); } break; case FLOAT: switch (h.bytes_per_unit) { case 4: case 8: break; default: return (FALSE); } break; case ULAW: case ALAW: switch (h.bytes_per_unit) { case 1: break; default: return (FALSE); } break; default: return (FALSE); } return (TRUE); } // Clip most negative values and convert to floating-point inline double AudioTypePcm:: char2dbl(char B) { return ((unsigned char)B == 0x80 ? -1. : (double)B / 127.); } inline double AudioTypePcm:: short2dbl(short S) { return ((unsigned short)S == 0x8000 ? -1. : (double)S / 32767.); } inline double AudioTypePcm:: long2dbl(long L) { return ((unsigned long)L == 0x80000000 ? -1. : (double)L / 2147483647.); } // Convert floating-point to integer, scaled by the appropriate constant inline long AudioTypePcm:: dbl2long(double D, long C) { return (D >= 1. ? C : D <= -1. ? -C : (long)irint(D * (double)C)); } // Simple type conversions inline void AudioTypePcm:: char2short(char *&F, short *&T) { *T++ = ((short)*F++) << 8; } inline void AudioTypePcm:: char2long(char *&F, long *&T) { *T++ = ((long)*F++) << 24; } inline void AudioTypePcm:: char2float(char *&F, float *&T) { *T++ = char2dbl(*F++); } inline void AudioTypePcm:: char2double(char *&F, double *&T) { *T++ = char2dbl(*F++); } inline void AudioTypePcm:: char2ulaw(char *&F, ulaw *&T) { *T++ = audio_c2u(*F); F++; } inline void AudioTypePcm:: char2alaw(char *&F, alaw *&T) { *T++ = audio_c2a(*F); F++; } inline void AudioTypePcm:: short2char(short *&F, char *&T) { *T++ = (char)(*F++ >> 8); } inline void AudioTypePcm:: short2long(short *&F, long *&T) { *T++ = ((long)*F++) << 16; } inline void AudioTypePcm:: short2float(short *&F, float *&T) { *T++ = short2dbl(*F++); } inline void AudioTypePcm:: short2double(short *&F, double *&T) { *T++ = short2dbl(*F++); } inline void AudioTypePcm:: short2ulaw(short *&F, ulaw *&T) { *T++ = audio_s2u(*F); F++; } inline void AudioTypePcm:: short2alaw(short *&F, alaw *&T) { *T++ = audio_s2a(*F); F++; } inline void AudioTypePcm:: long2char(long *&F, char *&T) { *T++ = (char)(*F++ >> 24); } inline void AudioTypePcm:: long2short(long *&F, short *&T) { *T++ = (short)(*F++ >> 16); } inline void AudioTypePcm:: long2float(long *&F, float *&T) { *T++ = long2dbl(*F++); } inline void AudioTypePcm:: long2double(long *&F, double *&T) { *T++ = long2dbl(*F++); } inline void AudioTypePcm:: long2ulaw(long *&F, ulaw *&T) { *T++ = audio_l2u(*F); F++; } inline void AudioTypePcm:: long2alaw(long *&F, alaw *&T) { *T++ = audio_l2a(*F); F++; } inline void AudioTypePcm:: float2char(float *&F, char *&T) { *T++ = (char)dbl2long(*F++, 127); } inline void AudioTypePcm:: float2short(float *&F, short *&T) { *T++ = (short)dbl2long(*F++, 32767); } inline void AudioTypePcm:: float2long(float *&F, long *&T) { *T++ = dbl2long(*F++, 2147483647); } inline void AudioTypePcm:: float2double(float *&F, double *&T) { *T++ = *F++; } inline void AudioTypePcm:: float2ulaw(float *&F, ulaw *&T) { *T++ = audio_s2u(dbl2long(*F++, 32767)); } inline void AudioTypePcm:: float2alaw(float *&F, alaw *&T) { *T++ = audio_s2a(dbl2long(*F++, 32767)); } inline void AudioTypePcm:: double2char(double *&F, char *&T) { *T++ = (char)dbl2long(*F++, 127); } inline void AudioTypePcm:: double2short(double *&F, short *&T) { *T++ = (short)dbl2long(*F++, 32767); } inline void AudioTypePcm:: double2long(double *&F, long *&T) { *T++ = dbl2long(*F++, 2147483647); } inline void AudioTypePcm:: double2float(double *&F, float *&T) { *T++ = *F++; } inline void AudioTypePcm:: double2ulaw(double *&F, ulaw *&T) { *T++ = audio_s2u(dbl2long(*F++, 32767)); } inline void AudioTypePcm:: double2alaw(double *&F, alaw *&T) { *T++ = audio_s2a(dbl2long(*F++, 32767)); } inline void AudioTypePcm:: ulaw2char(ulaw *&F, char *&T) { *T++ = audio_u2c(*F); F++; } inline void AudioTypePcm:: ulaw2alaw(ulaw *&F, alaw *&T) { *T++ = audio_u2a(*F); F++; } inline void AudioTypePcm:: ulaw2short(ulaw *&F, short *&T) { *T++ = audio_u2s(*F); F++; } inline void AudioTypePcm:: ulaw2long(ulaw *&F, long *&T) { *T++ = audio_u2l(*F); F++; } inline void AudioTypePcm:: ulaw2float(ulaw *&F, float *&T) { *T++ = short2dbl(audio_u2s(*F)); F++; } inline void AudioTypePcm:: ulaw2double(ulaw *&F, double *&T) { *T++ = short2dbl(audio_u2s(*F)); F++; } inline void AudioTypePcm:: alaw2char(alaw *&F, char *&T) { *T++ = audio_a2c(*F); F++; } inline void AudioTypePcm:: alaw2short(alaw *&F, short *&T) { *T++ = audio_a2s(*F); F++; } inline void AudioTypePcm:: alaw2long(alaw *&F, long *&T) { *T++ = audio_a2l(*F); F++; } inline void AudioTypePcm:: alaw2float(alaw *&F, float *&T) { *T++ = short2dbl(audio_a2s(*F)); F++; } inline void AudioTypePcm:: alaw2double(alaw *&F, double *&T) { *T++ = short2dbl(audio_a2s(*F)); F++; } inline void AudioTypePcm:: alaw2ulaw(alaw*& F, ulaw*& T) { *T++ = audio_a2u(*F); F++; } // Convert buffer to the specified type // May replace the buffer with a new one, if necessary AudioError AudioTypePcm:: Convert( AudioBuffer*& inbuf, // data buffer to process AudioHdr outhdr) // target header { AudioBuffer* outbuf; AudioHdr inhdr; Double length; size_t frames; void* inptr; void* outptr; AudioError err; inhdr = inbuf->GetHeader(); length = inbuf->GetLength(); if (Undefined(length)) return (AUDIO_ERR_BADARG); // Make sure we're not being asked to do the impossible // XXX - how do we deal with multi-channel data?? // XXX - need a better error code if ((err = inhdr.Validate()) || (err = outhdr.Validate())) return (err); if ((inhdr.sample_rate != outhdr.sample_rate) || (inhdr.samples_per_unit != outhdr.samples_per_unit) || (inhdr.samples_per_unit != 1) || (inhdr.channels != outhdr.channels)) return (AUDIO_ERR_HDRINVAL); // If the buffer is not referenced, and the target size is no bigger // than the current size, the conversion can be done in place if (!inbuf->isReferenced() && (outhdr.bytes_per_unit <= inhdr.bytes_per_unit)) { outbuf = inbuf; } else { // Allocate a new buffer outbuf = new AudioBuffer(length, "(PCM conversion buffer)"); if (outbuf == 0) return (AUDIO_UNIXERROR); err = outbuf->SetHeader(outhdr); if (err != AUDIO_SUCCESS) { delete outbuf; return (err); } } // Convert from the input type to the output type inptr = inbuf->GetAddress(); outptr = outbuf->GetAddress(); frames = (size_t)inhdr.Time_to_Samples(length) * inhdr.channels; // Define macro to copy with no data conversion #define COPY(N) if (inptr != outptr) memcpy(outptr, inptr, frames * N) // Define macro to translate a buffer // XXX - The temporary pointers are necessary to get the updates // token catenation different for ANSI cpp v.s. old cpp. #ifdef __STDC__ #define MOVE(F, T) { \ F* ip = (F*)inptr; T* op = (T*)outptr; \ while (frames-- > 0) F ## 2 ## T(ip, op); \ } #else #define MOVE(F, T) { \ F* ip = (F*)inptr; T* op = (T*)outptr; \ while (frames-- > 0) F /* */ 2 /* */ T(ip, op);\ } #endif switch (inhdr.encoding) { case LINEAR: switch (outhdr.encoding) { case LINEAR: // Convert linear to linear switch (inhdr.bytes_per_unit) { case 1: switch (outhdr.bytes_per_unit) { case 1: COPY(1); break; case 2: MOVE(char, short); break; case 4: MOVE(char, long); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case 2: switch (outhdr.bytes_per_unit) { case 1: MOVE(short, char); break; case 2: COPY(2); break; case 4: MOVE(short, long); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case 4: switch (outhdr.bytes_per_unit) { case 1: MOVE(long, char); break; case 2: MOVE(long, short); break; case 4: COPY(4); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case FLOAT: // Convert linear to float switch (inhdr.bytes_per_unit) { case 1: switch (outhdr.bytes_per_unit) { case 4: MOVE(char, float); break; case 8: MOVE(char, double); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case 2: switch (outhdr.bytes_per_unit) { case 4: MOVE(short, float); break; case 8: MOVE(short, double); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case 4: switch (outhdr.bytes_per_unit) { case 4: MOVE(long, float); break; case 8: MOVE(long, double); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ULAW: // Convert linear to u-law switch (inhdr.bytes_per_unit) { case 1: MOVE(char, ulaw); break; case 2: MOVE(short, ulaw); break; case 4: MOVE(long, ulaw); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ALAW: // Convert linear to a-law switch (inhdr.bytes_per_unit) { case 1: MOVE(char, alaw); break; case 2: MOVE(short, alaw); break; case 4: MOVE(long, alaw); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case FLOAT: switch (outhdr.encoding) { case LINEAR: // Convert float to linear switch (inhdr.bytes_per_unit) { case 4: switch (outhdr.bytes_per_unit) { case 1: MOVE(float, char); break; case 2: MOVE(float, short); break; case 4: MOVE(float, long); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case 8: switch (outhdr.bytes_per_unit) { case 1: MOVE(double, char); break; case 2: MOVE(double, short); break; case 4: MOVE(double, long); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case FLOAT: // Convert float to float switch (inhdr.bytes_per_unit) { case 4: switch (outhdr.bytes_per_unit) { case 4: COPY(4); break; case 8: MOVE(float, double); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case 8: switch (outhdr.bytes_per_unit) { case 4: MOVE(double, float); break; case 8: COPY(8); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ULAW: // Convert float to u-law switch (inhdr.bytes_per_unit) { case 4: MOVE(float, ulaw); break; case 8: MOVE(double, ulaw); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ALAW: // Convert float to a-law switch (inhdr.bytes_per_unit) { case 4: MOVE(float, alaw); break; case 8: MOVE(double, alaw); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ULAW: switch (outhdr.encoding) { case LINEAR: // Convert ulaw to linear switch (outhdr.bytes_per_unit) { case 1: MOVE(ulaw, char); break; case 2: MOVE(ulaw, short); break; case 4: MOVE(ulaw, long); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case FLOAT: // Convert ulaw to float switch (outhdr.bytes_per_unit) { case 4: MOVE(ulaw, float); break; case 8: MOVE(ulaw, double); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ULAW: // Convert ulaw to u-law COPY(1); break; case ALAW: // Convert ulaw to a-law MOVE(ulaw, alaw); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ALAW: switch (outhdr.encoding) { case LINEAR: // Convert alaw to linear switch (outhdr.bytes_per_unit) { case 1: MOVE(alaw, char); break; case 2: MOVE(alaw, short); break; case 4: MOVE(alaw, long); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case FLOAT: // Convert alaw to float switch (outhdr.bytes_per_unit) { case 4: MOVE(alaw, float); break; case 8: MOVE(alaw, double); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; case ALAW: // Convert alaw to a-law COPY(1); break; case ULAW: // Convert alaw to u-law MOVE(alaw, ulaw); break; default: err = AUDIO_ERR_HDRINVAL; break; } break; default: err = AUDIO_ERR_HDRINVAL; break; } if (err) { if (outbuf != inbuf) delete outbuf; return (err); } // Finish up if (outbuf == inbuf) { // If the conversion was in-place, set the new header (void) inbuf->SetHeader(outhdr); } else { // This will delete the buffer inbuf->Reference(); inbuf->Dereference(); // Set the valid data length and replace the pointer outbuf->SetLength(length); inbuf = outbuf; } return (AUDIO_SUCCESS); } AudioError AudioTypePcm:: Flush( AudioBuffer*& /* buf */) { return (AUDIO_SUCCESS); } /* * 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) 1992-2001 by Sun Microsystems, Inc. * All rights reserved. * * Copyright 2019 RackTop Systems. */ #include #include #include #include #include // This is the first stab at a conversion class for Sample Rate conversions // class AudioTypeSampleRate methods // Constructor AudioTypeSampleRate:: AudioTypeSampleRate(int inrate, int outrate) : resampler(inrate, outrate), input_rate(inrate), output_rate(outrate) { } // Destructor AudioTypeSampleRate:: ~AudioTypeSampleRate() { } // Test conversion possibilities. // Return TRUE if conversion to/from the specified type is possible. Boolean AudioTypeSampleRate:: CanConvert( AudioHdr h) const // target header { if ((input_rate <= 0) || (output_rate <= 0)) return (FALSE); if ((h.encoding != LINEAR) || ((h.sample_rate != output_rate) && (h.sample_rate != input_rate)) || (h.bytes_per_unit != 2) || (h.channels != 1)) { return (FALSE); } return (TRUE); } // Convert buffer to the specified type // May replace the buffer with a new one, if necessary AudioError AudioTypeSampleRate:: Convert( AudioBuffer*& inbuf, // data buffer to process AudioHdr outhdr) // target header { AudioBuffer* outbuf; AudioHdr inhdr; Double length; int i; size_t nsamps; #ifdef DEBUG size_t insamps; #endif AudioError err; inhdr = inbuf->GetHeader(); length = inbuf->GetLength(); if (Undefined(length)) { return (AUDIO_ERR_BADARG); } // Make sure we're not being asked to do the impossible // XXX - need a better error code if ((err = inhdr.Validate()) || (err = outhdr.Validate())) { return (err); } // If the requested conversion is different than what was initially // established, then return an error. // XXX - Maybe one day flush and re-init the filter if ((inhdr.sample_rate != input_rate) || (outhdr.sample_rate != output_rate)) { return (AUDIO_ERR_BADARG); } // If conversion is a no-op, just return success if (inhdr.sample_rate == outhdr.sample_rate) { return (AUDIO_SUCCESS); } // If nothing in the buffer, do the simple thing if (length == 0.) { inbuf->SetHeader(outhdr); return (AUDIO_SUCCESS); } // Add some padding to the output buffer i = 4 * ((input_rate / output_rate) + (output_rate / input_rate)); length += outhdr.Samples_to_Time(i); // Allocate a new buffer outbuf = new AudioBuffer(length, "(SampleRate conversion buffer)"); if (outbuf == 0) return (AUDIO_UNIXERROR); err = outbuf->SetHeader(outhdr); if (err != AUDIO_SUCCESS) { delete outbuf; return (err); } // here's where the guts go ... nsamps = resampler.filter((short *)inbuf->GetAddress(), (int)inbuf->GetHeader().Time_to_Samples(inbuf->GetLength()), (short *)outbuf->GetAddress()); #ifdef DEBUG // do a sanity check. did we write more bytes then we had // available in the output buffer? insamps = (unsigned int) outbuf->GetHeader().Time_to_Samples(outbuf->GetSize()); AUDIO_DEBUG((2, "TypeResample: after filter, insamps=%d, outsamps=%d\n", insamps, nsamps)); #endif if (nsamps > outbuf->GetHeader().Time_to_Samples(outbuf->GetSize())) { AudioStderrMsg(outbuf, AUDIO_NOERROR, Fatal, (char *)"resample filter corrupted the heap"); } // set output size appropriately outbuf->SetLength(outbuf->GetHeader().Samples_to_Time(nsamps)); // This will delete the buffer inbuf->Reference(); inbuf->Dereference(); inbuf = outbuf; return (AUDIO_SUCCESS); } AudioError AudioTypeSampleRate:: Flush( AudioBuffer*& outbuf) { AudioHdr h; Double pos; int nsamp; size_t cnt; AudioError err; unsigned char *tmpbuf; if (outbuf == NULL) return (AUDIO_SUCCESS); h = outbuf->GetHeader(); nsamp = resampler.getFlushSize(); if (nsamp > 0) { cnt = (size_t)nsamp * h.bytes_per_unit; tmpbuf = new unsigned char[cnt]; // this does a flush nsamp = resampler.filter(NULL, 0, (short *)tmpbuf); // Copy to the supplied buffer if (nsamp > 0) { cnt = (size_t)nsamp * h.bytes_per_unit; pos = outbuf->GetLength(); err = outbuf->AppendData(tmpbuf, cnt, pos); if (err) return (err); } delete[] tmpbuf; } return (AUDIO_SUCCESS); } /* * 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 1993-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include