AudioBuffer.cc revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <memory.h>
#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), bufaddr(0), zflag(0), bufsize(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()) {
if (err = SetSize(local_length))
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);
}