xref: /illumos-gate/usr/src/cmd/audio/utilities/AudioHdrParse.cc (revision 7f3d7c9289dee6488b3cd2848a68c0b8580d750c)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright 2019 RackTop Systems.
27  */
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <math.h>
33 #include <AudioHdr.h>
34 
35 #define	irint(d)	((int)(d))
36 
37 // Convert a string to lowercase and return an allocated copy of it.
38 // XXX - There really should be a string-insensitive 8-bit compare routine.
39 static char *
to_lowercase(char * str)40 to_lowercase(
41 	char	*str)
42 {
43 	unsigned char	*oldstr;
44 	unsigned char	*newstr;
45 	int		i;
46 
47 	oldstr = (unsigned char *) str;
48 	newstr = new unsigned char [strlen(str) + 1];
49 	for (i = 0; ; i++) {
50 		if (isupper(oldstr[i]))
51 			newstr[i] = tolower(oldstr[i]);
52 		else
53 			newstr[i] = oldstr[i];
54 		if (oldstr[i] == '\0')
55 			break;
56 	}
57 	return ((char *)newstr);
58 }
59 
60 
61 
62 // class AudioHdr parsing methods
63 
64 
65 // Return a string containing the sample rate
66 char *AudioHdr::
RateString() const67 RateString() const
68 {
69 	char	*str;
70 	int	ratek;
71 	int	rateh;
72 	int	prec;
73 
74 	str = new char[32];
75 	ratek = sample_rate / 1000;
76 	rateh = sample_rate % 1000;
77 	if (rateh == 0) {
78 		(void) sprintf(str, "%dkHz", ratek);
79 	} else {
80 		// scale down to print minimum digits after the decimal point
81 		prec = 3;
82 		if ((rateh % 10) == 0) {
83 			prec--;
84 			rateh /= 10;
85 		}
86 		if ((rateh % 10) == 0) {
87 			prec--;
88 			rateh /= 10;
89 		}
90 		(void) sprintf(str, "%d.%0*dkHz", ratek, prec, rateh);
91 	}
92 	return (str);
93 }
94 
95 // Return a string containing the number of channels
96 char *AudioHdr::
ChannelString() const97 ChannelString() const
98 {
99 	char	*str;
100 
101 	str = new char[32];
102 	switch (channels) {
103 	case 1:
104 		(void) sprintf(str, "mono");
105 		break;
106 	case 2:
107 		(void) sprintf(str, "stereo");
108 		break;
109 	case 4:
110 		(void) sprintf(str, "quad");
111 		break;
112 	default:
113 		(void) sprintf(str, "%d-channel", channels);
114 		break;
115 	}
116 	return (str);
117 }
118 
119 // Return a string containing the encoding
120 char *AudioHdr::
EncodingString() const121 EncodingString() const
122 {
123 	char	*str;
124 	Double	prec;
125 	int	iprec;
126 
127 	str = new char[64];
128 	if ((samples_per_unit == 0) || (bytes_per_unit == 0) ||
129 	    (encoding == NONE)) {
130 		(void) sprintf(str, "???");
131 	} else {
132 		// First encode precision
133 		iprec = (bytes_per_unit * 8) / samples_per_unit;
134 		prec = ((Double)bytes_per_unit * 8.) / (Double)samples_per_unit;
135 		if (prec == (Double) iprec) {
136 			(void) sprintf(str, "%d-bit ", iprec);
137 		} else {
138 			(void) sprintf(str, "%.1f-bit ", double(prec));
139 		}
140 
141 		// Then encode format
142 		switch (encoding) {
143 		case ULAW:
144 			// XXX - See bug 1121000
145 			// XXX - (void) strcat(str, "μ-law");
146 			(void) strcat(str, "u-law");
147 			break;
148 		case ALAW:
149 			(void) strcat(str, "A-law");
150 			break;
151 		case LINEAR:
152 			(void) strcat(str, "linear");
153 			break;
154 		case FLOAT:
155 			(void) strcat(str, "float");
156 			break;
157 		case G721:
158 			(void) strcat(str, "G.721 ADPCM");
159 			break;
160 		case G722:
161 			(void) strcat(str, "G.722 ADPCM");
162 			break;
163 		case G723:
164 			(void) strcat(str, "G.723 ADPCM");
165 			break;
166 		case DVI:
167 			(void) strcat(str, "DVI ADPCM");
168 			break;
169 		default:
170 			(void) strcat(str, "???");
171 			break;
172 		}
173 	}
174 	return (str);
175 }
176 
177 // Return a string containing the entire audio encoding
178 char *AudioHdr::
FormatString() const179 FormatString() const
180 {
181 	char	*str;
182 	char	*rate;
183 	char	*chan;
184 	char	*enc;
185 
186 	str = new char[4 * 32];
187 
188 	enc = EncodingString();
189 	rate = RateString();
190 	chan = ChannelString();
191 	(void) sprintf(str, "%s, %s, %s", enc, rate, chan);
192 	delete rate;
193 	delete chan;
194 	delete enc;
195 	return (str);
196 }
197 
198 // Parse a string containing the sample rate
199 AudioError AudioHdr::
RateParse(char * str)200 RateParse(
201 	char		*str)
202 {
203 static char		*lib_khz = NULL;
204 static char		*lib_hz = NULL;
205 
206 	double		r;
207 	int		rate;
208 	char		khzbuf[16];
209 	char		*khz;
210 
211 	if (str == NULL)
212 		return (AUDIO_ERR_BADARG);
213 
214 	// Init i18n string translations
215 	if (lib_khz == NULL) {
216 		lib_khz = to_lowercase(_MGET_("khz"));
217 		lib_hz = to_lowercase(_MGET_("hz"));
218 	}
219 
220 	// Scan for a number followed by an optional khz designator
221 	switch (sscanf(str, " %lf %15s", &r, khzbuf)) {
222 	case 2:
223 		// Process 'khz', if present, and fall through
224 		khz = to_lowercase(khzbuf);
225 		if ((strcmp(khz, "khz") == 0) ||
226 		    (strcmp(khz, "khertz") == 0) ||
227 		    (strcmp(khz, "kilohertz") == 0) ||
228 		    (strcmp(khz, "k") == 0) ||
229 		    (strcoll(khz, lib_khz) == 0)) {
230 			r *= 1000.;
231 		} else if ((strcmp(khz, "hz") != 0) &&
232 		    (strcmp(khz, "hertz") != 0) &&
233 		    (strcoll(khz, lib_hz) != 0)) {
234 			delete khz;
235 			return (AUDIO_ERR_BADARG);
236 		}
237 		delete khz;
238 		/* FALLTHROUGH */
239 	case 1:
240 		rate = irint(r);
241 		break;
242 	default:
243 		return (AUDIO_ERR_BADARG);
244 	}
245 	// Check for reasonable bounds
246 	if ((rate <= 0) || (rate > 500000)) {
247 		return (AUDIO_ERR_BADARG);
248 	}
249 	sample_rate = (unsigned int) rate;
250 	return (AUDIO_SUCCESS);
251 }
252 
253 // Parse a string containing the number of channels
254 AudioError AudioHdr::
ChannelParse(char * str)255 ChannelParse(
256 	char		*str)
257 {
258 static char		*lib_chan = NULL;
259 static char		*lib_mono = NULL;
260 static char		*lib_stereo = NULL;
261 	char		cstrbuf[16];
262 	char		*cstr;
263 	char		xtra[4];
264 	int		chan;
265 
266 	// Init i18n string translations
267 	if (lib_chan == NULL) {
268 		lib_chan = to_lowercase(_MGET_("channel"));
269 		lib_mono = to_lowercase(_MGET_("mono"));
270 		lib_stereo = to_lowercase(_MGET_("stereo"));
271 	}
272 
273 	// Parse a number, followed by optional "-channel"
274 	switch (sscanf(str, " %d %15s", &chan, cstrbuf)) {
275 	case 2:
276 		cstr = to_lowercase(cstrbuf);
277 		if ((strcmp(cstr, "-channel") != 0) &&
278 		    (strcmp(cstr, "-chan") != 0) &&
279 		    (strcoll(cstr, lib_chan) != 0)) {
280 			delete cstr;
281 			return (AUDIO_ERR_BADARG);
282 		}
283 		delete cstr;
284 	case 1:
285 		break;
286 	default:
287 		// If no number, look for reasonable keywords
288 		if (sscanf(str, " %15s %1s", cstrbuf, xtra) != 1) {
289 			return (AUDIO_ERR_BADARG);
290 		}
291 		cstr = to_lowercase(cstrbuf);
292 		if ((strcmp(cstr, "mono") == 0) ||
293 		    (strcmp(cstr, "monaural") == 0) ||
294 		    (strcoll(cstr, lib_mono) == 0)) {
295 			chan = 1;
296 		} else if ((strcmp(cstr, "stereo") == 0) ||
297 		    (strcmp(cstr, "dual") == 0) ||
298 		    (strcoll(cstr, lib_stereo) == 0)) {
299 			chan = 2;
300 		} else if ((strcmp(cstr, "quad") == 0) ||
301 		    (strcmp(cstr, "quadrophonic") == 0)) {
302 			chan = 4;
303 		} else {
304 			delete cstr;
305 			return (AUDIO_ERR_BADARG);
306 		}
307 		delete cstr;
308 	}
309 	if ((chan <= 0) || (chan > 256)) {
310 		return (AUDIO_ERR_BADARG);
311 	}
312 	channels = (unsigned int) chan;
313 	return (AUDIO_SUCCESS);
314 }
315 
316 // Parse a string containing the audio encoding
317 AudioError AudioHdr::
EncodingParse(char * str)318 EncodingParse(
319 	char		*str)
320 {
321 static char		*lib_bit = NULL;
322 static char		*lib_ulaw = NULL;
323 static char		*lib_Alaw = NULL;
324 static char		*lib_linear = NULL;
325 	int		i;
326 	char		*p;
327 	char		estrbuf[64];
328 	char		*estr;
329 	char		xtrabuf[32];
330 	char		*xtra;
331 	char		*xp;
332 	char		buf[BUFSIZ];
333 	char		*cp;
334 	double		prec;
335 
336 	// Init i18n string translations
337 	if (lib_bit == NULL) {
338 		lib_bit = to_lowercase(_MGET_("bit"));
339 		lib_ulaw = to_lowercase(_MGET_("u-law"));
340 		lib_Alaw = to_lowercase(_MGET_("A-law"));
341 		lib_linear = to_lowercase(_MGET_("linear8"));
342 		lib_linear = to_lowercase(_MGET_("linear"));
343 	}
344 
345 	// first copy and remove leading spaces
346 	(void) strncpy(buf, str, BUFSIZ);
347 	for (cp = buf; *cp == ' '; cp++)
348 		continue;
349 
350 	// Delimit the precision.  If there is one, parse it.
351 	prec = 0.;
352 	p = strchr(cp, ' ');
353 	if (p != NULL) {
354 		*p++ = '\0';
355 		i = sscanf(cp, " %lf %15s", &prec, xtrabuf);
356 		if (i == 0) {
357 			return (AUDIO_ERR_BADARG);
358 		}
359 		if (i == 2) {
360 			// convert to lowercase and skip leading "-", if any
361 			xtra = to_lowercase(xtrabuf);
362 			xp = (xtra[0] == '-') ? &xtra[1] : &xtra[0];
363 
364 			if ((strcmp(xp, "bit") != 0) &&
365 			    (strcoll(xp, lib_bit) != 0)) {
366 				delete xtra;
367 				return (AUDIO_ERR_BADARG);
368 			}
369 			delete xtra;
370 		}
371 		if ((prec <= 0.) || (prec > 512.)) {
372 			return (AUDIO_ERR_BADARG);
373 		}
374 
375 		// Don't be fooled by "8 bit"
376 		i = sscanf(p, " %15s", xtrabuf);
377 		if (i == 1) {
378 			// convert to lowercase and skip leading "-", if any
379 			xtra = to_lowercase(xtrabuf);
380 			xp = (xtra[0] == '-') ? &xtra[1] : &xtra[0];
381 			if ((strcmp(xp, "bit") == 0) ||
382 			    (strcoll(xp, lib_bit) == 0)) {
383 				    xp = strchr(p, ' ');
384 				    if (xp != NULL)
385 					    p = xp;
386 				    else
387 					    p += strlen(xtrabuf);
388 			}
389 			delete xtra;
390 		}
391 	} else {
392 		p = cp;
393 	}
394 
395 	i = sscanf(p, " %31s %31s", estrbuf, xtrabuf);
396 
397 	// If "adpcm" appended with a space, concatenate it
398 	if (i == 2) {
399 		xtra = to_lowercase(xtrabuf);
400 		if (strcmp(xtra, "adpcm") == 0) {
401 			(void) strcat(estrbuf, xtra);
402 			i = 1;
403 		}
404 		delete xtra;
405 	}
406 	if (i == 1) {
407 		estr = to_lowercase(estrbuf);
408 		if ((strcmp(estr, "ulaw") == 0) ||
409 		    (strcmp(estr, "u-law") == 0) ||
410 		    /*
411 		     * NB: These characters are literal latin-1 MICRO SIGN
412 		     * maintained for compatibility
413 		     */
414 		    (strcmp(estr, "\xb5law") == 0) ||
415 		    (strcmp(estr, "\xb5-law") == 0) ||
416 		    (strcmp(estr, "mulaw") == 0) ||
417 		    (strcmp(estr, "mu-law") == 0) ||
418 		    (strcoll(estr, lib_ulaw) == 0)) {
419 			if ((prec != 0.) && (prec != 8.))
420 				return (AUDIO_ERR_BADARG);
421 			encoding = ULAW;
422 			samples_per_unit = 1;
423 			bytes_per_unit = 1;
424 		} else if ((strcmp(estr, "alaw") == 0) ||
425 		    (strcmp(estr, "a-law") == 0) ||
426 		    (strcoll(estr, lib_Alaw) == 0)) {
427 			if ((prec != 0.) && (prec != 8.))
428 				return (AUDIO_ERR_BADARG);
429 			encoding = ALAW;
430 			samples_per_unit = 1;
431 			bytes_per_unit = 1;
432 
433 		} else if ((strcmp(estr, "linear") == 0) ||
434 		    (strcmp(estr, "lin") == 0) ||
435 		    (strcmp(estr, "pcm") == 0) ||
436 		    (strcoll(estr, lib_linear) == 0)) {
437 			if ((prec != 0.) && (prec != 8.) && (prec != 16.) &&
438 			    (prec != 24.) && (prec != 32.))
439 				return (AUDIO_ERR_BADARG);
440 			if (prec == 0.)
441 				prec = 16.;
442 			encoding = LINEAR;
443 			samples_per_unit = 1;
444 			bytes_per_unit = irint(prec / 8.);
445 
446 		} else if ((strcmp(estr, "linear8") == 0) ||
447 		    (strcmp(estr, "lin8") == 0) ||
448 		    (strcmp(estr, "pcm8") == 0)) {
449 			if ((prec != 0.) && (prec != 8.))
450 				return (AUDIO_ERR_BADARG);
451 			prec = 8.;
452 			encoding = LINEAR;
453 			samples_per_unit = 1;
454 			bytes_per_unit = irint(prec / 8.);
455 
456 		} else if ((strcmp(estr, "linear16") == 0) ||
457 		    (strcmp(estr, "lin16") == 0) ||
458 		    (strcmp(estr, "pcm16") == 0)) {
459 			if ((prec != 0.) && (prec != 16.))
460 				return (AUDIO_ERR_BADARG);
461 			prec = 16.;
462 			encoding = LINEAR;
463 			samples_per_unit = 1;
464 			bytes_per_unit = irint(prec / 8.);
465 
466 		} else if ((strcmp(estr, "linear24") == 0) ||
467 		    (strcmp(estr, "lin24") == 0) ||
468 		    (strcmp(estr, "pcm24") == 0)) {
469 			if ((prec != 0.) && (prec != 24.))
470 				return (AUDIO_ERR_BADARG);
471 			prec = 24.;
472 			encoding = LINEAR;
473 			samples_per_unit = 1;
474 			bytes_per_unit = irint(prec / 8.);
475 
476 		} else if ((strcmp(estr, "linear32") == 0) ||
477 		    (strcmp(estr, "lin32") == 0) ||
478 		    (strcmp(estr, "pcm32") == 0)) {
479 			if ((prec != 0.) && (prec != 32.))
480 				return (AUDIO_ERR_BADARG);
481 			prec = 32.;
482 			encoding = LINEAR;
483 			samples_per_unit = 1;
484 			bytes_per_unit = irint(prec / 8.);
485 
486 		} else if ((strcmp(estr, "float") == 0) ||
487 		    (strcmp(estr, "floatingpoint") == 0) ||
488 		    (strcmp(estr, "floating-point") == 0)) {
489 			if ((prec != 0.) && (prec != 32.) && (prec != 64.))
490 				return (AUDIO_ERR_BADARG);
491 			if (prec == 0.)
492 				prec = 64.;
493 			encoding = FLOAT;
494 			samples_per_unit = 1;
495 			bytes_per_unit = irint(prec / 8.);
496 
497 		} else if ((strcmp(estr, "float32") == 0) ||
498 		    (strcmp(estr, "floatingpoint32") == 0) ||
499 		    (strcmp(estr, "floating-point32") == 0)) {
500 			if ((prec != 0.) && (prec != 32.))
501 				return (AUDIO_ERR_BADARG);
502 			prec = 32.;
503 			encoding = FLOAT;
504 			samples_per_unit = 1;
505 			bytes_per_unit = irint(prec / 8.);
506 
507 		} else if ((strcmp(estr, "float64") == 0) ||
508 		    (strcmp(estr, "double") == 0) ||
509 		    (strcmp(estr, "floatingpoint64") == 0) ||
510 		    (strcmp(estr, "floating-point64") == 0)) {
511 			if ((prec != 0.) && (prec != 64.))
512 				return (AUDIO_ERR_BADARG);
513 			prec = 64.;
514 			encoding = FLOAT;
515 			samples_per_unit = 1;
516 			bytes_per_unit = irint(prec / 8.);
517 
518 		} else if ((strcmp(estr, "g.721") == 0) ||
519 		    (strcmp(estr, "g721") == 0) ||
520 		    (strcmp(estr, "g.721adpcm") == 0) ||
521 		    (strcmp(estr, "g721adpcm") == 0)) {
522 			if ((prec != 0.) && (prec != 4.))
523 				return (AUDIO_ERR_BADARG);
524 			encoding = G721;
525 			samples_per_unit = 2;
526 			bytes_per_unit = 1;
527 
528 		} else if ((strcmp(estr, "g.722") == 0) ||
529 		    (strcmp(estr, "g722") == 0) ||
530 		    (strcmp(estr, "g.722adpcm") == 0) ||
531 		    (strcmp(estr, "g722adpcm") == 0)) {
532 			if ((prec != 0.) && (prec != 8.))
533 				return (AUDIO_ERR_BADARG);
534 			encoding = G722;
535 			samples_per_unit = 1;
536 			bytes_per_unit = 1;
537 
538 		} else if ((strcmp(estr, "g.723") == 0) ||
539 		    (strcmp(estr, "g723") == 0) ||
540 		    (strcmp(estr, "g.723adpcm") == 0) ||
541 		    (strcmp(estr, "g723adpcm") == 0)) {
542 			if ((prec != 0.) && (prec != 3.) && (prec != 5.))
543 				return (AUDIO_ERR_BADARG);
544 			if (prec == 0.)
545 				prec = 3.;
546 			encoding = G723;
547 			samples_per_unit = 8;
548 			bytes_per_unit = irint(prec);
549 
550 		} else if ((strcmp(estr, "g.723-3") == 0) ||
551 		    (strcmp(estr, "g.723_3") == 0) ||
552 		    (strcmp(estr, "g.723.3") == 0) ||
553 		    (strcmp(estr, "g723-3") == 0) ||
554 		    (strcmp(estr, "g723_3") == 0) ||
555 		    (strcmp(estr, "g723.3") == 0)) {
556 			if ((prec != 0.) && (prec != 3.))
557 				return (AUDIO_ERR_BADARG);
558 			prec = 3.;
559 			encoding = G723;
560 			samples_per_unit = 8;
561 			bytes_per_unit = irint(prec);
562 
563 		} else if ((strcmp(estr, "g.723-5") == 0) ||
564 		    (strcmp(estr, "g.723_5") == 0) ||
565 		    (strcmp(estr, "g.723.5") == 0) ||
566 		    (strcmp(estr, "g723-5") == 0) ||
567 		    (strcmp(estr, "g723_5") == 0) ||
568 		    (strcmp(estr, "g723.5") == 0)) {
569 			if ((prec != 0.) && (prec != 5.))
570 				return (AUDIO_ERR_BADARG);
571 			prec = 5.;
572 			encoding = G723;
573 			samples_per_unit = 8;
574 			bytes_per_unit = irint(prec);
575 
576 		} else if ((strcmp(estr, "dvi") == 0) ||
577 		    (strcmp(estr, "dviadpcm") == 0)) {
578 			if ((prec != 0.) && (prec != 4.))
579 				return (AUDIO_ERR_BADARG);
580 			encoding = DVI;
581 			samples_per_unit = 2;
582 			bytes_per_unit = 1;
583 
584 		} else {
585 			delete estr;
586 			return (AUDIO_ERR_BADARG);
587 		}
588 		delete estr;
589 	} else {
590 		return (AUDIO_ERR_BADARG);
591 	}
592 	return (AUDIO_SUCCESS);
593 }
594 
595 // Parse a string containing the comma-separated audio encoding
596 // Format is: "enc, chan, rate"
597 //	XXX - some countries use comma instead of decimal point
598 //	so there may be a problem with "44,1 khz"
599 AudioError AudioHdr::
FormatParse(char * str)600 FormatParse(
601 	char		*str)
602 {
603 	char		*pstr;
604 	char		*ptr;
605 	char		*p;
606 	AudioHdr	newhdr;
607 	AudioError	err;
608 
609 	pstr = new char[strlen(str) + 1];
610 	(void) strcpy(pstr, str);
611 	ptr = pstr;
612 
613 	// Delimit and parse the precision string
614 	p = strchr(ptr, ',');
615 	if (p == NULL)
616 		p = strchr(ptr, ' ');
617 	if (p == NULL) {
618 		err = AUDIO_ERR_BADARG;
619 		goto errret;
620 	}
621 	*p++ = '\0';
622 	err = newhdr.EncodingParse(ptr);
623 
624 	// Delimit and parse the sample rate string
625 	if (!err) {
626 		ptr = p;
627 		p = strchr(ptr, ',');
628 		if (p == NULL)
629 			p = strchr(ptr, ' ');
630 		if (p == NULL) {
631 			err = AUDIO_ERR_BADARG;
632 			goto errret;
633 		}
634 		*p++ = '\0';
635 		err = newhdr.RateParse(ptr);
636 	}
637 
638 	// Finally, parse the channels string
639 	if (!err) {
640 		err = newhdr.ChannelParse(p);
641 	}
642 
643 	// Validate the resulting header
644 	if (!err)
645 		err = newhdr.Validate();
646 	if (!err)
647 		*this = newhdr;
648 errret:
649 	delete[] pstr;
650 	return (err);
651 }
652