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