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 <AudioTypeG72X.h>
34 
35 // class AudioTypeG72X methods
36 // G.721 & G.723 compress/decompress
37 
38 // Constructor
39 AudioTypeG72X::
40 AudioTypeG72X()
41 {
42 	initialized = FALSE;
43 }
44 
45 // Destructor
46 AudioTypeG72X::
47 ~AudioTypeG72X()
48 {
49 }
50 
51 // Test conversion possibilities.
52 // Return TRUE if conversion to/from the specified type is possible.
53 Boolean AudioTypeG72X::
54 CanConvert(
55 	AudioHdr	h) const		// target header
56 {
57 	// g72x conversion code handles mono 16-bit pcm, ulaw, alaw
58 	if (h.channels != 1)
59 		return (FALSE);
60 
61 	switch (h.encoding) {
62 	case LINEAR:
63 		if ((h.samples_per_unit != 1) ||
64 		    (h.bytes_per_unit != 2))
65 			return (FALSE);
66 		break;
67 	case ALAW:
68 	case ULAW:
69 		if ((h.samples_per_unit != 1) ||
70 		    (h.bytes_per_unit != 1))
71 			return (FALSE);
72 		break;
73 	case G721:
74 		if ((h.samples_per_unit != 2) ||
75 		    (h.bytes_per_unit != 1))
76 			return (FALSE);
77 		break;
78 	case G723:
79 		if (h.samples_per_unit != 8)
80 			return (FALSE);
81 
82 		// XXX - 5-bit G.722 not supported yet
83 		if (h.bytes_per_unit != 3)
84 			return (FALSE);
85 		break;
86 	case FLOAT:
87 	default:
88 		return (FALSE);
89 	}
90 	return (TRUE);
91 }
92 
93 // Convert buffer to the specified type
94 // May replace the buffer with a new one, if necessary
95 AudioError AudioTypeG72X::
96 Convert(
97 	AudioBuffer*&	inbuf,			// data buffer to process
98 	AudioHdr	outhdr)			// target header
99 {
100 	AudioBuffer*	outbuf;
101 	AudioHdr	inhdr;
102 	Audio_hdr	chdr;	// C struct for g72x convert code
103 	Double		length;
104 	Double		pad;
105 	size_t		nbytes;
106 	int		cnt;
107 	unsigned char	*inptr;
108 	unsigned char	*outptr;
109 	AudioError	err;
110 
111 	inhdr = inbuf->GetHeader();
112 	length = inbuf->GetLength();
113 
114 	if (Undefined(length)) {
115 		return (AUDIO_ERR_BADARG);
116 	}
117 
118 	// Make sure we're not being asked to do the impossible
119 	if ((err = inhdr.Validate()) || (err = outhdr.Validate())) {
120 		return (err);
121 	}
122 
123 	if (!CanConvert(inhdr) || !CanConvert(outhdr) ||
124 	    (inhdr.sample_rate != outhdr.sample_rate) ||
125 	    (inhdr.channels != outhdr.channels))
126 		return (AUDIO_ERR_HDRINVAL);
127 
128 	// if conversion is a no-op, just return success
129 	if ((inhdr.encoding == outhdr.encoding) &&
130 	    (inhdr.bytes_per_unit == outhdr.bytes_per_unit)) {
131 		return (AUDIO_SUCCESS);
132 	}
133 
134 	// Add some padding to the output buffer
135 	pad = outhdr.Samples_to_Time(
136 	    4 * outhdr.bytes_per_unit * outhdr.channels);
137 
138 	// Allocate a new buffer
139 	outbuf = new AudioBuffer(length + pad, "(G72x conversion buffer)");
140 	if (outbuf == 0)
141 		return (AUDIO_UNIXERROR);
142 	if (err = outbuf->SetHeader(outhdr)) {
143 		delete outbuf;
144 		return (err);
145 	}
146 
147 	// Convert from the input type to the output type
148 	inptr = (unsigned char *)inbuf->GetAddress();
149 	outptr = (unsigned char *)outbuf->GetAddress();
150 	nbytes = (size_t)inhdr.Time_to_Bytes(length);
151 	if (nbytes == 0)
152 		goto cleanup;
153 
154 	switch (inhdr.encoding) {
155 	case ALAW:
156 	case ULAW:
157 	case LINEAR:
158 		switch (outhdr.encoding) {
159 		case G721:
160 			chdr = (Audio_hdr)inhdr;
161 			if (!initialized) {
162 				g721_init_state(&g72x_state);
163 				initialized = TRUE;
164 			}
165 			err = g721_encode((void*)inptr, nbytes, &chdr,
166 			    outptr, &cnt, &g72x_state);
167 			length = outhdr.Bytes_to_Time(cnt);
168 			break;
169 		case G723:
170 			chdr = (Audio_hdr)inhdr;
171 			if (!initialized) {
172 				g723_init_state(&g72x_state);
173 				initialized = TRUE;
174 			}
175 			err = g723_encode((void*)inptr, nbytes, &chdr,
176 			    outptr, &cnt, &g72x_state);
177 			length = outhdr.Bytes_to_Time(cnt);
178 			break;
179 		default:
180 			err = AUDIO_ERR_HDRINVAL; break;
181 		}
182 		break;
183 	case G721:
184 		switch (outhdr.encoding) {
185 		case ALAW:
186 		case ULAW:
187 		case LINEAR:
188 			chdr = (Audio_hdr)outhdr;
189 			if (!initialized) {
190 				g721_init_state(&g72x_state);
191 				initialized = TRUE;
192 			}
193 			err = g721_decode(inptr, nbytes, &chdr,
194 			    (void*)outptr, &cnt, &g72x_state);
195 			length = outhdr.Samples_to_Time(cnt);
196 			break;
197 		default:
198 			err = AUDIO_ERR_HDRINVAL; break;
199 		}
200 		break;
201 	case G723:
202 		switch (outhdr.encoding) {
203 		case ALAW:
204 		case ULAW:
205 		case LINEAR:
206 			chdr = (Audio_hdr)outhdr;
207 			if (!initialized) {
208 				g723_init_state(&g72x_state);
209 				initialized = TRUE;
210 			}
211 			err = g723_decode(inptr, nbytes, &chdr,
212 			    (void*)outptr, &cnt, &g72x_state);
213 			length = outhdr.Samples_to_Time(cnt);
214 			break;
215 		default:
216 			err = AUDIO_ERR_HDRINVAL; break;
217 		}
218 		break;
219 	default:
220 		err = AUDIO_ERR_HDRINVAL; break;
221 	}
222 	if (err) {
223 		if (outbuf != inbuf)
224 			delete outbuf;
225 		return (err);
226 	}
227 cleanup:
228 	// This will delete the buffer
229 	inbuf->Reference();
230 	inbuf->Dereference();
231 
232 	// Set the valid data length
233 	outbuf->SetLength(length);
234 	inbuf = outbuf;
235 
236 	return (AUDIO_SUCCESS);
237 }
238 
239 // Flush out any leftover state, appending to supplied buffer
240 AudioError AudioTypeG72X::
241 Flush(
242 	AudioBuffer*&	outbuf)
243 {
244 	AudioHdr	h;
245 	Double		pos;
246 	size_t		cnt;
247 	AudioError	err;
248 	unsigned char	tmpbuf[32];
249 
250 	if (!initialized)
251 		return (AUDIO_SUCCESS);
252 	initialized = FALSE;
253 	if (outbuf == NULL)
254 		return (AUDIO_SUCCESS);
255 
256 	h = outbuf->GetHeader();
257 
258 	switch (h.encoding) {
259 	case G721:
260 	case G723:
261 		switch (h.encoding) {
262 		case G721:
263 			err = g721_encode(NULL, 0, NULL,
264 			    tmpbuf, (int *)&cnt, &g72x_state);
265 			break;
266 		case G723:
267 			err = g723_encode(NULL, 0, NULL,
268 			    tmpbuf, (int *)&cnt, &g72x_state);
269 			break;
270 		}
271 		// Copy to the supplied buffer
272 		if (cnt > 0) {
273 			pos = outbuf->GetLength();
274 			err = outbuf->AppendData(tmpbuf, cnt, pos);
275 			if (err)
276 				return (err);
277 		}
278 		break;
279 	default:
280 		break;
281 	}
282 	return (AUDIO_SUCCESS);
283 }
284