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