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 27 #pragma ident "%Z%%M% %I% %E% SMI" 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 case 1: 239 rate = irint(r); 240 break; 241 default: 242 return (AUDIO_ERR_BADARG); 243 } 244 // Check for reasonable bounds 245 if ((rate <= 0) || (rate > 500000)) { 246 return (AUDIO_ERR_BADARG); 247 } 248 sample_rate = (unsigned int) rate; 249 return (AUDIO_SUCCESS); 250 } 251 252 // Parse a string containing the number of channels 253 AudioError AudioHdr:: 254 ChannelParse( 255 char *str) 256 { 257 static char *lib_chan = NULL; 258 static char *lib_mono = NULL; 259 static char *lib_stereo = NULL; 260 char cstrbuf[16]; 261 char *cstr; 262 char xtra[4]; 263 int chan; 264 265 // Init i18n string translations 266 if (lib_chan == NULL) { 267 lib_chan = to_lowercase(_MGET_("channel")); 268 lib_mono = to_lowercase(_MGET_("mono")); 269 lib_stereo = to_lowercase(_MGET_("stereo")); 270 } 271 272 // Parse a number, followed by optional "-channel" 273 switch (sscanf(str, " %d %15s", &chan, cstrbuf)) { 274 case 2: 275 cstr = to_lowercase(cstrbuf); 276 if ((strcmp(cstr, "-channel") != 0) && 277 (strcmp(cstr, "-chan") != 0) && 278 (strcoll(cstr, lib_chan) != 0)) { 279 delete cstr; 280 return (AUDIO_ERR_BADARG); 281 } 282 delete cstr; 283 case 1: 284 break; 285 default: 286 // If no number, look for reasonable keywords 287 if (sscanf(str, " %15s %1s", cstrbuf, xtra) != 1) { 288 return (AUDIO_ERR_BADARG); 289 } 290 cstr = to_lowercase(cstrbuf); 291 if ((strcmp(cstr, "mono") == 0) || 292 (strcmp(cstr, "monaural") == 0) || 293 (strcoll(cstr, lib_mono) == 0)) { 294 chan = 1; 295 } else if ((strcmp(cstr, "stereo") == 0) || 296 (strcmp(cstr, "dual") == 0) || 297 (strcoll(cstr, lib_stereo) == 0)) { 298 chan = 2; 299 } else if ((strcmp(cstr, "quad") == 0) || 300 (strcmp(cstr, "quadrophonic") == 0)) { 301 chan = 4; 302 } else { 303 delete cstr; 304 return (AUDIO_ERR_BADARG); 305 } 306 delete cstr; 307 } 308 if ((chan <= 0) || (chan > 256)) { 309 return (AUDIO_ERR_BADARG); 310 } 311 channels = (unsigned int) chan; 312 return (AUDIO_SUCCESS); 313 } 314 315 // Parse a string containing the audio encoding 316 AudioError AudioHdr:: 317 EncodingParse( 318 char *str) 319 { 320 static char *lib_bit = NULL; 321 static char *lib_ulaw = NULL; 322 static char *lib_Alaw = NULL; 323 static char *lib_linear = NULL; 324 int i; 325 char *p; 326 char estrbuf[64]; 327 char *estr; 328 char xtrabuf[32]; 329 char *xtra; 330 char *xp; 331 char buf[BUFSIZ]; 332 char *cp; 333 double prec; 334 335 // Init i18n string translations 336 if (lib_bit == NULL) { 337 lib_bit = to_lowercase(_MGET_("bit")); 338 lib_ulaw = to_lowercase(_MGET_("u-law")); 339 lib_Alaw = to_lowercase(_MGET_("A-law")); 340 lib_linear = to_lowercase(_MGET_("linear8")); 341 lib_linear = to_lowercase(_MGET_("linear")); 342 } 343 344 // first copy and remove leading spaces 345 (void) strncpy(buf, str, BUFSIZ); 346 for (cp = buf; *cp == ' '; cp++) 347 continue; 348 349 // Delimit the precision. If there is one, parse it. 350 prec = 0.; 351 p = strchr(cp, ' '); 352 if (p != NULL) { 353 *p++ = '\0'; 354 i = sscanf(cp, " %lf %15s", &prec, xtrabuf); 355 if (i == 0) { 356 return (AUDIO_ERR_BADARG); 357 } 358 if (i == 2) { 359 // convert to lowercase and skip leading "-", if any 360 xtra = to_lowercase(xtrabuf); 361 xp = (xtra[0] == '-') ? &xtra[1] : &xtra[0]; 362 363 if ((strcmp(xp, "bit") != 0) && 364 (strcoll(xp, lib_bit) != 0)) { 365 delete xtra; 366 return (AUDIO_ERR_BADARG); 367 } 368 delete xtra; 369 } 370 if ((prec <= 0.) || (prec > 512.)) { 371 return (AUDIO_ERR_BADARG); 372 } 373 374 // Don't be fooled by "8 bit" 375 i = sscanf(p, " %15s", xtrabuf); 376 if (i == 1) { 377 // convert to lowercase and skip leading "-", if any 378 xtra = to_lowercase(xtrabuf); 379 xp = (xtra[0] == '-') ? &xtra[1] : &xtra[0]; 380 if ((strcmp(xp, "bit") == 0) || 381 (strcoll(xp, lib_bit) == 0)) { 382 xp = strchr(p, ' '); 383 if (xp != NULL) 384 p = xp; 385 else 386 p += strlen(xtrabuf); 387 } 388 delete xtra; 389 } 390 } else { 391 p = cp; 392 } 393 394 i = sscanf(p, " %31s %31s", estrbuf, xtrabuf); 395 396 // If "adpcm" appended with a space, concatenate it 397 if (i == 2) { 398 xtra = to_lowercase(xtrabuf); 399 if (strcmp(xtra, "adpcm") == 0) { 400 (void) strcat(estrbuf, xtra); 401 i = 1; 402 } 403 delete xtra; 404 } 405 if (i == 1) { 406 estr = to_lowercase(estrbuf); 407 if ((strcmp(estr, "ulaw") == 0) || 408 (strcmp(estr, "u-law") == 0) || 409 (strcmp(estr, "�law") == 0) || 410 (strcmp(estr, "�-law") == 0) || 411 (strcmp(estr, "mulaw") == 0) || 412 (strcmp(estr, "mu-law") == 0) || 413 (strcoll(estr, lib_ulaw) == 0)) { 414 if ((prec != 0.) && (prec != 8.)) 415 return (AUDIO_ERR_BADARG); 416 encoding = ULAW; 417 samples_per_unit = 1; 418 bytes_per_unit = 1; 419 } else if ((strcmp(estr, "alaw") == 0) || 420 (strcmp(estr, "a-law") == 0) || 421 (strcoll(estr, lib_Alaw) == 0)) { 422 if ((prec != 0.) && (prec != 8.)) 423 return (AUDIO_ERR_BADARG); 424 encoding = ALAW; 425 samples_per_unit = 1; 426 bytes_per_unit = 1; 427 428 } else if ((strcmp(estr, "linear") == 0) || 429 (strcmp(estr, "lin") == 0) || 430 (strcmp(estr, "pcm") == 0) || 431 (strcoll(estr, lib_linear) == 0)) { 432 if ((prec != 0.) && (prec != 8.) && (prec != 16.) && 433 (prec != 24.) && (prec != 32.)) 434 return (AUDIO_ERR_BADARG); 435 if (prec == 0.) 436 prec = 16.; 437 encoding = LINEAR; 438 samples_per_unit = 1; 439 bytes_per_unit = irint(prec / 8.); 440 441 } else if ((strcmp(estr, "linear8") == 0) || 442 (strcmp(estr, "lin8") == 0) || 443 (strcmp(estr, "pcm8") == 0)) { 444 if ((prec != 0.) && (prec != 8.)) 445 return (AUDIO_ERR_BADARG); 446 prec = 8.; 447 encoding = LINEAR; 448 samples_per_unit = 1; 449 bytes_per_unit = irint(prec / 8.); 450 451 } else if ((strcmp(estr, "linear16") == 0) || 452 (strcmp(estr, "lin16") == 0) || 453 (strcmp(estr, "pcm16") == 0)) { 454 if ((prec != 0.) && (prec != 16.)) 455 return (AUDIO_ERR_BADARG); 456 prec = 16.; 457 encoding = LINEAR; 458 samples_per_unit = 1; 459 bytes_per_unit = irint(prec / 8.); 460 461 } else if ((strcmp(estr, "linear24") == 0) || 462 (strcmp(estr, "lin24") == 0) || 463 (strcmp(estr, "pcm24") == 0)) { 464 if ((prec != 0.) && (prec != 24.)) 465 return (AUDIO_ERR_BADARG); 466 prec = 24.; 467 encoding = LINEAR; 468 samples_per_unit = 1; 469 bytes_per_unit = irint(prec / 8.); 470 471 } else if ((strcmp(estr, "linear32") == 0) || 472 (strcmp(estr, "lin32") == 0) || 473 (strcmp(estr, "pcm32") == 0)) { 474 if ((prec != 0.) && (prec != 32.)) 475 return (AUDIO_ERR_BADARG); 476 prec = 32.; 477 encoding = LINEAR; 478 samples_per_unit = 1; 479 bytes_per_unit = irint(prec / 8.); 480 481 } else if ((strcmp(estr, "float") == 0) || 482 (strcmp(estr, "floatingpoint") == 0) || 483 (strcmp(estr, "floating-point") == 0)) { 484 if ((prec != 0.) && (prec != 32.) && (prec != 64.)) 485 return (AUDIO_ERR_BADARG); 486 if (prec == 0.) 487 prec = 64.; 488 encoding = FLOAT; 489 samples_per_unit = 1; 490 bytes_per_unit = irint(prec / 8.); 491 492 } else if ((strcmp(estr, "float32") == 0) || 493 (strcmp(estr, "floatingpoint32") == 0) || 494 (strcmp(estr, "floating-point32") == 0)) { 495 if ((prec != 0.) && (prec != 32.)) 496 return (AUDIO_ERR_BADARG); 497 prec = 32.; 498 encoding = FLOAT; 499 samples_per_unit = 1; 500 bytes_per_unit = irint(prec / 8.); 501 502 } else if ((strcmp(estr, "float64") == 0) || 503 (strcmp(estr, "double") == 0) || 504 (strcmp(estr, "floatingpoint64") == 0) || 505 (strcmp(estr, "floating-point64") == 0)) { 506 if ((prec != 0.) && (prec != 64.)) 507 return (AUDIO_ERR_BADARG); 508 prec = 64.; 509 encoding = FLOAT; 510 samples_per_unit = 1; 511 bytes_per_unit = irint(prec / 8.); 512 513 } else if ((strcmp(estr, "g.721") == 0) || 514 (strcmp(estr, "g721") == 0) || 515 (strcmp(estr, "g.721adpcm") == 0) || 516 (strcmp(estr, "g721adpcm") == 0)) { 517 if ((prec != 0.) && (prec != 4.)) 518 return (AUDIO_ERR_BADARG); 519 encoding = G721; 520 samples_per_unit = 2; 521 bytes_per_unit = 1; 522 523 } else if ((strcmp(estr, "g.722") == 0) || 524 (strcmp(estr, "g722") == 0) || 525 (strcmp(estr, "g.722adpcm") == 0) || 526 (strcmp(estr, "g722adpcm") == 0)) { 527 if ((prec != 0.) && (prec != 8.)) 528 return (AUDIO_ERR_BADARG); 529 encoding = G722; 530 samples_per_unit = 1; 531 bytes_per_unit = 1; 532 533 } else if ((strcmp(estr, "g.723") == 0) || 534 (strcmp(estr, "g723") == 0) || 535 (strcmp(estr, "g.723adpcm") == 0) || 536 (strcmp(estr, "g723adpcm") == 0)) { 537 if ((prec != 0.) && (prec != 3.) && (prec != 5.)) 538 return (AUDIO_ERR_BADARG); 539 if (prec == 0.) 540 prec = 3.; 541 encoding = G723; 542 samples_per_unit = 8; 543 bytes_per_unit = irint(prec); 544 545 } else if ((strcmp(estr, "g.723-3") == 0) || 546 (strcmp(estr, "g.723_3") == 0) || 547 (strcmp(estr, "g.723.3") == 0) || 548 (strcmp(estr, "g723-3") == 0) || 549 (strcmp(estr, "g723_3") == 0) || 550 (strcmp(estr, "g723.3") == 0)) { 551 if ((prec != 0.) && (prec != 3.)) 552 return (AUDIO_ERR_BADARG); 553 prec = 3.; 554 encoding = G723; 555 samples_per_unit = 8; 556 bytes_per_unit = irint(prec); 557 558 } else if ((strcmp(estr, "g.723-5") == 0) || 559 (strcmp(estr, "g.723_5") == 0) || 560 (strcmp(estr, "g.723.5") == 0) || 561 (strcmp(estr, "g723-5") == 0) || 562 (strcmp(estr, "g723_5") == 0) || 563 (strcmp(estr, "g723.5") == 0)) { 564 if ((prec != 0.) && (prec != 5.)) 565 return (AUDIO_ERR_BADARG); 566 prec = 5.; 567 encoding = G723; 568 samples_per_unit = 8; 569 bytes_per_unit = irint(prec); 570 571 } else if ((strcmp(estr, "dvi") == 0) || 572 (strcmp(estr, "dviadpcm") == 0)) { 573 if ((prec != 0.) && (prec != 4.)) 574 return (AUDIO_ERR_BADARG); 575 encoding = DVI; 576 samples_per_unit = 2; 577 bytes_per_unit = 1; 578 579 } else { 580 delete estr; 581 return (AUDIO_ERR_BADARG); 582 } 583 delete estr; 584 } else { 585 return (AUDIO_ERR_BADARG); 586 } 587 return (AUDIO_SUCCESS); 588 } 589 590 // Parse a string containing the comma-separated audio encoding 591 // Format is: "enc, chan, rate" 592 // XXX - some countries use comma instead of decimal point 593 // so there may be a problem with "44,1 khz" 594 AudioError AudioHdr:: 595 FormatParse( 596 char *str) 597 { 598 char *pstr; 599 char *ptr; 600 char *p; 601 AudioHdr newhdr; 602 AudioError err; 603 604 pstr = new char[strlen(str) + 1]; 605 (void) strcpy(pstr, str); 606 ptr = pstr; 607 608 // Delimit and parse the precision string 609 p = strchr(ptr, ','); 610 if (p == NULL) 611 p = strchr(ptr, ' '); 612 if (p == NULL) { 613 err = AUDIO_ERR_BADARG; 614 goto errret; 615 } 616 *p++ = '\0'; 617 err = newhdr.EncodingParse(ptr); 618 619 // Delimit and parse the sample rate string 620 if (!err) { 621 ptr = p; 622 p = strchr(ptr, ','); 623 if (p == NULL) 624 p = strchr(ptr, ' '); 625 if (p == NULL) { 626 err = AUDIO_ERR_BADARG; 627 goto errret; 628 } 629 *p++ = '\0'; 630 err = newhdr.RateParse(ptr); 631 } 632 633 // Finally, parse the channels string 634 if (!err) { 635 err = newhdr.ChannelParse(p); 636 } 637 638 // Validate the resulting header 639 if (!err) 640 err = newhdr.Validate(); 641 if (!err) 642 *this = newhdr; 643 errret: 644 delete pstr; 645 return (err); 646 } 647