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