FreeBSD/Linux Kernel Cross Reference
sys/dev/aurateconv.c
1 /* $NetBSD: aurateconv.c,v 1.9 2003/12/31 13:51:28 bjh21 Exp $ */
2
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by TAMURA Kent
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: aurateconv.c,v 1.9 2003/12/31 13:51:28 bjh21 Exp $");
41
42 #include <sys/systm.h>
43 #include <sys/types.h>
44 #include <sys/device.h>
45 #include <sys/errno.h>
46 #include <sys/select.h>
47 #include <sys/audioio.h>
48
49 #include <dev/audio_if.h>
50 #include <dev/audiovar.h>
51
52 #ifdef AURATECONV_DEBUG
53 #define DPRINTF(x) printf x
54 #else
55 #define DPRINTF(x)
56 #endif
57
58 static int auconv_play_slinear16_LE(struct auconv_context *,
59 const struct audio_params *, uint8_t *, const uint8_t *, int);
60 static int auconv_play_slinear24_LE(struct auconv_context *,
61 const struct audio_params *, uint8_t *, const uint8_t *, int);
62 static int auconv_play_slinear16_BE(struct auconv_context *,
63 const struct audio_params *, uint8_t *, const uint8_t *, int);
64 static int auconv_play_slinear24_BE(struct auconv_context *,
65 const struct audio_params *, uint8_t *, const uint8_t *, int);
66
67 static int auconv_record_slinear16_LE(struct auconv_context *,
68 const struct audio_params *, uint8_t *, const uint8_t *, int);
69 static int auconv_record_slinear24_LE(struct auconv_context *,
70 const struct audio_params *, uint8_t *, const uint8_t *, int);
71 static int auconv_record_slinear16_BE(struct auconv_context *,
72 const struct audio_params *, uint8_t *, const uint8_t *, int);
73 static int auconv_record_slinear24_BE(struct auconv_context *,
74 const struct audio_params *, uint8_t *, const uint8_t *, int);
75
76 int
77 auconv_check_params(const struct audio_params *params)
78 {
79 DPRINTF(("auconv_check_params: rate=%ld:%ld chan=%d:%d prec=%d:%d "
80 "enc=%d:%d\n", params->sample_rate, params->hw_sample_rate,
81 params->channels, params->hw_channels, params->precision,
82 params->hw_precision, params->encoding, params->hw_encoding));
83 if (params->hw_channels == params->channels
84 && params->hw_sample_rate == params->sample_rate)
85 return 0; /* No conversion */
86
87 if ((params->hw_encoding != AUDIO_ENCODING_SLINEAR_LE
88 && params->hw_encoding != AUDIO_ENCODING_SLINEAR_BE)
89 || (params->hw_precision != 16 && params->hw_precision != 24))
90 return (EINVAL);
91
92 if (params->hw_channels != params->channels) {
93 if (params->hw_channels == 1 && params->channels == 2) {
94 /* Ok */
95 } else if (params->hw_channels == 2 && params->channels == 1) {
96 /* Ok */
97 } else if (params->hw_channels > params->channels) {
98 /* Ok */
99 } else
100 return (EINVAL);
101 }
102 if (params->hw_channels > AUDIO_MAX_CHANNELS
103 || params->channels > AUDIO_MAX_CHANNELS)
104 return (EINVAL);
105
106 if (params->hw_sample_rate != params->sample_rate)
107 if (params->hw_sample_rate <= 0 || params->sample_rate <= 0)
108 return (EINVAL);
109 return 0;
110 }
111
112 void
113 auconv_init_context(struct auconv_context *context, long src_rate,
114 long dst_rate, uint8_t *start, uint8_t *end)
115 {
116 int i;
117
118 context->ring_start = start;
119 context->ring_end = end;
120 if (dst_rate > src_rate) {
121 context->count = src_rate;
122 } else {
123 context->count = 0;
124 }
125 for (i = 0; i < AUDIO_MAX_CHANNELS; i++)
126 context->prev[i] = 0;
127 }
128
129 /*
130 * src is a ring buffer.
131 */
132 int
133 auconv_record(struct auconv_context *context,
134 const struct audio_params *params, uint8_t *dest,
135 const uint8_t *src, int srcsize)
136 {
137 if (params->hw_sample_rate == params->sample_rate
138 && params->hw_channels == params->channels) {
139 int n;
140
141 n = context->ring_end - src;
142 if (srcsize <= n)
143 memcpy(dest, src, srcsize);
144 else {
145 memcpy(dest, src, n);
146 memcpy(dest + n, context->ring_start, srcsize - n);
147 }
148 return srcsize;
149 }
150
151 switch (params->hw_encoding) {
152 case AUDIO_ENCODING_SLINEAR_LE:
153 switch (params->hw_precision) {
154 case 16:
155 return auconv_record_slinear16_LE(context, params,
156 dest, src, srcsize);
157 case 24:
158 return auconv_record_slinear24_LE(context, params,
159 dest, src, srcsize);
160 }
161 break;
162 case AUDIO_ENCODING_SLINEAR_BE:
163 switch (params->hw_precision) {
164 case 16:
165 return auconv_record_slinear16_BE(context, params,
166 dest, src, srcsize);
167 case 24:
168 return auconv_record_slinear24_BE(context, params,
169 dest, src, srcsize);
170 }
171 break;
172 default:
173 /* This should be rejected in auconv_check_params() */
174 printf("auconv_record: unimplemented encoding: %d\n",
175 params->hw_encoding);
176 return 0;
177 }
178 printf("auconv_record: unimplemented precision: %d\n",
179 params->hw_precision);
180 return 0;
181 }
182
183 /*
184 * dest is a ring buffer.
185 */
186 int
187 auconv_play(struct auconv_context *context, const struct audio_params *params,
188 uint8_t *dest, const uint8_t *src, int srcsize)
189 {
190 int n;
191
192 if (params->hw_sample_rate == params->sample_rate
193 && params->hw_channels == params->channels) {
194 n = context->ring_end - dest;
195 if (srcsize <= n) {
196 memcpy(dest, src, srcsize);
197 } else {
198 memcpy(dest, src, n);
199 memcpy(context->ring_start, src + n, srcsize - n);
200 }
201 return srcsize;
202 }
203
204 switch (params->hw_encoding) {
205 case AUDIO_ENCODING_SLINEAR_LE:
206 switch (params->hw_precision) {
207 case 16:
208 return auconv_play_slinear16_LE(context, params,
209 dest, src, srcsize);
210 case 24:
211 return auconv_play_slinear24_LE(context, params,
212 dest, src, srcsize);
213 }
214 break;
215 case AUDIO_ENCODING_SLINEAR_BE:
216 switch (params->hw_precision) {
217 case 16:
218 return auconv_play_slinear16_BE(context, params,
219 dest, src, srcsize);
220 case 24:
221 return auconv_play_slinear24_BE(context, params,
222 dest, src, srcsize);
223 }
224 break;
225 default:
226 /* This should be rejected in auconv_check_params() */
227 printf("auconv_play: unimplemented encoding: %d\n",
228 params->hw_encoding);
229 return 0;
230 }
231 printf("auconv_play: unimplemented precision: %d\n",
232 params->hw_precision);
233 return 0;
234 }
235
236
237 #define RING_CHECK(C, V) \
238 do { \
239 if (V >= (C)->ring_end) \
240 V = (C)->ring_start; \
241 } while (/*CONSTCOND*/ 0)
242
243 #define READ_S8LE(P) *(int8_t*)(P)
244 #define WRITE_S8LE(P, V) *(int8_t*)(P) = V
245 #define READ_S8BE(P) *(int8_t*)(P)
246 #define WRITE_S8BE(P, V) *(int8_t*)(P) = V
247 #if BYTE_ORDER == LITTLE_ENDIAN
248 # define READ_S16LE(P) *(int16_t*)(P)
249 # define WRITE_S16LE(P, V) *(int16_t*)(P) = V
250 # define READ_S16BE(P) (int16_t)((P)[0] | ((P)[1]<<8))
251 # define WRITE_S16BE(P, V) \
252 do { \
253 int vv = V; \
254 (P)[0] = vv; \
255 (P)[1] = vv >> 8; \
256 } while (/*CONSTCOND*/ 0)
257 #else
258 # define READ_S16LE(P) (int16_t)((P)[0] | ((P)[1]<<8))
259 # define WRITE_S16LE(P, V) \
260 do { \
261 int vv = V; \
262 (P)[0] = vv; \
263 (P)[1] = vv >> 8; \
264 } while (/*CONSTCOND*/ 0)
265 # define READ_S16BE(P) *(int16_t*)(P)
266 # define WRITE_S16BE(P, V) *(int16_t*)(P) = V
267 #endif
268 #define READ_S24LE(P) (int32_t)((P)[0] | ((P)[1]<<8) | (((int8_t)((P)[2]))<<16))
269 #define WRITE_S24LE(P, V) \
270 do { \
271 int vvv = V; \
272 (P)[0] = vvv; \
273 (P)[1] = vvv >> 8; \
274 (P)[2] = vvv >> 16; \
275 } while (/*CONSTCOND*/ 0)
276 #define READ_S24BE(P) (int32_t)((P)[2] | ((P)[1]<<8) | (((int8_t)((P)[0]))<<16))
277 #define WRITE_S24BE(P, V) \
278 do { \
279 int vvv = V; \
280 (P)[0] = vvv >> 16; \
281 (P)[1] = vvv >> 8; \
282 (P)[2] = vvv; \
283 } while (/*CONSTCOND*/ 0)
284
285 #define P_READ_Sn(BITS, EN, V, RP, PAR) \
286 do { \
287 int j; \
288 for (j = 0; j < (PAR)->channels; j++) { \
289 (V)[j] = READ_S##BITS##EN(RP); \
290 RP += (BITS) / NBBY; \
291 } \
292 } while (/*CONSTCOND*/ 0)
293 #define P_WRITE_Sn(BITS, EN, V, WP, PAR, CON, WC) \
294 do { \
295 if ((PAR)->channels == 2 && (PAR)->hw_channels == 1) { \
296 WRITE_S##BITS##EN(WP, ((V)[0] + (V)[1]) / 2); \
297 WP += (BITS) / NBBY; \
298 RING_CHECK(CON, WP); \
299 WC += (BITS) / NBBY; \
300 } else { /* channels <= hw_channels */ \
301 int j; \
302 for (j = 0; j < (PAR)->channels; j++) { \
303 WRITE_S##BITS##EN(WP, (V)[j]); \
304 WP += (BITS) / NBBY; \
305 RING_CHECK(CON, WP); \
306 } \
307 if (j == 1 && 1 < (PAR)->hw_channels) { \
308 WRITE_S##BITS##EN(WP, (V)[0]); \
309 WP += (BITS) / NBBY; \
310 RING_CHECK(CON, WP); \
311 j++; \
312 } \
313 for (; j < (PAR)->hw_channels; j++) { \
314 WRITE_S##BITS##EN(WP, 0); \
315 WP += (BITS) / NBBY; \
316 RING_CHECK(CON, WP); \
317 } \
318 WC += (BITS) / NBBY * j; \
319 } \
320 } while (/*CONSTCOND*/ 0)
321
322 #define R_READ_Sn(BITS, EN, V, RP, PAR, CON, RC) \
323 do { \
324 int j; \
325 for (j = 0; j < (PAR)->hw_channels; j++) { \
326 (V)[j] = READ_S##BITS##EN(RP); \
327 RP += (BITS) / NBBY; \
328 RING_CHECK(CON, RP); \
329 RC += (BITS) / NBBY; \
330 } \
331 } while (/*CONSTCOND*/ 0)
332 #define R_WRITE_Sn(BITS, EN, V, WP, PAR, WC) \
333 do { \
334 if ((PAR)->channels == 2 && (PAR)->hw_channels == 1) { \
335 WRITE_S##BITS##EN(WP, (V)[0]); \
336 WP += (BITS) / NBBY; \
337 WRITE_S##BITS##EN(WP, (V)[0]); \
338 WP += (BITS) / NBBY; \
339 WC += (BITS) / NBBY * 2; \
340 } else if ((PAR)->channels == 1 && (PAR)->hw_channels >= 2) { \
341 WRITE_S##BITS##EN(WP, ((V)[0] + (V)[1]) / 2); \
342 WP += (BITS) / NBBY; \
343 WC += (BITS) / NBBY; \
344 } else { /* channels <= hw_channels */ \
345 int j; \
346 for (j = 0; j < (PAR)->channels; j++) { \
347 WRITE_S##BITS##EN(WP, (V)[j]); \
348 WP += (BITS) / NBBY; \
349 } \
350 WC += (BITS) / NBBY * j; \
351 } \
352 } while (/*CONSTCOND*/ 0)
353
354 /*
355 * Function templates
356 *
357 * Source may be 1 sample. Destination buffer must have space for converted
358 * source.
359 * Don't use them for 32bit data because this linear interpolation overflows
360 * for 32bit data.
361 */
362 #define AUCONV_PLAY_SLINEAR(BITS, EN) \
363 static int \
364 auconv_play_slinear##BITS##_##EN (struct auconv_context *context, \
365 const struct audio_params *params, \
366 uint8_t *dest, const uint8_t *src, \
367 int srcsize) \
368 { \
369 int wrote; \
370 uint8_t *w; \
371 const uint8_t *r; \
372 const uint8_t *src_end; \
373 int32_t v[AUDIO_MAX_CHANNELS]; \
374 int32_t prev[AUDIO_MAX_CHANNELS], next[AUDIO_MAX_CHANNELS], c256; \
375 int i, values_size; \
376 \
377 wrote = 0; \
378 w = dest; \
379 r = src; \
380 src_end = src + srcsize; \
381 if (params->sample_rate == params->hw_sample_rate) { \
382 while (r < src_end) { \
383 P_READ_Sn(BITS, EN, v, r, params); \
384 P_WRITE_Sn(BITS, EN, v, w, params, context, wrote); \
385 } \
386 } else if (params->hw_sample_rate < params->sample_rate) { \
387 for (;;) { \
388 do { \
389 if (r >= src_end) \
390 return wrote; \
391 P_READ_Sn(BITS, EN, v, r, params); \
392 context->count += params->hw_sample_rate; \
393 } while (context->count < params->sample_rate); \
394 context->count -= params->sample_rate; \
395 P_WRITE_Sn(BITS, EN, v, w, params, context, wrote); \
396 } \
397 } else { \
398 /* Initial value of context->count is params->sample_rate */ \
399 values_size = sizeof(int32_t) * params->channels; \
400 memcpy(prev, context->prev, values_size); \
401 P_READ_Sn(BITS, EN, next, r, params); \
402 for (;;) { \
403 c256 = context->count * 256 / params->hw_sample_rate; \
404 for (i = 0; i < params->channels; i++) \
405 v[i] = (c256 * next[i] + (256 - c256) * prev[i]) >> 8; \
406 P_WRITE_Sn(BITS, EN, v, w, params, context, wrote); \
407 context->count += params->sample_rate; \
408 if (context->count >= params->hw_sample_rate) { \
409 context->count -= params->hw_sample_rate; \
410 memcpy(prev, next, values_size); \
411 if (r >= src_end) \
412 break; \
413 P_READ_Sn(BITS, EN, next, r, params); \
414 } \
415 } \
416 memcpy(context->prev, next, values_size); \
417 } \
418 return wrote; \
419 }
420
421 #define AUCONV_RECORD_SLINEAR(BITS, EN) \
422 static int \
423 auconv_record_slinear##BITS##_##EN (struct auconv_context *context, \
424 const struct audio_params *params, \
425 uint8_t *dest, const uint8_t *src, \
426 int srcsize) \
427 { \
428 int wrote, rsize; \
429 uint8_t *w; \
430 const uint8_t *r; \
431 int32_t v[AUDIO_MAX_CHANNELS]; \
432 int32_t prev[AUDIO_MAX_CHANNELS], next[AUDIO_MAX_CHANNELS], c256; \
433 int i, values_size; \
434 \
435 wrote = 0; \
436 rsize = 0; \
437 w = dest; \
438 r = src; \
439 if (params->sample_rate == params->hw_sample_rate) { \
440 while (rsize < srcsize) { \
441 R_READ_Sn(BITS, EN, v, r, params, context, rsize); \
442 R_WRITE_Sn(BITS, EN, v, w, params, wrote); \
443 } \
444 } else if (params->sample_rate < params->hw_sample_rate) { \
445 for (;;) { \
446 do { \
447 if (rsize >= srcsize) \
448 return wrote; \
449 R_READ_Sn(BITS, EN, v, r, params, context, rsize); \
450 context->count += params->sample_rate; \
451 } while (context->count < params->hw_sample_rate); \
452 context->count -= params->hw_sample_rate; \
453 R_WRITE_Sn(BITS, EN, v, w, params, wrote); \
454 } \
455 } else { \
456 /* Initial value of context->count is params->hw_sample_rate */ \
457 values_size = sizeof(int32_t) * params->hw_channels; \
458 memcpy(prev, context->prev, values_size); \
459 R_READ_Sn(BITS, EN, next, r, params, context, rsize); \
460 for (;;) { \
461 c256 = context->count * 256 / params->sample_rate; \
462 for (i = 0; i < params->hw_channels; i++) \
463 v[i] = (c256 * next[i] + (256 - c256) * prev[i]) >> 8; \
464 R_WRITE_Sn(BITS, EN, v, w, params, wrote); \
465 context->count += params->hw_sample_rate; \
466 if (context->count >= params->sample_rate) { \
467 context->count -= params->sample_rate; \
468 memcpy(prev, next, values_size); \
469 if (rsize >= srcsize) \
470 break; \
471 R_READ_Sn(BITS, EN, next, r, params, context, rsize); \
472 } \
473 } \
474 memcpy(context->prev, next, values_size); \
475 } \
476 return wrote; \
477 }
478
479 AUCONV_PLAY_SLINEAR(16, LE)
480 AUCONV_PLAY_SLINEAR(24, LE)
481 AUCONV_PLAY_SLINEAR(16, BE)
482 AUCONV_PLAY_SLINEAR(24, BE)
483 AUCONV_RECORD_SLINEAR(16, LE)
484 AUCONV_RECORD_SLINEAR(24, LE)
485 AUCONV_RECORD_SLINEAR(16, BE)
486 AUCONV_RECORD_SLINEAR(24, BE)
Cache object: 65ef015815dbe9537e53d34b33d81369
|