xref: /illumos-gate/usr/src/cmd/audio/utilities/AudioTypeChannel.cc (revision 7d98732934f32a5f80fe624c9689e8f54dd17ffe)
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 	err = outbuf->SetHeader(newhdr);
121 	if (err != AUDIO_SUCCESS) {
122 		delete outbuf;
123 		return (err);
124 	}
125 
126 	// Get the number of sample frames and the size of each
127 	nsamps = (size_t)inhdr.Time_to_Samples(length);
128 	nbytes = (size_t)inhdr.FrameLength();
129 	chans = inhdr.channels;
130 
131 	// multi-channel -> mono conversion
132 	if ((chans > 1) && (newhdr.channels == 1)) {
133 		switch (inhdr.bytes_per_unit) {
134 		case 1:
135 			cin = (char *)inbuf->GetAddress();
136 			cout = (char *)outbuf->GetAddress();
137 
138 			for (i = 0; i < nsamps; i++) {
139 				smix = 0;
140 				for (j = 0; j < chans; j++) {
141 					smix += *cin++;
142 				}
143 				if (smix < -0x7f) {
144 					smix = -0x7f;
145 				} else if (smix > 0x7f) {
146 					smix = 0x7f;
147 				}
148 				*cout++ = (char)smix;
149 			}
150 			break;
151 		case 2:
152 			sin = (short *)inbuf->GetAddress();
153 			sout = (short *)outbuf->GetAddress();
154 
155 			for (i = 0; i < nsamps; i++) {
156 				smix = 0;
157 				for (j = 0; j < chans; j++) {
158 					smix += *sin++;
159 				}
160 				if (smix < -0x7fff) {
161 					smix = -0x7fff;
162 				} else if (smix > 0x7fff) {
163 					smix = 0x7fff;
164 				}
165 				*sout++ = (short)smix;
166 			}
167 			break;
168 		default:
169 			err = AUDIO_ERR_HDRINVAL;
170 		}
171 
172 	} else if ((chans == 1) && (newhdr.channels > 1)) {
173 		// mono -> multi-channel
174 		chans = newhdr.channels;
175 		cin = (char *)inbuf->GetAddress();
176 		cout = (char *)outbuf->GetAddress();
177 
178 		// XXX - this could be optimized by special-casing stuff
179 		for (i = 0; i < nsamps; i++) {
180 			for (j = 0; j < chans; j++) {
181 				for (k = 0; k < nbytes; k++)
182 					*cout++ = cin[k];
183 			}
184 			cin += nbytes;
185 		}
186 	}
187 
188 	if (err) {
189 		if (outbuf != inbuf)
190 			delete outbuf;
191 		return (err);
192 	}
193 
194 	// This will delete the buffer
195 	inbuf->Reference();
196 	inbuf->Dereference();
197 
198 	// Set the valid data length
199 	outbuf->SetLength(length);
200 	inbuf = outbuf;
201 	return (AUDIO_SUCCESS);
202 }
203 
204 AudioError AudioTypeChannel::
Flush(AudioBuffer * &)205 Flush(
206 	AudioBuffer*&	/* buf */)
207 {
208 	return (AUDIO_SUCCESS);
209 }
210