OpenSolaris_b135/cmd/audio/utilities/AudioTypePcm.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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <AudioTypePcm.h>
#include <libaudio.h>

#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);
		if (err = outbuf->SetHeader(outhdr)) {
			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);
}