]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/pcm/feeder.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / sys / dev / sound / pcm / feeder.c
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <dev/sound/pcm/sound.h>
30
31 static int chn_addfeeder(pcm_channel *c, pcm_feeder *f);
32 static int chn_removefeeder(pcm_channel *c);
33
34 #define FEEDBUFSZ       8192
35
36 static unsigned char ulaw_to_u8[] = {
37      3,    7,   11,   15,   19,   23,   27,   31,
38     35,   39,   43,   47,   51,   55,   59,   63,
39     66,   68,   70,   72,   74,   76,   78,   80,
40     82,   84,   86,   88,   90,   92,   94,   96,
41     98,   99,  100,  101,  102,  103,  104,  105,
42    106,  107,  108,  109,  110,  111,  112,  113,
43    113,  114,  114,  115,  115,  116,  116,  117,
44    117,  118,  118,  119,  119,  120,  120,  121,
45    121,  121,  122,  122,  122,  122,  123,  123,
46    123,  123,  124,  124,  124,  124,  125,  125,
47    125,  125,  125,  125,  126,  126,  126,  126,
48    126,  126,  126,  126,  127,  127,  127,  127,
49    127,  127,  127,  127,  127,  127,  127,  127,
50    128,  128,  128,  128,  128,  128,  128,  128,
51    128,  128,  128,  128,  128,  128,  128,  128,
52    128,  128,  128,  128,  128,  128,  128,  128,
53    253,  249,  245,  241,  237,  233,  229,  225,
54    221,  217,  213,  209,  205,  201,  197,  193,
55    190,  188,  186,  184,  182,  180,  178,  176,
56    174,  172,  170,  168,  166,  164,  162,  160,
57    158,  157,  156,  155,  154,  153,  152,  151,
58    150,  149,  148,  147,  146,  145,  144,  143,
59    143,  142,  142,  141,  141,  140,  140,  139,
60    139,  138,  138,  137,  137,  136,  136,  135,
61    135,  135,  134,  134,  134,  134,  133,  133,
62    133,  133,  132,  132,  132,  132,  131,  131,
63    131,  131,  131,  131,  130,  130,  130,  130,
64    130,  130,  130,  130,  129,  129,  129,  129,
65    129,  129,  129,  129,  129,  129,  129,  129,
66    128,  128,  128,  128,  128,  128,  128,  128,
67    128,  128,  128,  128,  128,  128,  128,  128,
68    128,  128,  128,  128,  128,  128,  128,  128,
69 };
70
71 static unsigned char u8_to_ulaw[] = {
72      0,    0,    0,    0,    0,    1,    1,    1,
73      1,    2,    2,    2,    2,    3,    3,    3,
74      3,    4,    4,    4,    4,    5,    5,    5,
75      5,    6,    6,    6,    6,    7,    7,    7,
76      7,    8,    8,    8,    8,    9,    9,    9,
77      9,   10,   10,   10,   10,   11,   11,   11,
78     11,   12,   12,   12,   12,   13,   13,   13,
79     13,   14,   14,   14,   14,   15,   15,   15,
80     15,   16,   16,   17,   17,   18,   18,   19,
81     19,   20,   20,   21,   21,   22,   22,   23,
82     23,   24,   24,   25,   25,   26,   26,   27,
83     27,   28,   28,   29,   29,   30,   30,   31,
84     31,   32,   33,   34,   35,   36,   37,   38,
85     39,   40,   41,   42,   43,   44,   45,   46,
86     47,   49,   51,   53,   55,   57,   59,   61,
87     63,   66,   70,   74,   78,   84,   92,  104,
88    254,  231,  219,  211,  205,  201,  197,  193,
89    190,  188,  186,  184,  182,  180,  178,  176,
90    175,  174,  173,  172,  171,  170,  169,  168,
91    167,  166,  165,  164,  163,  162,  161,  160,
92    159,  159,  158,  158,  157,  157,  156,  156,
93    155,  155,  154,  154,  153,  153,  152,  152,
94    151,  151,  150,  150,  149,  149,  148,  148,
95    147,  147,  146,  146,  145,  145,  144,  144,
96    143,  143,  143,  143,  142,  142,  142,  142,
97    141,  141,  141,  141,  140,  140,  140,  140,
98    139,  139,  139,  139,  138,  138,  138,  138,
99    137,  137,  137,  137,  136,  136,  136,  136,
100    135,  135,  135,  135,  134,  134,  134,  134,
101    133,  133,  133,  133,  132,  132,  132,  132,
102    131,  131,  131,  131,  130,  130,  130,  130,
103    129,  129,  129,  129,  128,  128,  128,  128,
104 };
105
106 /*****************************************************************************/
107
108 static int
109 feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
110 {
111         int ret, c = 0, s;
112         KASSERT(count, ("feed_root: count == 0"));
113         count &= ~((1 << ch->align) - 1);
114         KASSERT(count, ("feed_root: aligned count == 0"));
115         s = spltty();
116         if (ch->smegcnt > 0) {
117                 c = min(ch->smegcnt, count);
118                 bcopy(ch->smegbuf, buffer, c);
119                 ch->smegcnt -= c;
120         }
121         count = min(count, stream->uio_resid);
122         if (count) {
123                 ret = uiomove(buffer, count, stream);
124                 KASSERT(ret == 0, ("feed_root: uiomove failed"));
125         }
126         splx(s);
127         return c + count;
128 }
129 pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root };
130
131 /*****************************************************************************/
132
133 static int
134 feed_8to16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
135 {
136         int i, j, k;
137         k = f->source->feed(f->source, c, b, count / 2, stream);
138         j = k - 1;
139         i = j * 2 + 1;
140         while (i > 0 && j >= 0) {
141                 b[i--] = b[j--];
142                 b[i--] = 0;
143         }
144         return k * 2;
145 }
146 static pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 };
147
148 /*****************************************************************************/
149
150 static int
151 feed_16to8_init(pcm_feeder *f)
152 {
153         f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
154         return (f->data == NULL);
155 }
156
157 static int
158 feed_16to8_free(pcm_feeder *f)
159 {
160         if (f->data) free(f->data, M_DEVBUF);
161         f->data = NULL;
162         return 0;
163 }
164
165 static int
166 feed_16to8le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
167 {
168         u_int32_t i = 0, toget = count * 2;
169         int j = 1, k;
170         k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
171         while (j < k) {
172                 b[i++] = ((u_int8_t *)f->data)[j];
173                 j += 2;
174         }
175         return i;
176 }
177 static pcm_feeder feeder_16to8le =
178         { "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le };
179
180 /*****************************************************************************/
181
182 static int
183 feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
184 {
185         int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
186         j = k - 1;
187         i = j * 2 + 1;
188         while (i > 0 && j >= 0) {
189                 b[i--] = b[j];
190                 b[i--] = b[j];
191                 j--;
192         }
193         return k * 2;
194 }
195 static pcm_feeder feeder_monotostereo8 =
196         { "monotostereo8", 0, NULL, NULL, feed_monotostereo8 };
197
198 /*****************************************************************************/
199
200 static int
201 feed_stereotomono8_init(pcm_feeder *f)
202 {
203         f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
204         return (f->data == NULL);
205 }
206
207 static int
208 feed_stereotomono8_free(pcm_feeder *f)
209 {
210         if (f->data) free(f->data, M_DEVBUF);
211         f->data = NULL;
212         return 0;
213 }
214
215 static int
216 feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
217 {
218         u_int32_t i = 0, toget = count * 2;
219         int j = 0, k;
220         k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
221         while (j < k) {
222                 b[i++] = ((u_int8_t *)f->data)[j];
223                 j += 2;
224         }
225         return i;
226 }
227 static pcm_feeder feeder_stereotomono8 =
228         { "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free,
229         feed_stereotomono8 };
230
231 /*****************************************************************************/
232
233 static int
234 feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
235 {
236         u_int8_t t;
237         int i = 0, j = f->source->feed(f->source, c, b, count, stream);
238         while (i < j) {
239                 t = b[i];
240                 b[i] = b[i + 1];
241                 b[i + 1] = t;
242                 i += 2;
243         }
244         return i;
245 }
246 static pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian };
247
248 /*****************************************************************************/
249
250 static int
251 feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
252 {
253         int i = 0, j = f->source->feed(f->source, c, b, count, stream);
254         int ssz = (int)f->data, ofs = ssz - 1;
255         while (i < j) {
256                 b[i + ofs] ^= 0x80;
257                 i += ssz;
258         }
259         return i;
260 }
261 static pcm_feeder feeder_sign8 =
262         { "sign8", 0, NULL, NULL, feed_sign, (void *)1 };
263 static pcm_feeder feeder_sign16 =
264         { "sign16", -1, NULL, NULL, feed_sign, (void *)2 };
265
266 /*****************************************************************************/
267
268 static int
269 feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
270 {
271         int i = 0, j = f->source->feed(f->source, c, b, count, stream);
272         while (i < j) {
273                 b[i] = ((u_int8_t *)f->data)[b[i]];
274                 i++;
275         }
276         return i;
277 }
278 static pcm_feeder feeder_ulawtou8 =
279         { "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 };
280 static pcm_feeder feeder_u8toulaw =
281         { "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw };
282
283 /*****************************************************************************/
284
285 struct fmtspec {
286         int stereo;
287         int sign;
288         int bit16;
289         int bigendian;
290         int ulaw;
291         int bad;
292 };
293
294 struct fmtcvt {
295         pcm_feeder *f;
296         struct fmtspec ispec, ospec;
297 };
298
299 struct fmtcvt cvttab[] = {
300         {&feeder_ulawtou8,      {-1,  0, 0, 0,  1},     {-1,  0, 0, 0,  0}},
301         {&feeder_u8toulaw,      {-1,  0, 0, 0,  0},     {-1,  0, 0, 0,  1}},
302         {&feeder_sign8,         {-1,  0, 0, 0,  0},     {-1,  1, 0, 0,  0}},
303         {&feeder_sign8,         {-1,  1, 0, 0,  0},     {-1,  0, 0, 0,  0}},
304         {&feeder_monotostereo8, { 0, -1, 0, 0, -1},     { 1, -1, 0, 0, -1}},
305         {&feeder_stereotomono8, { 1, -1, 0, 0, -1},     { 0, -1, 0, 0, -1}},
306         {&feeder_sign16,        {-1,  0, 1, 0,  0},     {-1,  1, 1, 0,  0}},
307         {&feeder_sign16,        {-1,  1, 1, 0,  0},     {-1,  0, 1, 0,  0}},
308         {&feeder_8to16,         {-1, -1, 0, 0,  0},     {-1, -1, 1, 0,  0}},
309         {&feeder_16to8le,       {-1, -1, 1, 0,  0},     {-1, -1, 0, 0,  0}},
310         {&feeder_endian,        {-1, -1, 1, 0,  0},     {-1, -1, 1, 1,  0}},
311         {&feeder_endian,        {-1, -1, 1, 1,  0},     {-1, -1, 1, 0,  0}},
312 };
313 #define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
314
315 static int
316 getspec(u_int32_t fmt, struct fmtspec *spec)
317 {
318         spec->stereo = (fmt & AFMT_STEREO)? 1 : 0;
319         spec->sign = (fmt & AFMT_SIGNED)? 1 : 0;
320         spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0;
321         spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0;
322         spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0;
323         spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0;
324         return 0;
325 }
326
327 static int
328 cmp(int x, int y)
329 {
330         return (x == -1 || x == y || y == -1)? 1 : 0;
331 }
332
333 static int
334 cmpspec(struct fmtspec *x, struct fmtspec *y)
335 {
336         int i = 0;
337         if (cmp(x->stereo, y->stereo)) i |= 0x01;
338         if (cmp(x->sign, y->sign)) i |= 0x02;
339         if (cmp(x->bit16, y->bit16)) i |= 0x04;
340         if (cmp(x->bigendian, y->bigendian)) i |= 0x08;
341         if (cmp(x->ulaw, y->ulaw)) i |= 0x10;
342         return i;
343 }
344
345 static int
346 cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
347 {
348         int i = cmpspec(s, &cvt->ospec);
349         chn_addfeeder(c, cvt->f);
350         if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo;
351         if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign;
352         if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16;
353         if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian;
354         if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw;
355         return i;
356 }
357
358 int
359 chn_feedchain(pcm_channel *c)
360 {
361         int i, chosen, iter;
362         u_int32_t mask;
363         struct fmtspec s, t;
364         struct fmtcvt *e;
365
366         while (chn_removefeeder(c) != -1);
367         c->align = 0;
368         if ((c->format & chn_getcaps(c)->formats) == c->format)
369                 return c->format;
370         getspec(c->format, &s);
371         if (s.bad) return -1;
372         getspec(chn_getcaps(c)->bestfmt, &t);
373         mask = (~cmpspec(&s, &t)) & 0x1f;
374         iter = 0;
375         do {
376                 if (mask == 0 || iter >= 8) break;
377                 chosen = -1;
378                 for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
379                         e = &cvttab[i];
380                         if ((cmpspec(&s, &e->ispec) == 0x1f) &&
381                            ((~cmpspec(&e->ispec, &e->ospec)) & mask))
382                            chosen = i;
383                 }
384                 if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
385                 iter++;
386         } while (chosen != -1);
387         return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
388 }
389
390 static int
391 chn_addfeeder(pcm_channel *c, pcm_feeder *f)
392 {
393         pcm_feeder *n;
394         n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
395         *n = *f;
396         n->source = c->feeder;
397         c->feeder = n;
398         if (n->init) n->init(n);
399         if (n->align > 0) c->align += n->align;
400         else if (n->align < 0 && c->align < -n->align) c->align -= n->align;
401         return 0;
402 }
403
404 static int
405 chn_removefeeder(pcm_channel *c)
406 {
407         pcm_feeder *f;
408         if (c->feeder == &feeder_root) return -1;
409         f = c->feeder->source;
410         if (c->feeder->free) c->feeder->free(c->feeder);
411         free(c->feeder, M_DEVBUF);
412         c->feeder = f;
413         return 0;
414 }
415