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 (strcmp(estr, "�law") == 0) || 411 (strcmp(estr, "�-law") == 0) || 412 (strcmp(estr, "mulaw") == 0) || 413 (strcmp(estr, "mu-law") == 0) || 414 (strcoll(estr, lib_ulaw) == 0)) { 415 if ((prec != 0.) && (prec != 8.)) 416 return (AUDIO_ERR_BADARG); 417 encoding = ULAW; 418 samples_per_unit = 1; 419 bytes_per_unit = 1; 420 } else if ((strcmp(estr, "alaw") == 0) || 421 (strcmp(estr, "a-law") == 0) || 422 (strcoll(estr, lib_Alaw) == 0)) { 423 if ((prec != 0.) && (prec != 8.)) 424 return (AUDIO_ERR_BADARG); 425 encoding = ALAW; 426 samples_per_unit = 1; 427 bytes_per_unit = 1; 428 429 } else if ((strcmp(estr, "linear") == 0) || 430 (strcmp(estr, "lin") == 0) || 431 (strcmp(estr, "pcm") == 0) || 432 (strcoll(estr, lib_linear) == 0)) { 433 if ((prec != 0.) && (prec != 8.) && (prec != 16.) && 434 (prec != 24.) && (prec != 32.)) 435 return (AUDIO_ERR_BADARG); 436 if (prec == 0.) 437 prec = 16.; 438 encoding = LINEAR; 439 samples_per_unit = 1; 440 bytes_per_unit = irint(prec / 8.); 441 442 } else if ((strcmp(estr, "linear8") == 0) || 443 (strcmp(estr, "lin8") == 0) || 444 (strcmp(estr, "pcm8") == 0)) { 445 if ((prec != 0.) && (prec != 8.)) 446 return (AUDIO_ERR_BADARG); 447 prec = 8.; 448 encoding = LINEAR; 449 samples_per_unit = 1; 450 bytes_per_unit = irint(prec / 8.); 451 452 } else if ((strcmp(estr, "linear16") == 0) || 453 (strcmp(estr, "lin16") == 0) || 454 (strcmp(estr, "pcm16") == 0)) { 455 if ((prec != 0.) && (prec != 16.)) 456 return (AUDIO_ERR_BADARG); 457 prec = 16.; 458 encoding = LINEAR; 459 samples_per_unit = 1; 460 bytes_per_unit = irint(prec / 8.); 461 462 } else if ((strcmp(estr, "linear24") == 0) || 463 (strcmp(estr, "lin24") == 0) || 464 (strcmp(estr, "pcm24") == 0)) { 465 if ((prec != 0.) && (prec != 24.)) 466 return (AUDIO_ERR_BADARG); 467 prec = 24.; 468 encoding = LINEAR; 469 samples_per_unit = 1; 470 bytes_per_unit = irint(prec / 8.); 471 472 } else if ((strcmp(estr, "linear32") == 0) || 473 (strcmp(estr, "lin32") == 0) || 474 (strcmp(estr, "pcm32") == 0)) { 475 if ((prec != 0.) && (prec != 32.)) 476 return (AUDIO_ERR_BADARG); 477 prec = 32.; 478 encoding = LINEAR; 479 samples_per_unit = 1; 480 bytes_per_unit = irint(prec / 8.); 481 482 } else if ((strcmp(estr, "float") == 0) || 483 (strcmp(estr, "floatingpoint") == 0) || 484 (strcmp(estr, "floating-point") == 0)) { 485 if ((prec != 0.) && (prec != 32.) && (prec != 64.)) 486 return (AUDIO_ERR_BADARG); 487 if (prec == 0.) 488 prec = 64.; 489 encoding = FLOAT; 490 samples_per_unit = 1; 491 bytes_per_unit = irint(prec / 8.); 492 493 } else if ((strcmp(estr, "float32") == 0) || 494 (strcmp(estr, "floatingpoint32") == 0) || 495 (strcmp(estr, "floating-point32") == 0)) { 496 if ((prec != 0.) && (prec != 32.)) 497 return (AUDIO_ERR_BADARG); 498 prec = 32.; 499 encoding = FLOAT; 500 samples_per_unit = 1; 501 bytes_per_unit = irint(prec / 8.); 502 503 } else if ((strcmp(estr, "float64") == 0) || 504 (strcmp(estr, "double") == 0) || 505 (strcmp(estr, "floatingpoint64") == 0) || 506 (strcmp(estr, "floating-point64") == 0)) { 507 if ((prec != 0.) && (prec != 64.)) 508 return (AUDIO_ERR_BADARG); 509 prec = 64.; 510 encoding = FLOAT; 511 samples_per_unit = 1; 512 bytes_per_unit = irint(prec / 8.); 513 514 } else if ((strcmp(estr, "g.721") == 0) || 515 (strcmp(estr, "g721") == 0) || 516 (strcmp(estr, "g.721adpcm") == 0) || 517 (strcmp(estr, "g721adpcm") == 0)) { 518 if ((prec != 0.) && (prec != 4.)) 519 return (AUDIO_ERR_BADARG); 520 encoding = G721; 521 samples_per_unit = 2; 522 bytes_per_unit = 1; 523 524 } else if ((strcmp(estr, "g.722") == 0) || 525 (strcmp(estr, "g722") == 0) || 526 (strcmp(estr, "g.722adpcm") == 0) || 527 (strcmp(estr, "g722adpcm") == 0)) { 528 if ((prec != 0.) && (prec != 8.)) 529 return (AUDIO_ERR_BADARG); 530 encoding = G722; 531 samples_per_unit = 1; 532 bytes_per_unit = 1; 533 534 } else if ((strcmp(estr, "g.723") == 0) || 535 (strcmp(estr, "g723") == 0) || 536 (strcmp(estr, "g.723adpcm") == 0) || 537 (strcmp(estr, "g723adpcm") == 0)) { 538 if ((prec != 0.) && (prec != 3.) && (prec != 5.)) 539 return (AUDIO_ERR_BADARG); 540 if (prec == 0.) 541 prec = 3.; 542 encoding = G723; 543 samples_per_unit = 8; 544 bytes_per_unit = irint(prec); 545 546 } else if ((strcmp(estr, "g.723-3") == 0) || 547 (strcmp(estr, "g.723_3") == 0) || 548 (strcmp(estr, "g.723.3") == 0) || 549 (strcmp(estr, "g723-3") == 0) || 550 (strcmp(estr, "g723_3") == 0) || 551 (strcmp(estr, "g723.3") == 0)) { 552 if ((prec != 0.) && (prec != 3.)) 553 return (AUDIO_ERR_BADARG); 554 prec = 3.; 555 encoding = G723; 556 samples_per_unit = 8; 557 bytes_per_unit = irint(prec); 558 559 } else if ((strcmp(estr, "g.723-5") == 0) || 560 (strcmp(estr, "g.723_5") == 0) || 561 (strcmp(estr, "g.723.5") == 0) || 562 (strcmp(estr, "g723-5") == 0) || 563 (strcmp(estr, "g723_5") == 0) || 564 (strcmp(estr, "g723.5") == 0)) { 565 if ((prec != 0.) && (prec != 5.)) 566 return (AUDIO_ERR_BADARG); 567 prec = 5.; 568 encoding = G723; 569 samples_per_unit = 8; 570 bytes_per_unit = irint(prec); 571 572 } else if ((strcmp(estr, "dvi") == 0) || 573 (strcmp(estr, "dviadpcm") == 0)) { 574 if ((prec != 0.) && (prec != 4.)) 575 return (AUDIO_ERR_BADARG); 576 encoding = DVI; 577 samples_per_unit = 2; 578 bytes_per_unit = 1; 579 580 } else { 581 delete estr; 582 return (AUDIO_ERR_BADARG); 583 } 584 delete estr; 585 } else { 586 return (AUDIO_ERR_BADARG); 587 } 588 return (AUDIO_SUCCESS); 589 } 590 591 // Parse a string containing the comma-separated audio encoding 592 // Format is: "enc, chan, rate" 593 // XXX - some countries use comma instead of decimal point 594 // so there may be a problem with "44,1 khz" 595 AudioError AudioHdr:: 596 FormatParse( 597 char *str) 598 { 599 char *pstr; 600 char *ptr; 601 char *p; 602 AudioHdr newhdr; 603 AudioError err; 604 605 pstr = new char[strlen(str) + 1]; 606 (void) strcpy(pstr, str); 607 ptr = pstr; 608 609 // Delimit and parse the precision string 610 p = strchr(ptr, ','); 611 if (p == NULL) 612 p = strchr(ptr, ' '); 613 if (p == NULL) { 614 err = AUDIO_ERR_BADARG; 615 goto errret; 616 } 617 *p++ = '\0'; 618 err = newhdr.EncodingParse(ptr); 619 620 // Delimit and parse the sample rate string 621 if (!err) { 622 ptr = p; 623 p = strchr(ptr, ','); 624 if (p == NULL) 625 p = strchr(ptr, ' '); 626 if (p == NULL) { 627 err = AUDIO_ERR_BADARG; 628 goto errret; 629 } 630 *p++ = '\0'; 631 err = newhdr.RateParse(ptr); 632 } 633 634 // Finally, parse the channels string 635 if (!err) { 636 err = newhdr.ChannelParse(p); 637 } 638 639 // Validate the resulting header 640 if (!err) 641 err = newhdr.Validate(); 642 if (!err) 643 *this = newhdr; 644 errret: 645 delete[] pstr; 646 return (err); 647 } 648