xref: /titanic_44/usr/src/cmd/audio/audioconvert/convert.cc (revision 2b4a78020b9c38d1b95e2f3fefa6d6e4be382d1f)
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