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