OpenSolaris_b135/cmd/audio/utilities/AudioList.cc

/*
 * 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.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <AudioExtent.h>
#include <AudioList.h>
#include <AudioDebug.h>

// 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);
}