xref: /illumos-gate/usr/src/cmd/audio/utilities/AudioTypePcm.cc (revision 7d98732934f32a5f80fe624c9689e8f54dd17ffe)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdlib.h>
28 #include <memory.h>
29 #include <math.h>
30 #include <AudioTypePcm.h>
31 #include <libaudio.h>
32 
33 #define	irint(d)	((int)d)
34 
35 // class AudioTypePcm methods
36 
37 
38 // Constructor
39 AudioTypePcm::
AudioTypePcm()40 AudioTypePcm()
41 {
42 	// Set up fixed header values; the rest are negotiable
43 	hdr.Clear();
44 	hdr.samples_per_unit = 1;
45 	hdr.encoding = LINEAR;
46 }
47 
48 // Test conversion possibilities.
49 // Return TRUE if conversion to/from the specified type is possible.
50 Boolean AudioTypePcm::
CanConvert(AudioHdr h) const51 CanConvert(
52 	AudioHdr	h) const		// target header
53 {
54 	if (h.samples_per_unit != 1)
55 		return (FALSE);
56 
57 	switch (h.encoding) {
58 	case LINEAR:
59 		switch (h.bytes_per_unit) {
60 		case 1: case 2: case 4:
61 			break;
62 		default:
63 			return (FALSE);
64 		}
65 		break;
66 	case FLOAT:
67 		switch (h.bytes_per_unit) {
68 		case 4: case 8:
69 			break;
70 		default:
71 			return (FALSE);
72 		}
73 		break;
74 	case ULAW:
75 	case ALAW:
76 		switch (h.bytes_per_unit) {
77 		case 1:
78 			break;
79 		default:
80 			return (FALSE);
81 		}
82 		break;
83 	default:
84 		return (FALSE);
85 	}
86 	return (TRUE);
87 }
88 
89 // Clip most negative values and convert to floating-point
90 inline double AudioTypePcm::
char2dbl(char B)91 char2dbl(char B)
92 {
93 	return ((unsigned char)B == 0x80 ? -1. : (double)B / 127.);
94 }
95 inline double AudioTypePcm::
short2dbl(short S)96 short2dbl(short S)
97 {
98 	return ((unsigned short)S == 0x8000 ? -1. : (double)S / 32767.);
99 }
100 inline double AudioTypePcm::
long2dbl(long L)101 long2dbl(long L)
102 {
103 	return ((unsigned long)L == 0x80000000 ? -1. : (double)L / 2147483647.);
104 }
105 // Convert floating-point to integer, scaled by the appropriate constant
106 inline long AudioTypePcm::
dbl2long(double D,long C)107 dbl2long(double D, long C)
108 {
109 	return (D >= 1. ? C : D <= -1. ? -C : (long)irint(D * (double)C));
110 }
111 
112 // Simple type conversions
113 inline void AudioTypePcm::
char2short(char * & F,short * & T)114 char2short(char *&F, short *&T) { *T++ = ((short)*F++) << 8; }
115 inline void AudioTypePcm::
char2long(char * & F,long * & T)116 char2long(char *&F, long *&T) { *T++ = ((long)*F++) << 24; }
117 inline void AudioTypePcm::
char2float(char * & F,float * & T)118 char2float(char *&F, float *&T) { *T++ = char2dbl(*F++); }
119 inline void AudioTypePcm::
char2double(char * & F,double * & T)120 char2double(char *&F, double *&T) { *T++ = char2dbl(*F++); }
121 inline void AudioTypePcm::
char2ulaw(char * & F,ulaw * & T)122 char2ulaw(char *&F, ulaw *&T) { *T++ = audio_c2u(*F); F++; }
123 inline void AudioTypePcm::
char2alaw(char * & F,alaw * & T)124 char2alaw(char *&F, alaw *&T) { *T++ = audio_c2a(*F); F++; }
125 
126 inline void AudioTypePcm::
short2char(short * & F,char * & T)127 short2char(short *&F, char *&T) { *T++ = (char)(*F++ >> 8); }
128 inline void AudioTypePcm::
short2long(short * & F,long * & T)129 short2long(short *&F, long *&T) { *T++ = ((long)*F++) << 16; }
130 inline void AudioTypePcm::
short2float(short * & F,float * & T)131 short2float(short *&F, float *&T) { *T++ = short2dbl(*F++); }
132 inline void AudioTypePcm::
short2double(short * & F,double * & T)133 short2double(short *&F, double *&T) { *T++ = short2dbl(*F++); }
134 inline void AudioTypePcm::
short2ulaw(short * & F,ulaw * & T)135 short2ulaw(short *&F, ulaw *&T) { *T++ = audio_s2u(*F); F++; }
136 inline void AudioTypePcm::
short2alaw(short * & F,alaw * & T)137 short2alaw(short *&F, alaw *&T) { *T++ = audio_s2a(*F); F++; }
138 
139 inline void AudioTypePcm::
long2char(long * & F,char * & T)140 long2char(long *&F, char *&T) { *T++ = (char)(*F++ >> 24); }
141 inline void AudioTypePcm::
long2short(long * & F,short * & T)142 long2short(long *&F, short *&T) { *T++ = (short)(*F++ >> 16); }
143 inline void AudioTypePcm::
long2float(long * & F,float * & T)144 long2float(long *&F, float *&T) { *T++ = long2dbl(*F++); }
145 inline void AudioTypePcm::
long2double(long * & F,double * & T)146 long2double(long *&F, double *&T) { *T++ = long2dbl(*F++); }
147 inline void AudioTypePcm::
long2ulaw(long * & F,ulaw * & T)148 long2ulaw(long *&F, ulaw *&T) { *T++ = audio_l2u(*F); F++; }
149 inline void AudioTypePcm::
long2alaw(long * & F,alaw * & T)150 long2alaw(long *&F, alaw *&T) { *T++ = audio_l2a(*F); F++; }
151 
152 inline void AudioTypePcm::
float2char(float * & F,char * & T)153 float2char(float *&F, char *&T) { *T++ = (char)dbl2long(*F++, 127); }
154 inline void AudioTypePcm::
float2short(float * & F,short * & T)155 float2short(float *&F, short *&T) { *T++ = (short)dbl2long(*F++, 32767); }
156 inline void AudioTypePcm::
float2long(float * & F,long * & T)157 float2long(float *&F, long *&T) { *T++ = dbl2long(*F++, 2147483647); }
158 inline void AudioTypePcm::
float2double(float * & F,double * & T)159 float2double(float *&F, double *&T) { *T++ = *F++; }
160 inline void AudioTypePcm::
float2ulaw(float * & F,ulaw * & T)161 float2ulaw(float *&F, ulaw *&T) { *T++ = audio_s2u(dbl2long(*F++, 32767)); }
162 inline void AudioTypePcm::
float2alaw(float * & F,alaw * & T)163 float2alaw(float *&F, alaw *&T) { *T++ = audio_s2a(dbl2long(*F++, 32767)); }
164 
165 inline void AudioTypePcm::
double2char(double * & F,char * & T)166 double2char(double *&F, char *&T) { *T++ = (char)dbl2long(*F++, 127); }
167 inline void AudioTypePcm::
double2short(double * & F,short * & T)168 double2short(double *&F, short *&T) { *T++ = (short)dbl2long(*F++, 32767); }
169 inline void AudioTypePcm::
double2long(double * & F,long * & T)170 double2long(double *&F, long *&T) { *T++ = dbl2long(*F++, 2147483647); }
171 inline void AudioTypePcm::
double2float(double * & F,float * & T)172 double2float(double *&F, float *&T) { *T++ = *F++; }
173 inline void AudioTypePcm::
double2ulaw(double * & F,ulaw * & T)174 double2ulaw(double *&F, ulaw *&T) { *T++ = audio_s2u(dbl2long(*F++, 32767)); }
175 inline void AudioTypePcm::
double2alaw(double * & F,alaw * & T)176 double2alaw(double *&F, alaw *&T) { *T++ = audio_s2a(dbl2long(*F++, 32767)); }
177 
178 inline void AudioTypePcm::
ulaw2char(ulaw * & F,char * & T)179 ulaw2char(ulaw *&F, char *&T) { *T++ = audio_u2c(*F); F++; }
180 inline void AudioTypePcm::
ulaw2alaw(ulaw * & F,alaw * & T)181 ulaw2alaw(ulaw *&F, alaw *&T) { *T++ = audio_u2a(*F); F++; }
182 inline void AudioTypePcm::
ulaw2short(ulaw * & F,short * & T)183 ulaw2short(ulaw *&F, short *&T) { *T++ = audio_u2s(*F); F++; }
184 inline void AudioTypePcm::
ulaw2long(ulaw * & F,long * & T)185 ulaw2long(ulaw *&F, long *&T) { *T++ = audio_u2l(*F); F++; }
186 inline void AudioTypePcm::
ulaw2float(ulaw * & F,float * & T)187 ulaw2float(ulaw *&F, float *&T) { *T++ = short2dbl(audio_u2s(*F)); F++; }
188 inline void AudioTypePcm::
ulaw2double(ulaw * & F,double * & T)189 ulaw2double(ulaw *&F, double *&T) { *T++ = short2dbl(audio_u2s(*F)); F++; }
190 
191 inline void AudioTypePcm::
alaw2char(alaw * & F,char * & T)192 alaw2char(alaw *&F, char *&T) { *T++ = audio_a2c(*F); F++; }
193 inline void AudioTypePcm::
alaw2short(alaw * & F,short * & T)194 alaw2short(alaw *&F, short *&T) { *T++ = audio_a2s(*F); F++; }
195 inline void AudioTypePcm::
alaw2long(alaw * & F,long * & T)196 alaw2long(alaw *&F, long *&T) { *T++ = audio_a2l(*F); F++; }
197 inline void AudioTypePcm::
alaw2float(alaw * & F,float * & T)198 alaw2float(alaw *&F, float *&T) { *T++ = short2dbl(audio_a2s(*F)); F++; }
199 inline void AudioTypePcm::
alaw2double(alaw * & F,double * & T)200 alaw2double(alaw *&F, double *&T) { *T++ = short2dbl(audio_a2s(*F)); F++; }
201 inline void AudioTypePcm::
alaw2ulaw(alaw * & F,ulaw * & T)202 alaw2ulaw(alaw*& F, ulaw*& T) { *T++ = audio_a2u(*F); F++; }
203 
204 
205 // Convert buffer to the specified type
206 // May replace the buffer with a new one, if necessary
207 AudioError AudioTypePcm::
Convert(AudioBuffer * & inbuf,AudioHdr outhdr)208 Convert(
209 	AudioBuffer*&	inbuf,			// data buffer to process
210 	AudioHdr	outhdr)			// target header
211 {
212 	AudioBuffer*	outbuf;
213 	AudioHdr	inhdr;
214 	Double		length;
215 	size_t		frames;
216 	void*		inptr;
217 	void*		outptr;
218 	AudioError	err;
219 
220 	inhdr = inbuf->GetHeader();
221 	length = inbuf->GetLength();
222 
223 	if (Undefined(length))
224 		return (AUDIO_ERR_BADARG);
225 
226 	// Make sure we're not being asked to do the impossible
227 	// XXX - how do we deal with multi-channel data??
228 	// XXX - need a better error code
229 	if ((err = inhdr.Validate()) || (err = outhdr.Validate()))
230 		return (err);
231 	if ((inhdr.sample_rate != outhdr.sample_rate) ||
232 	    (inhdr.samples_per_unit != outhdr.samples_per_unit) ||
233 	    (inhdr.samples_per_unit != 1) ||
234 	    (inhdr.channels != outhdr.channels))
235 		return (AUDIO_ERR_HDRINVAL);
236 
237 	// If the buffer is not referenced, and the target size is no bigger
238 	// than the current size, the conversion can be done in place
239 	if (!inbuf->isReferenced() &&
240 	    (outhdr.bytes_per_unit <= inhdr.bytes_per_unit)) {
241 		outbuf = inbuf;
242 	} else {
243 		// Allocate a new buffer
244 		outbuf = new AudioBuffer(length, "(PCM conversion buffer)");
245 		if (outbuf == 0)
246 			return (AUDIO_UNIXERROR);
247 		err = outbuf->SetHeader(outhdr);
248 		if (err != AUDIO_SUCCESS) {
249 			delete outbuf;
250 			return (err);
251 		}
252 	}
253 
254 	// Convert from the input type to the output type
255 	inptr = inbuf->GetAddress();
256 	outptr = outbuf->GetAddress();
257 	frames = (size_t)inhdr.Time_to_Samples(length)
258 		* inhdr.channels;
259 
260 // Define macro to copy with no data conversion
261 #define	COPY(N)		if (inptr != outptr) memcpy(outptr, inptr, frames * N)
262 // Define macro to translate a buffer
263 // XXX - The temporary pointers are necessary to get the updates
264 
265 // token catenation different for ANSI cpp v.s. old cpp.
266 #ifdef __STDC__
267 #define	MOVE(F, T)	{						\
268 			    F* ip = (F*)inptr; T* op = (T*)outptr;	\
269 			    while (frames-- > 0) F ## 2 ## T(ip, op);	\
270 			}
271 #else
272 #define	MOVE(F, T)	{						\
273 			    F* ip = (F*)inptr; T* op = (T*)outptr;	\
274 			    while (frames-- > 0) F /* */ 2 /* */ T(ip, op);\
275 			}
276 #endif
277 	switch (inhdr.encoding) {
278 	case LINEAR:
279 		switch (outhdr.encoding) {
280 		case LINEAR:		// Convert linear to linear
281 			switch (inhdr.bytes_per_unit) {
282 			case 1:
283 				switch (outhdr.bytes_per_unit) {
284 				case 1: COPY(1); break;
285 				case 2: MOVE(char, short); break;
286 				case 4: MOVE(char, long); break;
287 				default: err = AUDIO_ERR_HDRINVAL; break;
288 				}
289 				break;
290 			case 2:
291 				switch (outhdr.bytes_per_unit) {
292 				case 1: MOVE(short, char); break;
293 				case 2: COPY(2); break;
294 				case 4: MOVE(short, long); break;
295 				default: err = AUDIO_ERR_HDRINVAL; break;
296 				}
297 				break;
298 			case 4:
299 				switch (outhdr.bytes_per_unit) {
300 				case 1: MOVE(long, char); break;
301 				case 2: MOVE(long, short); break;
302 				case 4: COPY(4); break;
303 				default: err = AUDIO_ERR_HDRINVAL; break;
304 				}
305 				break;
306 			default:
307 				err = AUDIO_ERR_HDRINVAL; break;
308 			}
309 			break;
310 		case FLOAT:		// Convert linear to float
311 			switch (inhdr.bytes_per_unit) {
312 			case 1:
313 				switch (outhdr.bytes_per_unit) {
314 				case 4: MOVE(char, float); break;
315 				case 8: MOVE(char, double); break;
316 				default: err = AUDIO_ERR_HDRINVAL; break;
317 				}
318 				break;
319 			case 2:
320 				switch (outhdr.bytes_per_unit) {
321 				case 4: MOVE(short, float); break;
322 				case 8: MOVE(short, double); break;
323 				default: err = AUDIO_ERR_HDRINVAL; break;
324 				}
325 				break;
326 			case 4:
327 				switch (outhdr.bytes_per_unit) {
328 				case 4: MOVE(long, float); break;
329 				case 8: MOVE(long, double); break;
330 				default: err = AUDIO_ERR_HDRINVAL; break;
331 				}
332 				break;
333 			default:
334 				err = AUDIO_ERR_HDRINVAL; break;
335 			}
336 			break;
337 		case ULAW:		// Convert linear to u-law
338 			switch (inhdr.bytes_per_unit) {
339 			case 1: MOVE(char, ulaw); break;
340 			case 2: MOVE(short, ulaw); break;
341 			case 4: MOVE(long, ulaw); break;
342 			default: err = AUDIO_ERR_HDRINVAL; break;
343 			}
344 			break;
345 		case ALAW:		// Convert linear to a-law
346 			switch (inhdr.bytes_per_unit) {
347 			case 1: MOVE(char, alaw); break;
348 			case 2: MOVE(short, alaw); break;
349 			case 4: MOVE(long, alaw); break;
350 			default: err = AUDIO_ERR_HDRINVAL; break;
351 			}
352 			break;
353 		default:
354 			err = AUDIO_ERR_HDRINVAL; break;
355 		}
356 		break;
357 	case FLOAT:
358 		switch (outhdr.encoding) {
359 		case LINEAR:		// Convert float to linear
360 			switch (inhdr.bytes_per_unit) {
361 			case 4:
362 				switch (outhdr.bytes_per_unit) {
363 				case 1: MOVE(float, char); break;
364 				case 2: MOVE(float, short); break;
365 				case 4: MOVE(float, long); break;
366 				default: err = AUDIO_ERR_HDRINVAL; break;
367 				}
368 				break;
369 			case 8:
370 				switch (outhdr.bytes_per_unit) {
371 				case 1: MOVE(double, char); break;
372 				case 2: MOVE(double, short); break;
373 				case 4: MOVE(double, long); break;
374 				default: err = AUDIO_ERR_HDRINVAL; break;
375 				}
376 				break;
377 			default:
378 				err = AUDIO_ERR_HDRINVAL; break;
379 			}
380 			break;
381 		case FLOAT:		// Convert float to float
382 			switch (inhdr.bytes_per_unit) {
383 			case 4:
384 				switch (outhdr.bytes_per_unit) {
385 				case 4: COPY(4); break;
386 				case 8: MOVE(float, double); break;
387 				default: err = AUDIO_ERR_HDRINVAL; break;
388 				}
389 				break;
390 			case 8:
391 				switch (outhdr.bytes_per_unit) {
392 				case 4: MOVE(double, float); break;
393 				case 8: COPY(8); break;
394 				default: err = AUDIO_ERR_HDRINVAL; break;
395 				}
396 				break;
397 			default:
398 				err = AUDIO_ERR_HDRINVAL; break;
399 			}
400 			break;
401 		case ULAW:		// Convert float to u-law
402 			switch (inhdr.bytes_per_unit) {
403 			case 4: MOVE(float, ulaw); break;
404 			case 8: MOVE(double, ulaw); break;
405 			default: err = AUDIO_ERR_HDRINVAL; break;
406 			}
407 			break;
408 		case ALAW:		// Convert float to a-law
409 			switch (inhdr.bytes_per_unit) {
410 			case 4: MOVE(float, alaw); break;
411 			case 8: MOVE(double, alaw); break;
412 			default: err = AUDIO_ERR_HDRINVAL; break;
413 			}
414 			break;
415 		default:
416 			err = AUDIO_ERR_HDRINVAL; break;
417 		}
418 		break;
419 	case ULAW:
420 		switch (outhdr.encoding) {
421 		case LINEAR:		// Convert ulaw to linear
422 			switch (outhdr.bytes_per_unit) {
423 			case 1: MOVE(ulaw, char); break;
424 			case 2: MOVE(ulaw, short); break;
425 			case 4: MOVE(ulaw, long); break;
426 			default: err = AUDIO_ERR_HDRINVAL; break;
427 			}
428 			break;
429 		case FLOAT:		// Convert ulaw to float
430 			switch (outhdr.bytes_per_unit) {
431 			case 4: MOVE(ulaw, float); break;
432 			case 8: MOVE(ulaw, double); break;
433 			default: err = AUDIO_ERR_HDRINVAL; break;
434 			}
435 			break;
436 		case ULAW:		// Convert ulaw to u-law
437 			COPY(1); break;
438 		case ALAW:		// Convert ulaw to a-law
439 			MOVE(ulaw, alaw); break;
440 		default:
441 			err = AUDIO_ERR_HDRINVAL; break;
442 		}
443 		break;
444 	case ALAW:
445 		switch (outhdr.encoding) {
446 		case LINEAR:		// Convert alaw to linear
447 			switch (outhdr.bytes_per_unit) {
448 			case 1: MOVE(alaw, char); break;
449 			case 2: MOVE(alaw, short); break;
450 			case 4: MOVE(alaw, long); break;
451 			default: err = AUDIO_ERR_HDRINVAL; break;
452 			}
453 			break;
454 		case FLOAT:		// Convert alaw to float
455 			switch (outhdr.bytes_per_unit) {
456 			case 4: MOVE(alaw, float); break;
457 			case 8: MOVE(alaw, double); break;
458 			default: err = AUDIO_ERR_HDRINVAL; break;
459 			}
460 			break;
461 		case ALAW:		// Convert alaw to a-law
462 			COPY(1); break;
463 		case ULAW:		// Convert alaw to u-law
464 			MOVE(alaw, ulaw); break;
465 		default:
466 			err = AUDIO_ERR_HDRINVAL; break;
467 		}
468 		break;
469 	default:
470 		err = AUDIO_ERR_HDRINVAL; break;
471 	}
472 	if (err) {
473 		if (outbuf != inbuf)
474 			delete outbuf;
475 		return (err);
476 	}
477 
478 	// Finish up
479 	if (outbuf == inbuf) {
480 		// If the conversion was in-place, set the new header
481 		(void) inbuf->SetHeader(outhdr);
482 	} else {
483 		// This will delete the buffer
484 		inbuf->Reference();
485 		inbuf->Dereference();
486 
487 		// Set the valid data length and replace the pointer
488 		outbuf->SetLength(length);
489 		inbuf = outbuf;
490 	}
491 	return (AUDIO_SUCCESS);
492 }
493 
494 AudioError AudioTypePcm::
Flush(AudioBuffer * &)495 Flush(
496 	AudioBuffer*&	/* buf */)
497 {
498 	return (AUDIO_SUCCESS);
499 }
500