AudioTypePcm.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 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);
}