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
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
34 #include <sys/types.h>
37 #include <sys/param.h>
39 #include <AudioFile.h>
40 #include <AudioPipe.h>
41 #include <AudioRawPipe.h>
43 #include <AudioTypePcm.h>
44 #include <AudioTypeG72X.h>
45 #include <AudioTypeChannel.h>
46 #include <AudioTypeMux.h>
47 #include <AudioTypeSampleRate.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
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.
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
);
97 // check if this conversion is a no-op
105 off_t
/* o_offset */)
107 if ((ihdr
== ohdr
) &&
116 // Conversion list maintenance routines
118 // Return a pointer to the last conversion entry in the list
121 struct conv_list
*list
)
123 struct conv_list
*lp
;
125 for (lp
= list
; lp
!= NULL
; lp
= lp
->next
) {
126 if (lp
->next
== NULL
)
132 // Release the conversion list
135 struct conv_list
*&list
)
139 struct conv_list
*tlp
;
140 AudioTypeConvert
* conv
;
142 while (list
!= NULL
) {
145 for (i
= 0; i
< bufs
; i
++) {
146 // Delete the conversion string
147 if (list
[i
].desc
!= NULL
)
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
);
161 // Append a new entry on the end of the conversion 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
171 struct conv_list
*lp
;
172 struct conv_list
*nlp
;
175 nlp
= new struct conv_list
[bufs
];
177 Err(MGET("out of memory\n"));
180 B
= tohdr
.Validate();
181 // Initialize a conversion entry for each expected buffer
182 for (i
= 0; i
< bufs
; i
++) {
185 B
= nlp
[i
].hdr
.Validate();
186 nlp
[i
].bufcnt
= bufs
;
189 nlp
[i
].desc
= strdup(desc
);
195 // Link in the new entry
199 lp
= get_last_conv(list
);
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.
214 struct conv_list
*&list
,
218 AudioTypeConvert
* conv
;
222 conv
= new AudioTypeMux
;
225 if (!conv
->CanConvert(ihdr
)) {
227 return (AUDIO_ERR_FORMATLOCK
);
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");
236 // Multiplex multiple buffers
237 ihdr
.channels
= bufs
; // set the target interleave
239 bufs
= 1; // just one conversion necessary
240 msg
= MGET("Interleave multi-channel data");
242 if (!conv
->CanConvert(ihdr
))
245 append_conv_list(list
, ihdr
, bufs
, conv
, msg
);
247 return (AUDIO_SUCCESS
);
250 // Convert to PCM (linear, ulaw, alaw)
253 struct conv_list
*&list
,
259 AudioTypeConvert
* conv
;
265 conv
= new AudioTypePcm
;
268 if (!conv
->CanConvert(ihdr
)) {
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
))
280 outfmt
= ihdr
.EncodingString();
282 sprintf(msg
, MGET("Convert %s to %s"), infmt
, outfmt
);
286 append_conv_list(list
, ihdr
, bufs
, conv
, msg
);
287 return (AUDIO_SUCCESS
);
290 // Convert multi-channel data to mono, or vice versa
293 struct conv_list
*&list
,
295 unsigned int tochans
,
298 AudioTypeConvert
* conv
;
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
)) {
316 if (!conv
->CanConvert(ihdr
)) {
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
))
327 outchans
= ihdr
.ChannelString();
329 sprintf(msg
, MGET("Convert %s to %s"), inchans
, outchans
);
333 append_conv_list(list
, ihdr
, bufs
, conv
, msg
);
334 return (AUDIO_SUCCESS
);
340 struct conv_list
*&list
,
346 AudioTypeConvert
* conv
;
350 struct conv_list
*lp
;
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
)) {
366 if (!conv
->CanConvert(ihdr
)) {
368 return (AUDIO_ERR_FORMATLOCK
);
372 // Set up conversion, get encoding strings
373 infmt
= ihdr
.EncodingString();
374 ihdr
.encoding
= tofmt
;
377 ihdr
.bytes_per_unit
= unitsz
;
378 ihdr
.samples_per_unit
= 2;
381 ihdr
.bytes_per_unit
= unitsz
;
382 ihdr
.samples_per_unit
= 8;
385 if (!conv
->CanConvert(ihdr
))
387 outfmt
= ihdr
.EncodingString();
389 sprintf(msg
, MGET("Convert %s to %s"), infmt
, 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
++) {
401 lp
[i
].conv
= new AudioTypeG72X
;
403 return (AUDIO_SUCCESS
);
409 struct conv_list
*&list
,
415 AudioTypeConvert
* conv
;
419 struct conv_list
*lp
;
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
;
430 if (!conv
->CanConvert(ihdr
)) {
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
))
447 outfmt
= ihdr
.EncodingString();
449 sprintf(msg
, MGET("Convert %s to %s"), infmt
, 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
++) {
461 lp
[i
].conv
= new AudioTypeG72X
;
463 return (AUDIO_SUCCESS
);
466 // Sample rate conversion
469 struct conv_list
*&list
,
474 AudioTypeConvert
* conv
;
475 unsigned int fromrate
;
479 struct conv_list
*lp
;
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
)) {
492 if (!conv
->CanConvert(ihdr
)) {
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
))
503 outrate
= ihdr
.RateString();
505 sprintf(msg
, MGET("Convert %s to %s"), inrate
, 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
++) {
517 lp
[i
].conv
= new AudioTypeSampleRate(fromrate
, torate
);
519 return (AUDIO_SUCCESS
);
522 // Returns TRUE if the specified header has a pcm type encoding
527 if (hdr
.samples_per_unit
!= 1)
529 switch (hdr
.encoding
) {
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
551 build_conversion_list(
552 struct conv_list
*&list
,
561 ihdr
= ifp
->GetHeader();
562 ohdr
= ofp
->GetHeader();
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.
571 if (!IS_MONO(ihdr
)) {
573 // If multi-channel pcm,
574 // mix the channels down to one
575 err
= add_channel_convert(list
,
578 // If not pcm, demultiplex in order
580 err
= add_mux_convert(list
, ihdr
, bufs
);
583 } else if (bufs
!= 1) {
584 // Multi-channel data was demultiplexed
586 // If multi-channel pcm, recombine them
587 // for mixing down to one
588 err
= add_mux_convert(list
, ihdr
, bufs
);
590 // If not pcm, decompress it
591 err
= add_decompress(list
, ihdr
,
592 ohdr
.encoding
, ohdr
.bytes_per_unit
,
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
);
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
616 err
= add_decompress(list
, ihdr
,
617 ohdr
.encoding
, ohdr
.bytes_per_unit
,
620 err
= add_rate_convert(list
, ihdr
,
621 ohdr
.sample_rate
, bufs
);
626 if (ENC_CONV(ihdr
, ohdr
)) {
627 // Encoding is changing:
629 // if we start compressed, decompress
630 err
= add_decompress(list
, ihdr
,
631 ohdr
.encoding
, ohdr
.bytes_per_unit
,
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
,
639 // we should be able to compress now
640 err
= add_compress(list
, ihdr
,
641 ohdr
.encoding
, ohdr
.bytes_per_unit
,
647 // The sample rate and encoding match.
648 // All that's left to do is get the channels right
650 // Combine channels back into an interleaved stream
651 err
= add_mux_convert(list
, ihdr
, bufs
);
654 if (!IS_MONO(ohdr
)) {
655 // If multi-channel output, try to accomodate
656 err
= add_channel_convert(list
,
657 ihdr
, ohdr
.channels
, bufs
);
661 // Everything should be done at this point.
662 // XXX - this should never be reached
663 return (AUDIO_ERR_FORMATLOCK
);
668 // Set up the conversion list and execute it
674 struct conv_list
*list
= NULL
;
675 struct conv_list
*lp
;
677 AudioBuffer
** multibuf
;
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
);
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
);
707 if (ohdr
.channels
> 2) {
708 msg1
= ohdr
.ChannelString();
709 Err(MGET("Warning: converting %s to %s\n"),
710 ifp
->GetName(), msg1
);
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
);
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
)
733 if (cvtlen
> ohdr
.Bytes_to_Time(CVTMAXBUF
* 4))
734 cvtlen
= ohdr
.Bytes_to_Time(CVTMAXBUF
* 4);
737 if (!(obuf
= new AudioBuffer(cvtlen
, MGET("Audio Convert Buffer")))) {
738 Err(MGET("Can't create conversion buffer\n"));
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());
749 // If growing buffer, free the old one rather than copy data
750 if (obuf
->GetSize() < cvtlen
)
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
))
758 obuf
->SetLength(ihdr
.Bytes_to_Time(len
));
760 // Process each entry in the conversion list
761 for (lp
= list
; lp
; lp
= lp
->next
) {
763 // If multiple buffers, make multiple calls
764 if (lp
->bufcnt
== 1) {
765 err
= lp
->conv
->Convert(obuf
, lp
->hdr
);
767 multibuf
= (AudioBuffer
**)obuf
;
768 for (i
= 0; i
< lp
->bufcnt
; i
++) {
769 err
= lp
[i
].conv
->Convert(
770 multibuf
[i
], lp
[i
].hdr
);
777 "Conversion failed: %s (%s)\n"),
778 lp
->desc
? lp
->desc
: MGET("???"),
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());
792 // Now flush any left overs from conversions w/state
793 obuf
->SetLength(0.0);
794 for (lp
= list
; lp
; lp
= lp
->next
) {
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
);
802 err
= lp
->conv
->Flush(obuf
);
804 multibuf
= (AudioBuffer
**)obuf
;
805 for (i
= 0; i
< lp
->bufcnt
; i
++) {
806 err
= lp
[i
].conv
->Convert(
807 multibuf
[i
], lp
[i
].hdr
);
809 err
= lp
[i
].conv
->Flush(
818 "Warning: Flush of final bytes failed: "
820 lp
->desc
? lp
->desc
: MGET("???"),
823 /* return (-1); ignore errors for now */
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 */
838 free_conv_list(list
);