dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / audio / audioconvert / convert.cc
blob3472844ce438a31493611a30a5675c7ac170d80e
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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>
49 #include <convert.h>
52 // Maximum sizes of buffer to convert, in seconds and bytes
53 #define CVTMAXTIME ((double)5.0)
54 #define CVTMAXBUF (64 * 1024)
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)
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)
72 char *enc1;
73 char *enc2;
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);
94 return (0);
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 */)
107 if ((ihdr == ohdr) &&
108 (i_fmt == o_fmt) &&
109 (i_offset == 0)) {
110 return (1);
112 return (0);
116 // Conversion list maintenance routines
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)
123 struct conv_list *lp;
125 for (lp = list; lp != NULL; lp = lp->next) {
126 if (lp->next == NULL)
127 break;
129 return (lp);
132 // Release the conversion list
133 void
134 free_conv_list(
135 struct conv_list *&list)
137 unsigned int i;
138 unsigned int bufs;
139 struct conv_list *tlp;
140 AudioTypeConvert* conv;
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);
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);
155 tlp = list->next;
156 free((char *)list);
157 list = tlp;
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
170 unsigned int i;
171 struct conv_list *lp;
172 struct conv_list *nlp;
173 Boolean B;
175 nlp = new struct conv_list[bufs];
176 if (nlp == NULL) {
177 Err(MGET("out of memory\n"));
178 exit(1);
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;
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;
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.
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)
218 AudioTypeConvert* conv;
219 unsigned int n;
220 char *msg;
222 conv = new AudioTypeMux;
224 // Verify conversion
225 if (!conv->CanConvert(ihdr)) {
226 error: delete conv;
227 return (AUDIO_ERR_FORMATLOCK);
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");
242 if (!conv->CanConvert(ihdr))
243 goto error;
245 append_conv_list(list, ihdr, bufs, conv, msg);
246 bufs = n;
247 return (AUDIO_SUCCESS);
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)
259 AudioTypeConvert* conv;
260 char msg[BUFSIZ];
261 char *infmt;
262 char *outfmt;
263 AudioError err;
265 conv = new AudioTypePcm;
267 // Verify conversion
268 if (!conv->CanConvert(ihdr)) {
269 error: delete conv;
270 return (AUDIO_ERR_FORMATLOCK);
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();
282 sprintf(msg, MGET("Convert %s to %s"), infmt, outfmt);
283 delete infmt;
284 delete outfmt;
286 append_conv_list(list, ihdr, bufs, conv, msg);
287 return (AUDIO_SUCCESS);
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)
298 AudioTypeConvert* conv;
299 char msg[BUFSIZ];
300 char *inchans;
301 char *outchans;
302 AudioError err;
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);
308 conv = new AudioTypeChannel;
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);
316 if (!conv->CanConvert(ihdr)) {
317 error: delete conv;
318 return (AUDIO_ERR_FORMATLOCK);
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();
329 sprintf(msg, MGET("Convert %s to %s"), inchans, outchans);
330 delete inchans;
331 delete outchans;
333 append_conv_list(list, ihdr, bufs, conv, msg);
334 return (AUDIO_SUCCESS);
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)
346 AudioTypeConvert* conv;
347 char msg[BUFSIZ];
348 char *infmt;
349 char *outfmt;
350 struct conv_list *lp;
351 int i;
352 AudioError err;
354 // Make sure we're converting something we understand
355 if ((tofmt != G721) && (tofmt != G723))
356 return (AUDIO_ERR_FORMATLOCK);
358 conv = new AudioTypeG72X;
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);
366 if (!conv->CanConvert(ihdr)) {
367 error: delete conv;
368 return (AUDIO_ERR_FORMATLOCK);
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;
385 if (!conv->CanConvert(ihdr))
386 goto error;
387 outfmt = ihdr.EncodingString();
389 sprintf(msg, MGET("Convert %s to %s"), infmt, outfmt);
390 delete infmt;
391 delete outfmt;
393 append_conv_list(list, ihdr, bufs, NULL, msg);
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;
403 return (AUDIO_SUCCESS);
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)
415 AudioTypeConvert* conv;
416 char msg[BUFSIZ];
417 char *infmt;
418 char *outfmt;
419 struct conv_list *lp;
420 int i;
421 AudioError err;
423 // Make sure we're converting something we understand
424 if ((ihdr.encoding != G721) && (ihdr.encoding != G723))
425 return (AUDIO_ERR_FORMATLOCK);
427 conv = new AudioTypeG72X;
429 // Verify conversion
430 if (!conv->CanConvert(ihdr)) {
431 error: delete conv;
432 return (AUDIO_ERR_FORMATLOCK);
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;
447 outfmt = ihdr.EncodingString();
449 sprintf(msg, MGET("Convert %s to %s"), infmt, outfmt);
450 delete infmt;
451 delete outfmt;
453 append_conv_list(list, ihdr, bufs, NULL, msg);
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;
463 return (AUDIO_SUCCESS);
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)
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;
483 fromrate = ihdr.sample_rate;
484 conv = new AudioTypeSampleRate(fromrate, torate);
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);
492 if (!conv->CanConvert(ihdr)) {
493 error: delete conv;
494 return (AUDIO_ERR_FORMATLOCK);
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();
505 sprintf(msg, MGET("Convert %s to %s"), inrate, outrate);
506 delete inrate;
507 delete outrate;
509 append_conv_list(list, ihdr, bufs, NULL, msg);
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);
519 return (AUDIO_SUCCESS);
522 // Returns TRUE if the specified header has a pcm type encoding
523 Boolean
524 pcmtype(
525 AudioHdr& hdr)
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);
536 return (FALSE);
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)
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)
556 AudioHdr ihdr;
557 AudioHdr ohdr;
558 unsigned int bufs;
559 AudioError err;
561 ihdr = ifp->GetHeader();
562 ohdr = ofp->GetHeader();
563 bufs = 1;
565 // Each pass, add another conversion, until there's no more to do
566 while (((ihdr != ohdr) || (bufs != 1)) && !err) {
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);
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);
595 continue;
597 // At this point, input and output are both mono
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;
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);
623 continue;
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);
644 continue;
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;
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;
661 // Everything should be done at this point.
662 // XXX - this should never be reached
663 return (AUDIO_ERR_FORMATLOCK);
665 return (err);
668 // Set up the conversion list and execute it
670 do_convert(
671 AudioStream* ifp,
672 AudioStream* ofp)
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;
688 ihdr = ifp->GetHeader();
689 ohdr = ofp->GetHeader();
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);
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;
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;
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;
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)"));
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);
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);
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);
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);
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));
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;
775 if (err) {
776 Err(MGET(
777 "Conversion failed: %s (%s)\n"),
778 lp->desc ? lp->desc : MGET("???"),
779 err.msg());
780 return (-1);
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);
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]);
812 if (err)
813 break;
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());
823 /* return (-1); ignore errors for now */
824 break;
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 */
837 delete obuf;
838 free_conv_list(list);
839 return (0);