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 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <stdarg.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/file.h> 35 #include <sys/param.h> 36 #include <Audio.h> 37 #include <AudioFile.h> 38 #include <AudioPipe.h> 39 #include <AudioRawPipe.h> 40 #include <AudioLib.h> 41 #include <AudioTypePcm.h> 42 #include <AudioTypeG72X.h> 43 #include <AudioTypeChannel.h> 44 #include <AudioTypeMux.h> 45 #include <AudioTypeSampleRate.h> 46 47 #include <convert.h> 48 49 50 // Maximum sizes of buffer to convert, in seconds and bytes 51 #define CVTMAXTIME ((double)5.0) 52 #define CVTMAXBUF (64 * 1024) 53 54 // maintain a list of conversions 55 struct conv_list { 56 struct conv_list *next; // next conversion in chain 57 unsigned bufcnt; // number of buffers to process 58 AudioTypeConvert* conv; // conversion class 59 AudioHdr hdr; // what to convert to 60 char *desc; // describe conversion (for errs) 61 }; 62 63 64 // check if this is a valid conversion. return -1 if not, 0 if OK. 65 int 66 verify_conversion( 67 AudioHdr ihdr, 68 AudioHdr ohdr) 69 { 70 char *enc1; 71 char *enc2; 72 73 if (((ihdr.encoding != ULAW) && 74 (ihdr.encoding != ALAW) && 75 (ihdr.encoding != LINEAR) && 76 (ihdr.encoding != FLOAT) && 77 (ihdr.encoding != G721) && 78 (ihdr.encoding != G723)) || 79 ((ohdr.encoding != ULAW) && 80 (ohdr.encoding != ALAW) && 81 (ohdr.encoding != LINEAR) && 82 (ohdr.encoding != FLOAT) && 83 (ohdr.encoding != G721) && 84 (ohdr.encoding != G723))) { 85 enc1 = ihdr.EncodingString(); 86 enc2 = ohdr.EncodingString(); 87 Err(MGET("can't convert from %s to %s\n"), enc1, enc2); 88 delete enc1; 89 delete enc2; 90 return (-1); 91 } 92 return (0); 93 } 94 95 // check if this conversion is a no-op 96 int 97 noop_conversion( 98 AudioHdr ihdr, 99 AudioHdr ohdr, 100 format_type i_fmt, 101 format_type o_fmt, 102 off_t i_offset, 103 off_t /* o_offset */) 104 { 105 if ((ihdr == ohdr) && 106 (i_fmt == o_fmt) && 107 (i_offset == 0)) { 108 return (1); 109 } 110 return (0); 111 } 112 113 114 // Conversion list maintenance routines 115 116 // Return a pointer to the last conversion entry in the list 117 struct conv_list 118 *get_last_conv( 119 struct conv_list *list) 120 { 121 struct conv_list *lp; 122 123 for (lp = list; lp != NULL; lp = lp->next) { 124 if (lp->next == NULL) 125 break; 126 } 127 return (lp); 128 } 129 130 // Release the conversion list 131 void 132 free_conv_list( 133 struct conv_list *&list) 134 { 135 unsigned int i; 136 unsigned int bufs; 137 struct conv_list *tlp; 138 AudioTypeConvert* conv; 139 140 while (list != NULL) { 141 bufs = list->bufcnt; 142 conv = list->conv; 143 for (i = 0; i < bufs; i++) { 144 // Delete the conversion string 145 if (list[i].desc != NULL) 146 free(list[i].desc); 147 148 // Delete the conversion class if unique 149 if ((list[i].conv != NULL) && 150 ((i == 0) || (list[i].conv != conv))) 151 delete(list[i].conv); 152 } 153 tlp = list->next; 154 free((char *)list); 155 list = tlp; 156 } 157 } 158 159 // Append a new entry on the end of the conversion list 160 void 161 append_conv_list( 162 struct conv_list *&list, // list to modify 163 AudioHdr tohdr, // target format 164 unsigned int bufs, // number of buffers involved 165 AudioTypeConvert* conv, // NULL, if multiple buffers 166 char *desc) // string describing the transform 167 { 168 unsigned int i; 169 struct conv_list *lp; 170 struct conv_list *nlp; 171 Boolean B; 172 173 nlp = new struct conv_list[bufs]; 174 if (nlp == NULL) { 175 Err(MGET("out of memory\n")); 176 exit(1); 177 } 178 B = tohdr.Validate(); 179 // Initialize a conversion entry for each expected buffer 180 for (i = 0; i < bufs; i++) { 181 nlp[i].next = NULL; 182 nlp[i].hdr = tohdr; 183 B = nlp[i].hdr.Validate(); 184 nlp[i].bufcnt = bufs; 185 nlp[i].conv = conv; 186 if (desc && *desc) { 187 nlp[i].desc = strdup(desc); 188 } else { 189 nlp[i].desc = NULL; 190 } 191 } 192 193 // Link in the new entry 194 if (list == NULL) { 195 list = nlp; 196 } else { 197 lp = get_last_conv(list); 198 lp->next = nlp; 199 } 200 } 201 202 203 // Routines to establish specific conversions. 204 // These routines append the proper conversion to the list, and update 205 // the audio header structure to reflect the resulting data format. 206 207 // Multiplex/Demultiplex interleaved data 208 // If the data is multi-channel, demultiplex into multiple buffer streams. 209 // If there are multiple buffers, multiplex back into one interleaved stream. 210 AudioError 211 add_mux_convert( 212 struct conv_list *&list, 213 AudioHdr& ihdr, 214 unsigned int& bufs) 215 { 216 AudioTypeConvert* conv; 217 unsigned int n; 218 char *msg; 219 220 conv = new AudioTypeMux; 221 222 // Verify conversion 223 if (!conv->CanConvert(ihdr)) { 224 error: delete conv; 225 return (AUDIO_ERR_FORMATLOCK); 226 } 227 228 if (bufs == 1) { 229 // Demultiplex multi-channel data 230 n = ihdr.channels; // save the target number of buffers 231 ihdr.channels = 1; // each output buffer will be mono 232 msg = MGET("Split multi-channel data"); 233 } else { 234 // Multiplex multiple buffers 235 ihdr.channels = bufs; // set the target interleave 236 n = 1; 237 bufs = 1; // just one conversion necessary 238 msg = MGET("Interleave multi-channel data"); 239 } 240 if (!conv->CanConvert(ihdr)) 241 goto error; 242 243 append_conv_list(list, ihdr, bufs, conv, msg); 244 bufs = n; 245 return (AUDIO_SUCCESS); 246 } 247 248 // Convert to PCM (linear, ulaw, alaw) 249 AudioError 250 add_pcm_convert( 251 struct conv_list *&list, 252 AudioHdr& ihdr, 253 AudioEncoding tofmt, 254 unsigned int unitsz, 255 unsigned int& bufs) 256 { 257 AudioTypeConvert* conv; 258 char msg[BUFSIZ]; 259 char *infmt; 260 char *outfmt; 261 AudioError err; 262 263 conv = new AudioTypePcm; 264 265 // Verify conversion 266 if (!conv->CanConvert(ihdr)) { 267 error: delete conv; 268 return (AUDIO_ERR_FORMATLOCK); 269 } 270 271 // Set up conversion, get encoding strings 272 infmt = ihdr.EncodingString(); 273 ihdr.encoding = tofmt; 274 ihdr.bytes_per_unit = unitsz; 275 ihdr.samples_per_unit = 1; 276 if (!conv->CanConvert(ihdr)) 277 goto error; 278 outfmt = ihdr.EncodingString(); 279 280 sprintf(msg, MGET("Convert %s to %s"), infmt, outfmt); 281 delete infmt; 282 delete outfmt; 283 284 append_conv_list(list, ihdr, bufs, conv, msg); 285 return (AUDIO_SUCCESS); 286 } 287 288 // Convert multi-channel data to mono, or vice versa 289 AudioError 290 add_channel_convert( 291 struct conv_list *&list, 292 AudioHdr& ihdr, 293 unsigned int tochans, 294 unsigned int& bufs) 295 { 296 AudioTypeConvert* conv; 297 char msg[BUFSIZ]; 298 char *inchans; 299 char *outchans; 300 AudioError err; 301 302 // Make sure we're converting to/from mono with an interleaved buffer 303 if (((ihdr.channels != 1) && (tochans != 1)) || (bufs != 1)) 304 return (AUDIO_ERR_FORMATLOCK); 305 306 conv = new AudioTypeChannel; 307 308 // Verify conversion; if no good, try converting to 16-bit pcm first 309 if (!conv->CanConvert(ihdr) || (ihdr.channels != 1)) { 310 if (err = add_pcm_convert(list, ihdr, LINEAR, 2, bufs)) { 311 delete conv; 312 return (err); 313 } 314 if (!conv->CanConvert(ihdr)) { 315 error: delete conv; 316 return (AUDIO_ERR_FORMATLOCK); 317 } 318 } 319 320 // Set up conversion, get channel strings 321 inchans = ihdr.ChannelString(); 322 ihdr.channels = tochans; 323 if (!conv->CanConvert(ihdr)) 324 goto error; 325 outchans = ihdr.ChannelString(); 326 327 sprintf(msg, MGET("Convert %s to %s"), inchans, outchans); 328 delete inchans; 329 delete outchans; 330 331 append_conv_list(list, ihdr, bufs, conv, msg); 332 return (AUDIO_SUCCESS); 333 } 334 335 // Compress data 336 AudioError 337 add_compress( 338 struct conv_list *&list, 339 AudioHdr& ihdr, 340 AudioEncoding tofmt, 341 unsigned int unitsz, 342 unsigned int& bufs) 343 { 344 AudioTypeConvert* conv; 345 char msg[BUFSIZ]; 346 char *infmt; 347 char *outfmt; 348 struct conv_list *lp; 349 int i; 350 AudioError err; 351 352 // Make sure we're converting something we understand 353 if ((tofmt != G721) && (tofmt != G723)) 354 return (AUDIO_ERR_FORMATLOCK); 355 356 conv = new AudioTypeG72X; 357 358 // Verify conversion; if no good, try converting to 16-bit pcm first 359 if (!conv->CanConvert(ihdr)) { 360 if (err = add_pcm_convert(list, ihdr, LINEAR, 2, bufs)) { 361 delete conv; 362 return (err); 363 } 364 if (!conv->CanConvert(ihdr)) { 365 error: delete conv; 366 return (AUDIO_ERR_FORMATLOCK); 367 } 368 } 369 370 // Set up conversion, get encoding strings 371 infmt = ihdr.EncodingString(); 372 ihdr.encoding = tofmt; 373 switch (tofmt) { 374 case G721: 375 ihdr.bytes_per_unit = unitsz; 376 ihdr.samples_per_unit = 2; 377 break; 378 case G723: 379 ihdr.bytes_per_unit = unitsz; 380 ihdr.samples_per_unit = 8; 381 break; 382 } 383 if (!conv->CanConvert(ihdr)) 384 goto error; 385 outfmt = ihdr.EncodingString(); 386 387 sprintf(msg, MGET("Convert %s to %s"), infmt, outfmt); 388 delete infmt; 389 delete outfmt; 390 391 append_conv_list(list, ihdr, bufs, NULL, msg); 392 393 // Need a separate converter instantiation for each channel 394 lp = get_last_conv(list); 395 for (i = 0; i < bufs; i++) { 396 if (i == 0) 397 lp[i].conv = conv; 398 else 399 lp[i].conv = new AudioTypeG72X; 400 } 401 return (AUDIO_SUCCESS); 402 } 403 404 // Decompress data 405 AudioError 406 add_decompress( 407 struct conv_list *&list, 408 AudioHdr& ihdr, 409 AudioEncoding tofmt, 410 unsigned int unitsz, 411 unsigned int& bufs) 412 { 413 AudioTypeConvert* conv; 414 char msg[BUFSIZ]; 415 char *infmt; 416 char *outfmt; 417 struct conv_list *lp; 418 int i; 419 AudioError err; 420 421 // Make sure we're converting something we understand 422 if ((ihdr.encoding != G721) && (ihdr.encoding != G723)) 423 return (AUDIO_ERR_FORMATLOCK); 424 425 conv = new AudioTypeG72X; 426 427 // Verify conversion 428 if (!conv->CanConvert(ihdr)) { 429 error: delete conv; 430 return (AUDIO_ERR_FORMATLOCK); 431 } 432 433 // Set up conversion, get encoding strings 434 infmt = ihdr.EncodingString(); 435 ihdr.encoding = tofmt; 436 ihdr.bytes_per_unit = unitsz; 437 ihdr.samples_per_unit = 1; 438 if (!conv->CanConvert(ihdr)) { 439 // Try converting to 16-bit linear 440 ihdr.encoding = LINEAR; 441 ihdr.bytes_per_unit = 2; 442 if (!conv->CanConvert(ihdr)) 443 goto error; 444 } 445 outfmt = ihdr.EncodingString(); 446 447 sprintf(msg, MGET("Convert %s to %s"), infmt, outfmt); 448 delete infmt; 449 delete outfmt; 450 451 append_conv_list(list, ihdr, bufs, NULL, msg); 452 453 // Need a separate converter instantiation for each channel 454 lp = get_last_conv(list); 455 for (i = 0; i < bufs; i++) { 456 if (i == 0) 457 lp[i].conv = conv; 458 else 459 lp[i].conv = new AudioTypeG72X; 460 } 461 return (AUDIO_SUCCESS); 462 } 463 464 // Sample rate conversion 465 AudioError 466 add_rate_convert( 467 struct conv_list *&list, 468 AudioHdr& ihdr, 469 unsigned int torate, 470 unsigned int& bufs) 471 { 472 AudioTypeConvert* conv; 473 unsigned int fromrate; 474 char msg[BUFSIZ]; 475 char *inrate; 476 char *outrate; 477 struct conv_list *lp; 478 int i; 479 AudioError err; 480 481 fromrate = ihdr.sample_rate; 482 conv = new AudioTypeSampleRate(fromrate, torate); 483 484 // Verify conversion; if no good, try converting to 16-bit pcm first 485 if (!conv->CanConvert(ihdr)) { 486 if (err = add_pcm_convert(list, ihdr, LINEAR, 2, bufs)) { 487 delete conv; 488 return (err); 489 } 490 if (!conv->CanConvert(ihdr)) { 491 error: delete conv; 492 return (AUDIO_ERR_FORMATLOCK); 493 } 494 } 495 496 // Set up conversion, get encoding strings 497 inrate = ihdr.RateString(); 498 ihdr.sample_rate = torate; 499 if (!conv->CanConvert(ihdr)) 500 goto error; 501 outrate = ihdr.RateString(); 502 503 sprintf(msg, MGET("Convert %s to %s"), inrate, outrate); 504 delete inrate; 505 delete outrate; 506 507 append_conv_list(list, ihdr, bufs, NULL, msg); 508 509 // Need a separate converter instantiation for each channel 510 lp = get_last_conv(list); 511 for (i = 0; i < bufs; i++) { 512 if (i == 0) 513 lp[i].conv = conv; 514 else 515 lp[i].conv = new AudioTypeSampleRate(fromrate, torate); 516 } 517 return (AUDIO_SUCCESS); 518 } 519 520 // Returns TRUE if the specified header has a pcm type encoding 521 Boolean 522 pcmtype( 523 AudioHdr& hdr) 524 { 525 if (hdr.samples_per_unit != 1) 526 return (FALSE); 527 switch (hdr.encoding) { 528 case LINEAR: 529 case FLOAT: 530 case ULAW: 531 case ALAW: 532 return (TRUE); 533 } 534 return (FALSE); 535 } 536 537 #define IS_PCM(ihp) (pcmtype(ihp)) 538 #define IS_MONO(ihp) (ihp.channels == 1) 539 #define RATE_CONV(ihp, ohp) (ihp.sample_rate != ohp.sample_rate) 540 #define ENC_CONV(ihp, ohp) ((ihp.encoding != ohp.encoding) || \ 541 (ihp.samples_per_unit != \ 542 ohp.samples_per_unit) || \ 543 (ihp.bytes_per_unit != ohp.bytes_per_unit)) 544 #define CHAN_CONV(ihp, ohp) (ihp.channels != ohp.channels) 545 546 547 // Build the conversion list to get from input to output format 548 AudioError 549 build_conversion_list( 550 struct conv_list *&list, 551 AudioStream* ifp, 552 AudioStream* ofp) 553 { 554 AudioHdr ihdr; 555 AudioHdr ohdr; 556 unsigned int bufs; 557 AudioError err; 558 559 ihdr = ifp->GetHeader(); 560 ohdr = ofp->GetHeader(); 561 bufs = 1; 562 563 // Each pass, add another conversion, until there's no more to do 564 while (((ihdr != ohdr) || (bufs != 1)) && !err) { 565 566 // First off, if the target is mono, convert the source to mono 567 // before doing harder stuff, like sample rate conversion. 568 if (IS_MONO(ohdr)) { 569 if (!IS_MONO(ihdr)) { 570 if (IS_PCM(ihdr)) { 571 // If multi-channel pcm, 572 // mix the channels down to one 573 err = add_channel_convert(list, 574 ihdr, 1, bufs); 575 } else { 576 // If not pcm, demultiplex in order 577 // to decompress 578 err = add_mux_convert(list, ihdr, bufs); 579 } 580 continue; 581 } else if (bufs != 1) { 582 // Multi-channel data was demultiplexed 583 if (IS_PCM(ihdr)) { 584 // If multi-channel pcm, recombine them 585 // for mixing down to one 586 err = add_mux_convert(list, ihdr, bufs); 587 } else { 588 // If not pcm, decompress it 589 err = add_decompress(list, ihdr, 590 ohdr.encoding, ohdr.bytes_per_unit, 591 bufs); 592 } 593 continue; 594 } 595 // At this point, input and output are both mono 596 597 } else if (ihdr.channels != 1) { 598 // Here if input and output are both multi-channel. 599 // If sample rate conversion or compression, 600 // split into multiple streams 601 if (RATE_CONV(ihdr, ohdr) || 602 (ENC_CONV(ihdr, ohdr) && 603 (!IS_PCM(ihdr) || !IS_PCM(ohdr)))) { 604 err = add_mux_convert(list, ihdr, bufs); 605 continue; 606 } 607 } 608 609 // Input is either mono, split into multiple buffers, or 610 // this is a conversion that can be handled multi-channel. 611 if (RATE_CONV(ihdr, ohdr)) { 612 // Decompress before sample-rate conversion 613 if (!IS_PCM(ihdr)) { 614 err = add_decompress(list, ihdr, 615 ohdr.encoding, ohdr.bytes_per_unit, 616 bufs); 617 } else { 618 err = add_rate_convert(list, ihdr, 619 ohdr.sample_rate, bufs); 620 } 621 continue; 622 } 623 624 if (ENC_CONV(ihdr, ohdr)) { 625 // Encoding is changing: 626 if (!IS_PCM(ihdr)) { 627 // if we start compressed, decompress 628 err = add_decompress(list, ihdr, 629 ohdr.encoding, ohdr.bytes_per_unit, 630 bufs); 631 } else if (IS_PCM(ohdr)) { 632 // we should be able to convert to PCM now 633 err = add_pcm_convert(list, ihdr, 634 ohdr.encoding, ohdr.bytes_per_unit, 635 bufs); 636 } else { 637 // we should be able to compress now 638 err = add_compress(list, ihdr, 639 ohdr.encoding, ohdr.bytes_per_unit, 640 bufs); 641 } 642 continue; 643 } 644 645 // The sample rate and encoding match. 646 // All that's left to do is get the channels right 647 if (bufs > 1) { 648 // Combine channels back into an interleaved stream 649 err = add_mux_convert(list, ihdr, bufs); 650 continue; 651 } 652 if (!IS_MONO(ohdr)) { 653 // If multi-channel output, try to accomodate 654 err = add_channel_convert(list, 655 ihdr, ohdr.channels, bufs); 656 continue; 657 } 658 659 // Everything should be done at this point. 660 // XXX - this should never be reached 661 return (AUDIO_ERR_FORMATLOCK); 662 } 663 return (err); 664 } 665 666 // Set up the conversion list and execute it 667 int 668 do_convert( 669 AudioStream* ifp, 670 AudioStream* ofp) 671 { 672 struct conv_list *list = NULL; 673 struct conv_list *lp; 674 AudioBuffer* obuf; 675 AudioBuffer** multibuf; 676 AudioError err; 677 AudioHdr ihdr; 678 AudioHdr ohdr; 679 Double pos = 0.0; 680 size_t len; 681 unsigned int i; 682 Double cvtlen; 683 char *msg1; 684 char *msg2; 685 686 ihdr = ifp->GetHeader(); 687 ohdr = ofp->GetHeader(); 688 689 // create conversion list 690 if ((err = build_conversion_list(list, ifp, ofp)) != AUDIO_SUCCESS) { 691 free_conv_list(list); 692 msg1 = ohdr.FormatString(); 693 Err(MGET("Cannot convert %s to %s\n"), ifp->GetName(), msg1); 694 delete msg1; 695 return (-1); 696 } 697 698 // Print warnings for exceptional conditions 699 if ((ohdr.sample_rate < 8000) || (ohdr.sample_rate > 48000)) { 700 msg1 = ohdr.RateString(); 701 Err(MGET("Warning: converting %s to %s\n"), 702 ifp->GetName(), msg1); 703 delete msg1; 704 } 705 if (ohdr.channels > 2) { 706 msg1 = ohdr.ChannelString(); 707 Err(MGET("Warning: converting %s to %s\n"), 708 ifp->GetName(), msg1); 709 delete msg1; 710 } 711 712 if (Debug) { 713 msg1 = ihdr.FormatString(); 714 msg2 = ohdr.FormatString(); 715 Err(MGET("Converting %s:\n\t\tfrom: %s\n\t\tto: %s\n"), 716 ifp->GetName(), msg1, msg2); 717 delete msg1; 718 delete msg2; 719 720 // Print each entry in the conversion list 721 for (lp = list; lp; lp = lp->next) { 722 (void) fprintf(stderr, MGET("\t%s %s\n"), lp->desc, 723 (lp->bufcnt == 1) ? "" : MGET("(multi-channel)")); 724 } 725 } 726 727 // Calculate buffer size, obeying maximums 728 cvtlen = ihdr.Bytes_to_Time(CVTMAXBUF); 729 if (cvtlen > CVTMAXTIME) 730 cvtlen = CVTMAXTIME; 731 if (cvtlen > ohdr.Bytes_to_Time(CVTMAXBUF * 4)) 732 cvtlen = ohdr.Bytes_to_Time(CVTMAXBUF * 4); 733 734 // create output buf 735 if (!(obuf = new AudioBuffer(cvtlen, MGET("Audio Convert Buffer")))) { 736 Err(MGET("Can't create conversion buffer\n")); 737 exit(1); 738 } 739 740 while (1) { 741 // Reset length 742 len = (size_t)ihdr.Time_to_Bytes(cvtlen); 743 if ((err = obuf->SetHeader(ihdr)) != AUDIO_SUCCESS) { 744 Err(MGET("Can't set buffer header: %s\n"), err.msg()); 745 return (-1); 746 } 747 // If growing buffer, free the old one rather than copy data 748 if (obuf->GetSize() < cvtlen) 749 obuf->SetSize(0.); 750 obuf->SetSize(cvtlen); 751 752 // Read a chunk of input and set the real length of buffer 753 // XXX - Use Copy() method?? Check for errors? 754 if (err = ifp->ReadData(obuf->GetAddress(), len, pos)) 755 break; 756 obuf->SetLength(ihdr.Bytes_to_Time(len)); 757 758 // Process each entry in the conversion list 759 for (lp = list; lp; lp = lp->next) { 760 if (lp->conv) { 761 // If multiple buffers, make multiple calls 762 if (lp->bufcnt == 1) { 763 err = lp->conv->Convert(obuf, lp->hdr); 764 } else { 765 multibuf = (AudioBuffer**)obuf; 766 for (i = 0; i < lp->bufcnt; i++) { 767 err = lp[i].conv->Convert( 768 multibuf[i], lp[i].hdr); 769 if (err) 770 break; 771 } 772 } 773 if (err) { 774 Err(MGET( 775 "Conversion failed: %s (%s)\n"), 776 lp->desc ? lp->desc : MGET("???"), 777 err.msg()); 778 return (-1); 779 } 780 } 781 } 782 783 if ((err = write_output(obuf, ofp)) != AUDIO_SUCCESS) { 784 Err(MGET("Error writing to output file %s (%s)\n"), 785 ofp->GetName(), err.msg()); 786 return (-1); 787 } 788 } 789 790 // Now flush any left overs from conversions w/state 791 obuf->SetLength(0.0); 792 for (lp = list; lp; lp = lp->next) { 793 if (lp->conv) { 794 // First check if there's any residual to convert. 795 // If not, just set the header to this type. 796 // If multiple buffers, make multiple calls 797 if (lp->bufcnt == 1) { 798 err = lp->conv->Convert(obuf, lp->hdr); 799 if (!err) 800 err = lp->conv->Flush(obuf); 801 } else { 802 multibuf = (AudioBuffer**)obuf; 803 for (i = 0; i < lp->bufcnt; i++) { 804 err = lp[i].conv->Convert( 805 multibuf[i], lp[i].hdr); 806 if (!err) { 807 err = lp[i].conv->Flush( 808 multibuf[i]); 809 } 810 if (err) 811 break; 812 } 813 } 814 if (err) { 815 Err(MGET( 816 "Warning: Flush of final bytes failed: " 817 "%s (%s)\n"), 818 lp->desc ? lp->desc : MGET("???"), 819 err.msg()); 820 821 /* return (-1); ignore errors for now */ 822 break; 823 } 824 } 825 } 826 827 if (obuf->GetLength() > 0.0) { 828 if ((err = write_output(obuf, ofp)) != AUDIO_SUCCESS) { 829 Err(MGET("Warning: Final write to %s failed (%s)\n"), 830 ofp->GetName(), err.msg()); 831 /* return (-1); ignore errors for now */ 832 } 833 } 834 835 delete obuf; 836 free_conv_list(list); 837 return (0); 838 } 839