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 (c) 1993-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdlib.h>
30 #include <memory.h>
31 #include <math.h>
32 
33 #include <AudioTypeChannel.h>
34 
35 // This is a conversion class for channel conversions
36 // It handles mono->multi-channel and multi-channel->mono (mixing)
37 
38 // class AudioTypeChannel methods
39 
40 // Constructor
41 AudioTypeChannel::
AudioTypeChannel()42 AudioTypeChannel()
43 {
44 }
45 
46 // Destructor
47 AudioTypeChannel::
~AudioTypeChannel()48 ~AudioTypeChannel()
49 {
50 }
51 
52 // Test conversion possibilities.
53 // Return TRUE if conversion to/from the specified type is possible.
54 Boolean AudioTypeChannel::
CanConvert(AudioHdr) const55 CanConvert(
56 	AudioHdr	/* h */) const		// target header
57 {
58 	// XXX - This is misleading.  Multi-channel->mono conversions
59 	//	 must be linear format, but mono->multi-channel is
60 	//	 ok in any format.
61 	return (TRUE);
62 }
63 
64 // Convert buffer to the specified type
65 // May replace the buffer with a new one, if necessary
66 AudioError AudioTypeChannel::
Convert(AudioBuffer * & inbuf,AudioHdr outhdr)67 Convert(
68 	AudioBuffer*&	inbuf,			// data buffer to process
69 	AudioHdr	outhdr)			// target header
70 {
71 	AudioBuffer*	outbuf;
72 	AudioHdr	inhdr;
73 	AudioHdr	newhdr;
74 	Double		length;
75 	size_t		nsamps;
76 	size_t		nbytes;
77 	int		i;
78 	int		j;
79 	int		k;
80 	int		chans;
81 	char		*cin;
82 	char		*cout;
83 	short		*sin;
84 	short		*sout;
85 	AudioError	err;
86 	long		smix;
87 
88 	inhdr = inbuf->GetHeader();
89 	length = inbuf->GetLength();
90 
91 	// Make sure we're not being asked to do the impossible or trivial
92 	if ((err = inhdr.Validate()))
93 		return (err);
94 	if ((inhdr.sample_rate != outhdr.sample_rate) ||
95 	    (inhdr.encoding != outhdr.encoding) ||
96 	    (inhdr.samples_per_unit != outhdr.samples_per_unit) ||
97 	    (inhdr.bytes_per_unit != outhdr.bytes_per_unit))
98 		return (AUDIO_ERR_HDRINVAL);
99 	if (inhdr.channels == outhdr.channels)
100 		return (AUDIO_SUCCESS);
101 	if ((inhdr.channels != 1) && (outhdr.channels != 1))
102 		return (AUDIO_ERR_HDRINVAL);
103 	if (Undefined(length))
104 		return (AUDIO_ERR_BADARG);
105 
106 	// setup header for output buffer
107 	newhdr = inhdr;
108 	newhdr.channels = outhdr.channels;
109 
110 	// XXX - If multi-channel -> mono, must be linear to mix
111 	// We need to test for this before trying the conversion!
112 	if ((inhdr.channels > 1) && (newhdr.channels == 1)) {
113 		if ((inhdr.encoding != LINEAR) ||
114 		    (inhdr.bytes_per_unit > 2))
115 			return (AUDIO_ERR_HDRINVAL);
116 	}
117 
118 	// Allocate a new buffer
119 	outbuf = new AudioBuffer(length, "(Channel conversion buffer)");
120 	if (outbuf == 0)
121 		return (AUDIO_UNIXERROR);
122 	if (err = outbuf->SetHeader(newhdr)) {
123 		delete outbuf;
124 		return (err);
125 	}
126 
127 	// Get the number of sample frames and the size of each
128 	nsamps = (size_t)inhdr.Time_to_Samples(length);
129 	nbytes = (size_t)inhdr.FrameLength();
130 	chans = inhdr.channels;
131 
132 	// multi-channel -> mono conversion
133 	if ((chans > 1) && (newhdr.channels == 1)) {
134 		switch (inhdr.bytes_per_unit) {
135 		case 1:
136 			cin = (char *)inbuf->GetAddress();
137 			cout = (char *)outbuf->GetAddress();
138 
139 			for (i = 0; i < nsamps; i++) {
140 				smix = 0;
141 				for (j = 0; j < chans; j++) {
142 					smix += *cin++;
143 				}
144 				if (smix < -0x7f) {
145 					smix = -0x7f;
146 				} else if (smix > 0x7f) {
147 					smix = 0x7f;
148 				}
149 				*cout++ = (char)smix;
150 			}
151 			break;
152 		case 2:
153 			sin = (short *)inbuf->GetAddress();
154 			sout = (short *)outbuf->GetAddress();
155 
156 			for (i = 0; i < nsamps; i++) {
157 				smix = 0;
158 				for (j = 0; j < chans; j++) {
159 					smix += *sin++;
160 				}
161 				if (smix < -0x7fff) {
162 					smix = -0x7fff;
163 				} else if (smix > 0x7fff) {
164 					smix = 0x7fff;
165 				}
166 				*sout++ = (short)smix;
167 			}
168 			break;
169 		default:
170 			err = AUDIO_ERR_HDRINVAL;
171 		}
172 
173 	} else if ((chans == 1) && (newhdr.channels > 1)) {
174 		// mono -> multi-channel
175 		chans = newhdr.channels;
176 		cin = (char *)inbuf->GetAddress();
177 		cout = (char *)outbuf->GetAddress();
178 
179 		// XXX - this could be optimized by special-casing stuff
180 		for (i = 0; i < nsamps; i++) {
181 			for (j = 0; j < chans; j++) {
182 				for (k = 0; k < nbytes; k++)
183 					*cout++ = cin[k];
184 			}
185 			cin += nbytes;
186 		}
187 	}
188 
189 	if (err) {
190 		if (outbuf != inbuf)
191 			delete outbuf;
192 		return (err);
193 	}
194 
195 	// This will delete the buffer
196 	inbuf->Reference();
197 	inbuf->Dereference();
198 
199 	// Set the valid data length
200 	outbuf->SetLength(length);
201 	inbuf = outbuf;
202 	return (AUDIO_SUCCESS);
203 }
204 
205 AudioError AudioTypeChannel::
Flush(AudioBuffer * &)206 Flush(
207 	AudioBuffer*&	/* buf */)
208 {
209 	return (AUDIO_SUCCESS);
210 }
211