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) 1992-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 <AudioTypeMux.h>
34
35// This is a conversion class for channel multiplex/demultiplex
36
37// class AudioTypeMux methods
38
39// Constructor
40AudioTypeMux::
41AudioTypeMux()
42{
43}
44
45// Destructor
46AudioTypeMux::
47~AudioTypeMux()
48{
49}
50
51// Test conversion possibilities.
52// Return TRUE if conversion to/from the specified type is possible.
53Boolean AudioTypeMux::
54CanConvert(
55	AudioHdr	/* h */) const		// target header
56{
57	// XXX - The test is whether we're converting 1->many or many->1
58	//	This routine needs a to/from argument.
59	// XXX - What if the format doesn't have fixed-size sample units?
60	return (TRUE);
61}
62
63// Multiplex or demultiplex.
64// The buffer pointer should be a NULL-terminated array of buffers if 1-channel
65AudioError AudioTypeMux::
66Convert(
67	AudioBuffer*&	inbuf,			// data buffer to process
68	AudioHdr	outhdr)			// target header
69{
70	AudioBuffer*	outbuf;
71	AudioBuffer**	multibuf;
72	AudioHdr	inhdr;
73	Double		length;
74	unsigned int	channels;
75	size_t		nsamps;
76	size_t		nbytes;
77	size_t		unitsz;
78	unsigned char	**inptrs;
79	unsigned char	*in;
80	unsigned char	*out;
81	int		i;
82	int		j;
83	int		k;
84	AudioError	err;
85
86	channels = outhdr.channels;
87	if (channels == 1) {
88		inhdr = inbuf->GetHeader();	// Demux multi-channel data
89		length = inbuf->GetLength();
90	} else {
91		multibuf = (AudioBuffer**) inbuf;	// Mux multiple buffers
92		inhdr = multibuf[0]->GetHeader();
93		length = multibuf[0]->GetLength();
94	}
95
96	// Make sure we're not being asked to do the impossible or trivial
97	if ((err = inhdr.Validate()))
98		return (err);
99	if ((inhdr.sample_rate != outhdr.sample_rate) ||
100	    (inhdr.encoding != outhdr.encoding) ||
101	    (inhdr.samples_per_unit != outhdr.samples_per_unit) ||
102	    (inhdr.bytes_per_unit != outhdr.bytes_per_unit))
103		return (AUDIO_ERR_HDRINVAL);
104	if (inhdr.channels == outhdr.channels)
105		return (AUDIO_SUCCESS);
106	if ((inhdr.channels != 1) && (outhdr.channels != 1))
107		return (AUDIO_ERR_HDRINVAL);
108	if (Undefined(length))
109		return (AUDIO_ERR_BADARG);
110
111	// Get the number of sample frames and the size of each
112	nsamps = (size_t)inhdr.Time_to_Samples(length);
113	nbytes = (size_t)inhdr.FrameLength();
114	unitsz = (size_t)inhdr.bytes_per_unit;
115
116	// Figure out if we're multiplexing or demultiplexing
117	if (channels == 1) {
118		// Demultiplex multi-channel data into several mono channels
119
120		// Allocate buffer pointer array and each buffer
121		channels = inhdr.channels;
122		multibuf = (AudioBuffer**)
123		    calloc((channels + 1), sizeof (AudioBuffer*));
124		for (i = 0; i < channels; i++) {
125			multibuf[i] = new AudioBuffer(length,
126			    "(Demultiplex conversion buffer)");
127			if (multibuf[i] == 0) {
128				err = AUDIO_UNIXERROR;
129				goto cleanup;
130			}
131			if (err = multibuf[i]->SetHeader(outhdr)) {
132				delete multibuf[i];
133cleanup:			while (--i >= 0) {
134					delete multibuf[i];
135				}
136				delete multibuf;
137				return (err);
138			}
139		}
140		multibuf[i] = NULL;
141
142		for (i = 0; i < channels; i++) {
143			// Get output pointer and input channel pointer
144			out = (unsigned char *)multibuf[i]->GetAddress();
145			in = (unsigned char *)inbuf->GetAddress();
146			in += (i * unitsz);
147
148			// Copy a sample unit and bump the input pointer
149			for (j = 0; j < nsamps; j++) {
150				for (k = 0; k < unitsz; k++) {
151					*out++ = *in++;
152				}
153				in += ((channels - 1) * unitsz);
154			}
155
156			// Set the valid data length
157			multibuf[i]->SetLength(length);
158		}
159		// Release the input buffer
160		inbuf->Reference();
161		inbuf->Dereference();
162
163		// Return the array pointer (callers beware!)
164		inbuf = (AudioBuffer*) multibuf;
165
166	} else {
167		// Multiplex several mono channels into multi-channel data
168
169		// Allocate an output buffer
170		outbuf = new AudioBuffer(length,
171		    "(Multiplex conversion buffer)");
172		if (outbuf == 0)
173			return (AUDIO_UNIXERROR);
174		if (err = outbuf->SetHeader(outhdr)) {
175			delete outbuf;
176			return (err);
177		}
178
179		// Verify the input pointer is an array of buffer pointers
180		multibuf = (AudioBuffer**) inbuf;
181		for (channels = 0; ; channels++) {
182			// Look for NULL termination
183			if (multibuf[channels] == NULL)
184				break;
185			if (!multibuf[channels]->isBuffer())
186				return (AUDIO_ERR_BADARG);
187		}
188		if (channels != outhdr.channels)
189			return (AUDIO_ERR_BADARG);
190
191		// Allocate a bunch of input pointers
192		inptrs = (unsigned char **)
193		    calloc(channels, sizeof (unsigned char *));
194		for (i = 0; i < channels; i++) {
195			inptrs[i] = (unsigned char *) multibuf[i]->GetAddress();
196		}
197
198		// Get output pointer
199		out = (unsigned char *)outbuf->GetAddress();
200
201		for (i = 0; i < nsamps; i++) {
202			// Copy a sample frame from each input buffer
203			for (j = 0; j < channels; j++) {
204				in = inptrs[j];
205				for (k = 0; k < nbytes; k++) {
206					*out++ = *in++;
207				}
208				inptrs[j] = in;
209			}
210		}
211		// Set the valid data length
212		outbuf->SetLength(length);
213
214		// Release the input buffers and pointer arrays
215		for (i = 0; i < channels; i++) {
216			multibuf[i]->Reference();
217			multibuf[i]->Dereference();
218			multibuf[i] = NULL;
219		}
220		delete multibuf;
221		delete inptrs;
222
223		// Set the valid data length and return the new pointer
224		outbuf->SetLength(length);
225		inbuf = outbuf;
226	}
227	return (AUDIO_SUCCESS);
228}
229
230AudioError AudioTypeMux::
231Flush(
232	AudioBuffer*&	/* buf */)
233{
234	return (AUDIO_SUCCESS);
235}
236