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 * 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:: 67 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:: 97 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:: 121 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:: 179 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:: 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:: 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:: 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:: 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