diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/core/oss/pcm_plugin.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/core/oss/pcm_plugin.c')
-rw-r--r-- | sound/core/oss/pcm_plugin.c | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c new file mode 100644 index 00000000000..6bb31009f0b --- /dev/null +++ b/sound/core/oss/pcm_plugin.c | |||
@@ -0,0 +1,921 @@ | |||
1 | /* | ||
2 | * PCM Plug-In shared (kernel/library) code | ||
3 | * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> | ||
4 | * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | ||
5 | * | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU Library General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU Library General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Library General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #if 0 | ||
24 | #define PLUGIN_DEBUG | ||
25 | #endif | ||
26 | |||
27 | #include <sound/driver.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/vmalloc.h> | ||
31 | #include <sound/core.h> | ||
32 | #include <sound/pcm.h> | ||
33 | #include <sound/pcm_params.h> | ||
34 | #include "pcm_plugin.h" | ||
35 | |||
36 | #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) | ||
37 | #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) | ||
38 | |||
39 | static int snd_pcm_plugin_src_channels_mask(snd_pcm_plugin_t *plugin, | ||
40 | bitset_t *dst_vmask, | ||
41 | bitset_t **src_vmask) | ||
42 | { | ||
43 | bitset_t *vmask = plugin->src_vmask; | ||
44 | bitset_copy(vmask, dst_vmask, plugin->src_format.channels); | ||
45 | *src_vmask = vmask; | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, | ||
50 | bitset_t *src_vmask, | ||
51 | bitset_t **dst_vmask) | ||
52 | { | ||
53 | bitset_t *vmask = plugin->dst_vmask; | ||
54 | bitset_copy(vmask, src_vmask, plugin->dst_format.channels); | ||
55 | *dst_vmask = vmask; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * because some cards might have rates "very close", we ignore | ||
61 | * all "resampling" requests within +-5% | ||
62 | */ | ||
63 | static int rate_match(unsigned int src_rate, unsigned int dst_rate) | ||
64 | { | ||
65 | unsigned int low = (src_rate * 95) / 100; | ||
66 | unsigned int high = (src_rate * 105) / 100; | ||
67 | return dst_rate >= low && dst_rate <= high; | ||
68 | } | ||
69 | |||
70 | static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames) | ||
71 | { | ||
72 | snd_pcm_plugin_format_t *format; | ||
73 | ssize_t width; | ||
74 | size_t size; | ||
75 | unsigned int channel; | ||
76 | snd_pcm_plugin_channel_t *c; | ||
77 | |||
78 | if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
79 | format = &plugin->src_format; | ||
80 | } else { | ||
81 | format = &plugin->dst_format; | ||
82 | } | ||
83 | if ((width = snd_pcm_format_physical_width(format->format)) < 0) | ||
84 | return width; | ||
85 | size = frames * format->channels * width; | ||
86 | snd_assert((size % 8) == 0, return -ENXIO); | ||
87 | size /= 8; | ||
88 | if (plugin->buf_frames < frames) { | ||
89 | vfree(plugin->buf); | ||
90 | plugin->buf = vmalloc(size); | ||
91 | plugin->buf_frames = frames; | ||
92 | } | ||
93 | if (!plugin->buf) { | ||
94 | plugin->buf_frames = 0; | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | c = plugin->buf_channels; | ||
98 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | ||
99 | for (channel = 0; channel < format->channels; channel++, c++) { | ||
100 | c->frames = frames; | ||
101 | c->enabled = 1; | ||
102 | c->wanted = 0; | ||
103 | c->area.addr = plugin->buf; | ||
104 | c->area.first = channel * width; | ||
105 | c->area.step = format->channels * width; | ||
106 | } | ||
107 | } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { | ||
108 | snd_assert((size % format->channels) == 0,); | ||
109 | size /= format->channels; | ||
110 | for (channel = 0; channel < format->channels; channel++, c++) { | ||
111 | c->frames = frames; | ||
112 | c->enabled = 1; | ||
113 | c->wanted = 0; | ||
114 | c->area.addr = plugin->buf + (channel * size); | ||
115 | c->area.first = 0; | ||
116 | c->area.step = width; | ||
117 | } | ||
118 | } else | ||
119 | return -EINVAL; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, snd_pcm_uframes_t frames) | ||
124 | { | ||
125 | int err; | ||
126 | snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); | ||
127 | if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { | ||
128 | snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); | ||
129 | while (plugin->next) { | ||
130 | if (plugin->dst_frames) | ||
131 | frames = plugin->dst_frames(plugin, frames); | ||
132 | snd_assert(frames > 0, return -ENXIO); | ||
133 | plugin = plugin->next; | ||
134 | err = snd_pcm_plugin_alloc(plugin, frames); | ||
135 | if (err < 0) | ||
136 | return err; | ||
137 | } | ||
138 | } else { | ||
139 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | ||
140 | while (plugin->prev) { | ||
141 | if (plugin->src_frames) | ||
142 | frames = plugin->src_frames(plugin, frames); | ||
143 | snd_assert(frames > 0, return -ENXIO); | ||
144 | plugin = plugin->prev; | ||
145 | err = snd_pcm_plugin_alloc(plugin, frames); | ||
146 | if (err < 0) | ||
147 | return err; | ||
148 | } | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | |||
154 | snd_pcm_sframes_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, | ||
155 | snd_pcm_uframes_t frames, | ||
156 | snd_pcm_plugin_channel_t **channels) | ||
157 | { | ||
158 | *channels = plugin->buf_channels; | ||
159 | return frames; | ||
160 | } | ||
161 | |||
162 | int snd_pcm_plugin_build(snd_pcm_plug_t *plug, | ||
163 | const char *name, | ||
164 | snd_pcm_plugin_format_t *src_format, | ||
165 | snd_pcm_plugin_format_t *dst_format, | ||
166 | size_t extra, | ||
167 | snd_pcm_plugin_t **ret) | ||
168 | { | ||
169 | snd_pcm_plugin_t *plugin; | ||
170 | unsigned int channels; | ||
171 | |||
172 | snd_assert(plug != NULL, return -ENXIO); | ||
173 | snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); | ||
174 | plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL); | ||
175 | if (plugin == NULL) | ||
176 | return -ENOMEM; | ||
177 | plugin->name = name; | ||
178 | plugin->plug = plug; | ||
179 | plugin->stream = snd_pcm_plug_stream(plug); | ||
180 | plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
181 | plugin->src_format = *src_format; | ||
182 | plugin->src_width = snd_pcm_format_physical_width(src_format->format); | ||
183 | snd_assert(plugin->src_width > 0, ); | ||
184 | plugin->dst_format = *dst_format; | ||
185 | plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); | ||
186 | snd_assert(plugin->dst_width > 0, ); | ||
187 | if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
188 | channels = src_format->channels; | ||
189 | else | ||
190 | channels = dst_format->channels; | ||
191 | plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL); | ||
192 | if (plugin->buf_channels == NULL) { | ||
193 | snd_pcm_plugin_free(plugin); | ||
194 | return -ENOMEM; | ||
195 | } | ||
196 | plugin->src_vmask = bitset_alloc(src_format->channels); | ||
197 | if (plugin->src_vmask == NULL) { | ||
198 | snd_pcm_plugin_free(plugin); | ||
199 | return -ENOMEM; | ||
200 | } | ||
201 | plugin->dst_vmask = bitset_alloc(dst_format->channels); | ||
202 | if (plugin->dst_vmask == NULL) { | ||
203 | snd_pcm_plugin_free(plugin); | ||
204 | return -ENOMEM; | ||
205 | } | ||
206 | plugin->client_channels = snd_pcm_plugin_client_channels; | ||
207 | plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; | ||
208 | plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; | ||
209 | *ret = plugin; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) | ||
214 | { | ||
215 | if (! plugin) | ||
216 | return 0; | ||
217 | if (plugin->private_free) | ||
218 | plugin->private_free(plugin); | ||
219 | kfree(plugin->buf_channels); | ||
220 | vfree(plugin->buf); | ||
221 | kfree(plugin->src_vmask); | ||
222 | kfree(plugin->dst_vmask); | ||
223 | kfree(plugin); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | snd_pcm_sframes_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t drv_frames) | ||
228 | { | ||
229 | snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; | ||
230 | int stream = snd_pcm_plug_stream(plug); | ||
231 | |||
232 | snd_assert(plug != NULL, return -ENXIO); | ||
233 | if (drv_frames == 0) | ||
234 | return 0; | ||
235 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
236 | plugin = snd_pcm_plug_last(plug); | ||
237 | while (plugin && drv_frames > 0) { | ||
238 | plugin_prev = plugin->prev; | ||
239 | if (plugin->src_frames) | ||
240 | drv_frames = plugin->src_frames(plugin, drv_frames); | ||
241 | plugin = plugin_prev; | ||
242 | } | ||
243 | } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
244 | plugin = snd_pcm_plug_first(plug); | ||
245 | while (plugin && drv_frames > 0) { | ||
246 | plugin_next = plugin->next; | ||
247 | if (plugin->dst_frames) | ||
248 | drv_frames = plugin->dst_frames(plugin, drv_frames); | ||
249 | plugin = plugin_next; | ||
250 | } | ||
251 | } else | ||
252 | snd_BUG(); | ||
253 | return drv_frames; | ||
254 | } | ||
255 | |||
256 | snd_pcm_sframes_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, snd_pcm_uframes_t clt_frames) | ||
257 | { | ||
258 | snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; | ||
259 | snd_pcm_sframes_t frames; | ||
260 | int stream = snd_pcm_plug_stream(plug); | ||
261 | |||
262 | snd_assert(plug != NULL, return -ENXIO); | ||
263 | if (clt_frames == 0) | ||
264 | return 0; | ||
265 | frames = clt_frames; | ||
266 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
267 | plugin = snd_pcm_plug_first(plug); | ||
268 | while (plugin && frames > 0) { | ||
269 | plugin_next = plugin->next; | ||
270 | if (plugin->dst_frames) { | ||
271 | frames = plugin->dst_frames(plugin, frames); | ||
272 | if (frames < 0) | ||
273 | return frames; | ||
274 | } | ||
275 | plugin = plugin_next; | ||
276 | } | ||
277 | } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
278 | plugin = snd_pcm_plug_last(plug); | ||
279 | while (plugin) { | ||
280 | plugin_prev = plugin->prev; | ||
281 | if (plugin->src_frames) { | ||
282 | frames = plugin->src_frames(plugin, frames); | ||
283 | if (frames < 0) | ||
284 | return frames; | ||
285 | } | ||
286 | plugin = plugin_prev; | ||
287 | } | ||
288 | } else | ||
289 | snd_BUG(); | ||
290 | return frames; | ||
291 | } | ||
292 | |||
293 | static int snd_pcm_plug_formats(snd_mask_t *mask, int format) | ||
294 | { | ||
295 | snd_mask_t formats = *mask; | ||
296 | u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | | ||
297 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | | ||
298 | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | | ||
299 | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | | ||
300 | SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | | ||
301 | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | | ||
302 | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); | ||
303 | snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); | ||
304 | |||
305 | if (formats.bits[0] & (u32)linfmts) | ||
306 | formats.bits[0] |= (u32)linfmts; | ||
307 | if (formats.bits[1] & (u32)(linfmts >> 32)) | ||
308 | formats.bits[1] |= (u32)(linfmts >> 32); | ||
309 | return snd_mask_test(&formats, format); | ||
310 | } | ||
311 | |||
312 | static int preferred_formats[] = { | ||
313 | SNDRV_PCM_FORMAT_S16_LE, | ||
314 | SNDRV_PCM_FORMAT_S16_BE, | ||
315 | SNDRV_PCM_FORMAT_U16_LE, | ||
316 | SNDRV_PCM_FORMAT_U16_BE, | ||
317 | SNDRV_PCM_FORMAT_S24_LE, | ||
318 | SNDRV_PCM_FORMAT_S24_BE, | ||
319 | SNDRV_PCM_FORMAT_U24_LE, | ||
320 | SNDRV_PCM_FORMAT_U24_BE, | ||
321 | SNDRV_PCM_FORMAT_S32_LE, | ||
322 | SNDRV_PCM_FORMAT_S32_BE, | ||
323 | SNDRV_PCM_FORMAT_U32_LE, | ||
324 | SNDRV_PCM_FORMAT_U32_BE, | ||
325 | SNDRV_PCM_FORMAT_S8, | ||
326 | SNDRV_PCM_FORMAT_U8 | ||
327 | }; | ||
328 | |||
329 | int snd_pcm_plug_slave_format(int format, snd_mask_t *format_mask) | ||
330 | { | ||
331 | if (snd_mask_test(format_mask, format)) | ||
332 | return format; | ||
333 | if (! snd_pcm_plug_formats(format_mask, format)) | ||
334 | return -EINVAL; | ||
335 | if (snd_pcm_format_linear(format)) { | ||
336 | int width = snd_pcm_format_width(format); | ||
337 | int unsignd = snd_pcm_format_unsigned(format); | ||
338 | int big = snd_pcm_format_big_endian(format); | ||
339 | int format1; | ||
340 | int wid, width1=width; | ||
341 | int dwidth1 = 8; | ||
342 | for (wid = 0; wid < 4; ++wid) { | ||
343 | int end, big1 = big; | ||
344 | for (end = 0; end < 2; ++end) { | ||
345 | int sgn, unsignd1 = unsignd; | ||
346 | for (sgn = 0; sgn < 2; ++sgn) { | ||
347 | format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); | ||
348 | if (format1 >= 0 && | ||
349 | snd_mask_test(format_mask, format1)) | ||
350 | goto _found; | ||
351 | unsignd1 = !unsignd1; | ||
352 | } | ||
353 | big1 = !big1; | ||
354 | } | ||
355 | if (width1 == 32) { | ||
356 | dwidth1 = -dwidth1; | ||
357 | width1 = width; | ||
358 | } | ||
359 | width1 += dwidth1; | ||
360 | } | ||
361 | return -EINVAL; | ||
362 | _found: | ||
363 | return format1; | ||
364 | } else { | ||
365 | unsigned int i; | ||
366 | switch (format) { | ||
367 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
368 | for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { | ||
369 | int format1 = preferred_formats[i]; | ||
370 | if (snd_mask_test(format_mask, format1)) | ||
371 | return format1; | ||
372 | } | ||
373 | default: | ||
374 | return -EINVAL; | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | |||
379 | int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, | ||
380 | snd_pcm_hw_params_t *params, | ||
381 | snd_pcm_hw_params_t *slave_params) | ||
382 | { | ||
383 | snd_pcm_plugin_format_t tmpformat; | ||
384 | snd_pcm_plugin_format_t dstformat; | ||
385 | snd_pcm_plugin_format_t srcformat; | ||
386 | int src_access, dst_access; | ||
387 | snd_pcm_plugin_t *plugin = NULL; | ||
388 | int err; | ||
389 | int stream = snd_pcm_plug_stream(plug); | ||
390 | int slave_interleaved = (params_channels(slave_params) == 1 || | ||
391 | params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); | ||
392 | |||
393 | switch (stream) { | ||
394 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
395 | dstformat.format = params_format(slave_params); | ||
396 | dstformat.rate = params_rate(slave_params); | ||
397 | dstformat.channels = params_channels(slave_params); | ||
398 | srcformat.format = params_format(params); | ||
399 | srcformat.rate = params_rate(params); | ||
400 | srcformat.channels = params_channels(params); | ||
401 | src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
402 | dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | ||
403 | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | ||
404 | break; | ||
405 | case SNDRV_PCM_STREAM_CAPTURE: | ||
406 | dstformat.format = params_format(params); | ||
407 | dstformat.rate = params_rate(params); | ||
408 | dstformat.channels = params_channels(params); | ||
409 | srcformat.format = params_format(slave_params); | ||
410 | srcformat.rate = params_rate(slave_params); | ||
411 | srcformat.channels = params_channels(slave_params); | ||
412 | src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | ||
413 | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | ||
414 | dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | ||
415 | break; | ||
416 | default: | ||
417 | snd_BUG(); | ||
418 | return -EINVAL; | ||
419 | } | ||
420 | tmpformat = srcformat; | ||
421 | |||
422 | pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", | ||
423 | srcformat.format, | ||
424 | srcformat.rate, | ||
425 | srcformat.channels); | ||
426 | pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", | ||
427 | dstformat.format, | ||
428 | dstformat.rate, | ||
429 | dstformat.channels); | ||
430 | |||
431 | /* Format change (linearization) */ | ||
432 | if ((srcformat.format != dstformat.format || | ||
433 | !rate_match(srcformat.rate, dstformat.rate) || | ||
434 | srcformat.channels != dstformat.channels) && | ||
435 | !snd_pcm_format_linear(srcformat.format)) { | ||
436 | if (snd_pcm_format_linear(dstformat.format)) | ||
437 | tmpformat.format = dstformat.format; | ||
438 | else | ||
439 | tmpformat.format = SNDRV_PCM_FORMAT_S16; | ||
440 | switch (srcformat.format) { | ||
441 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
442 | err = snd_pcm_plugin_build_mulaw(plug, | ||
443 | &srcformat, &tmpformat, | ||
444 | &plugin); | ||
445 | break; | ||
446 | default: | ||
447 | return -EINVAL; | ||
448 | } | ||
449 | pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | ||
450 | if (err < 0) | ||
451 | return err; | ||
452 | err = snd_pcm_plugin_append(plugin); | ||
453 | if (err < 0) { | ||
454 | snd_pcm_plugin_free(plugin); | ||
455 | return err; | ||
456 | } | ||
457 | srcformat = tmpformat; | ||
458 | src_access = dst_access; | ||
459 | } | ||
460 | |||
461 | /* channels reduction */ | ||
462 | if (srcformat.channels > dstformat.channels) { | ||
463 | int sv = srcformat.channels; | ||
464 | int dv = dstformat.channels; | ||
465 | route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); | ||
466 | if (ttable == NULL) | ||
467 | return -ENOMEM; | ||
468 | #if 1 | ||
469 | if (sv == 2 && dv == 1) { | ||
470 | ttable[0] = HALF; | ||
471 | ttable[1] = HALF; | ||
472 | } else | ||
473 | #endif | ||
474 | { | ||
475 | int v; | ||
476 | for (v = 0; v < dv; ++v) | ||
477 | ttable[v * sv + v] = FULL; | ||
478 | } | ||
479 | tmpformat.channels = dstformat.channels; | ||
480 | if (rate_match(srcformat.rate, dstformat.rate) && | ||
481 | snd_pcm_format_linear(dstformat.format)) | ||
482 | tmpformat.format = dstformat.format; | ||
483 | err = snd_pcm_plugin_build_route(plug, | ||
484 | &srcformat, &tmpformat, | ||
485 | ttable, &plugin); | ||
486 | kfree(ttable); | ||
487 | pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | ||
488 | if (err < 0) { | ||
489 | snd_pcm_plugin_free(plugin); | ||
490 | return err; | ||
491 | } | ||
492 | err = snd_pcm_plugin_append(plugin); | ||
493 | if (err < 0) { | ||
494 | snd_pcm_plugin_free(plugin); | ||
495 | return err; | ||
496 | } | ||
497 | srcformat = tmpformat; | ||
498 | src_access = dst_access; | ||
499 | } | ||
500 | |||
501 | /* rate resampling */ | ||
502 | if (!rate_match(srcformat.rate, dstformat.rate)) { | ||
503 | tmpformat.rate = dstformat.rate; | ||
504 | if (srcformat.channels == dstformat.channels && | ||
505 | snd_pcm_format_linear(dstformat.format)) | ||
506 | tmpformat.format = dstformat.format; | ||
507 | err = snd_pcm_plugin_build_rate(plug, | ||
508 | &srcformat, &tmpformat, | ||
509 | &plugin); | ||
510 | pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); | ||
511 | if (err < 0) { | ||
512 | snd_pcm_plugin_free(plugin); | ||
513 | return err; | ||
514 | } | ||
515 | err = snd_pcm_plugin_append(plugin); | ||
516 | if (err < 0) { | ||
517 | snd_pcm_plugin_free(plugin); | ||
518 | return err; | ||
519 | } | ||
520 | srcformat = tmpformat; | ||
521 | src_access = dst_access; | ||
522 | } | ||
523 | |||
524 | /* channels extension */ | ||
525 | if (srcformat.channels < dstformat.channels) { | ||
526 | int sv = srcformat.channels; | ||
527 | int dv = dstformat.channels; | ||
528 | route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); | ||
529 | if (ttable == NULL) | ||
530 | return -ENOMEM; | ||
531 | #if 0 | ||
532 | { | ||
533 | int v; | ||
534 | for (v = 0; v < sv; ++v) | ||
535 | ttable[v * sv + v] = FULL; | ||
536 | } | ||
537 | #else | ||
538 | { | ||
539 | /* Playback is spreaded on all channels */ | ||
540 | int vd, vs; | ||
541 | for (vd = 0, vs = 0; vd < dv; ++vd) { | ||
542 | ttable[vd * sv + vs] = FULL; | ||
543 | vs++; | ||
544 | if (vs == sv) | ||
545 | vs = 0; | ||
546 | } | ||
547 | } | ||
548 | #endif | ||
549 | tmpformat.channels = dstformat.channels; | ||
550 | if (snd_pcm_format_linear(dstformat.format)) | ||
551 | tmpformat.format = dstformat.format; | ||
552 | err = snd_pcm_plugin_build_route(plug, | ||
553 | &srcformat, &tmpformat, | ||
554 | ttable, &plugin); | ||
555 | kfree(ttable); | ||
556 | pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | ||
557 | if (err < 0) { | ||
558 | snd_pcm_plugin_free(plugin); | ||
559 | return err; | ||
560 | } | ||
561 | err = snd_pcm_plugin_append(plugin); | ||
562 | if (err < 0) { | ||
563 | snd_pcm_plugin_free(plugin); | ||
564 | return err; | ||
565 | } | ||
566 | srcformat = tmpformat; | ||
567 | src_access = dst_access; | ||
568 | } | ||
569 | |||
570 | /* format change */ | ||
571 | if (srcformat.format != dstformat.format) { | ||
572 | tmpformat.format = dstformat.format; | ||
573 | if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { | ||
574 | err = snd_pcm_plugin_build_mulaw(plug, | ||
575 | &srcformat, &tmpformat, | ||
576 | &plugin); | ||
577 | } | ||
578 | else if (snd_pcm_format_linear(srcformat.format) && | ||
579 | snd_pcm_format_linear(tmpformat.format)) { | ||
580 | err = snd_pcm_plugin_build_linear(plug, | ||
581 | &srcformat, &tmpformat, | ||
582 | &plugin); | ||
583 | } | ||
584 | else | ||
585 | return -EINVAL; | ||
586 | pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | ||
587 | if (err < 0) | ||
588 | return err; | ||
589 | err = snd_pcm_plugin_append(plugin); | ||
590 | if (err < 0) { | ||
591 | snd_pcm_plugin_free(plugin); | ||
592 | return err; | ||
593 | } | ||
594 | srcformat = tmpformat; | ||
595 | src_access = dst_access; | ||
596 | } | ||
597 | |||
598 | /* de-interleave */ | ||
599 | if (src_access != dst_access) { | ||
600 | err = snd_pcm_plugin_build_copy(plug, | ||
601 | &srcformat, | ||
602 | &tmpformat, | ||
603 | &plugin); | ||
604 | pdprintf("interleave change (copy: returns %i)\n", err); | ||
605 | if (err < 0) | ||
606 | return err; | ||
607 | err = snd_pcm_plugin_append(plugin); | ||
608 | if (err < 0) { | ||
609 | snd_pcm_plugin_free(plugin); | ||
610 | return err; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, | ||
618 | char *buf, | ||
619 | snd_pcm_uframes_t count, | ||
620 | snd_pcm_plugin_channel_t **channels) | ||
621 | { | ||
622 | snd_pcm_plugin_t *plugin; | ||
623 | snd_pcm_plugin_channel_t *v; | ||
624 | snd_pcm_plugin_format_t *format; | ||
625 | int width, nchannels, channel; | ||
626 | int stream = snd_pcm_plug_stream(plug); | ||
627 | |||
628 | snd_assert(buf != NULL, return -ENXIO); | ||
629 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
630 | plugin = snd_pcm_plug_first(plug); | ||
631 | format = &plugin->src_format; | ||
632 | } else { | ||
633 | plugin = snd_pcm_plug_last(plug); | ||
634 | format = &plugin->dst_format; | ||
635 | } | ||
636 | v = plugin->buf_channels; | ||
637 | *channels = v; | ||
638 | if ((width = snd_pcm_format_physical_width(format->format)) < 0) | ||
639 | return width; | ||
640 | nchannels = format->channels; | ||
641 | snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); | ||
642 | for (channel = 0; channel < nchannels; channel++, v++) { | ||
643 | v->frames = count; | ||
644 | v->enabled = 1; | ||
645 | v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); | ||
646 | v->area.addr = buf; | ||
647 | v->area.first = channel * width; | ||
648 | v->area.step = nchannels * width; | ||
649 | } | ||
650 | return count; | ||
651 | } | ||
652 | |||
653 | static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, | ||
654 | bitset_t *client_vmask) | ||
655 | { | ||
656 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | ||
657 | if (plugin == NULL) { | ||
658 | return 0; | ||
659 | } else { | ||
660 | int schannels = plugin->dst_format.channels; | ||
661 | bitset_t bs[bitset_size(schannels)]; | ||
662 | bitset_t *srcmask; | ||
663 | bitset_t *dstmask = bs; | ||
664 | int err; | ||
665 | bitset_one(dstmask, schannels); | ||
666 | if (plugin == NULL) { | ||
667 | bitset_and(client_vmask, dstmask, schannels); | ||
668 | return 0; | ||
669 | } | ||
670 | while (1) { | ||
671 | err = plugin->src_channels_mask(plugin, dstmask, &srcmask); | ||
672 | if (err < 0) | ||
673 | return err; | ||
674 | dstmask = srcmask; | ||
675 | if (plugin->prev == NULL) | ||
676 | break; | ||
677 | plugin = plugin->prev; | ||
678 | } | ||
679 | bitset_and(client_vmask, dstmask, plugin->src_format.channels); | ||
680 | return 0; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, | ||
685 | snd_pcm_plugin_channel_t *src_channels) | ||
686 | { | ||
687 | snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); | ||
688 | unsigned int nchannels = plugin->src_format.channels; | ||
689 | bitset_t bs[bitset_size(nchannels)]; | ||
690 | bitset_t *srcmask = bs; | ||
691 | int err; | ||
692 | unsigned int channel; | ||
693 | for (channel = 0; channel < nchannels; channel++) { | ||
694 | if (src_channels[channel].enabled) | ||
695 | bitset_set(srcmask, channel); | ||
696 | else | ||
697 | bitset_reset(srcmask, channel); | ||
698 | } | ||
699 | err = snd_pcm_plug_playback_channels_mask(plug, srcmask); | ||
700 | if (err < 0) | ||
701 | return err; | ||
702 | for (channel = 0; channel < nchannels; channel++) { | ||
703 | if (!bitset_get(srcmask, channel)) | ||
704 | src_channels[channel].enabled = 0; | ||
705 | } | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, | ||
710 | snd_pcm_plugin_channel_t *src_channels, | ||
711 | snd_pcm_plugin_channel_t *client_channels) | ||
712 | { | ||
713 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | ||
714 | unsigned int nchannels = plugin->dst_format.channels; | ||
715 | bitset_t bs[bitset_size(nchannels)]; | ||
716 | bitset_t *dstmask = bs; | ||
717 | bitset_t *srcmask; | ||
718 | int err; | ||
719 | unsigned int channel; | ||
720 | for (channel = 0; channel < nchannels; channel++) { | ||
721 | if (client_channels[channel].enabled) | ||
722 | bitset_set(dstmask, channel); | ||
723 | else | ||
724 | bitset_reset(dstmask, channel); | ||
725 | } | ||
726 | while (plugin) { | ||
727 | err = plugin->src_channels_mask(plugin, dstmask, &srcmask); | ||
728 | if (err < 0) | ||
729 | return err; | ||
730 | dstmask = srcmask; | ||
731 | plugin = plugin->prev; | ||
732 | } | ||
733 | plugin = snd_pcm_plug_first(plug); | ||
734 | nchannels = plugin->src_format.channels; | ||
735 | for (channel = 0; channel < nchannels; channel++) { | ||
736 | if (!bitset_get(dstmask, channel)) | ||
737 | src_channels[channel].enabled = 0; | ||
738 | } | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | snd_pcm_sframes_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, snd_pcm_uframes_t size) | ||
743 | { | ||
744 | snd_pcm_plugin_t *plugin, *next; | ||
745 | snd_pcm_plugin_channel_t *dst_channels; | ||
746 | int err; | ||
747 | snd_pcm_sframes_t frames = size; | ||
748 | |||
749 | if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) | ||
750 | return err; | ||
751 | |||
752 | plugin = snd_pcm_plug_first(plug); | ||
753 | while (plugin && frames > 0) { | ||
754 | if ((next = plugin->next) != NULL) { | ||
755 | snd_pcm_sframes_t frames1 = frames; | ||
756 | if (plugin->dst_frames) | ||
757 | frames1 = plugin->dst_frames(plugin, frames); | ||
758 | if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { | ||
759 | return err; | ||
760 | } | ||
761 | if (err != frames1) { | ||
762 | frames = err; | ||
763 | if (plugin->src_frames) | ||
764 | frames = plugin->src_frames(plugin, frames1); | ||
765 | } | ||
766 | } else | ||
767 | dst_channels = NULL; | ||
768 | pdprintf("write plugin: %s, %li\n", plugin->name, frames); | ||
769 | if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | ||
770 | return frames; | ||
771 | src_channels = dst_channels; | ||
772 | plugin = next; | ||
773 | } | ||
774 | return snd_pcm_plug_client_size(plug, frames); | ||
775 | } | ||
776 | |||
777 | snd_pcm_sframes_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, snd_pcm_uframes_t size) | ||
778 | { | ||
779 | snd_pcm_plugin_t *plugin, *next; | ||
780 | snd_pcm_plugin_channel_t *src_channels, *dst_channels; | ||
781 | snd_pcm_sframes_t frames = size; | ||
782 | int err; | ||
783 | |||
784 | frames = snd_pcm_plug_slave_size(plug, frames); | ||
785 | if (frames < 0) | ||
786 | return frames; | ||
787 | |||
788 | src_channels = NULL; | ||
789 | plugin = snd_pcm_plug_first(plug); | ||
790 | while (plugin && frames > 0) { | ||
791 | if ((next = plugin->next) != NULL) { | ||
792 | if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { | ||
793 | return err; | ||
794 | } | ||
795 | frames = err; | ||
796 | if (!plugin->prev) { | ||
797 | if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0) | ||
798 | return err; | ||
799 | } | ||
800 | } else { | ||
801 | dst_channels = dst_channels_final; | ||
802 | } | ||
803 | pdprintf("read plugin: %s, %li\n", plugin->name, frames); | ||
804 | if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | ||
805 | return frames; | ||
806 | plugin = next; | ||
807 | src_channels = dst_channels; | ||
808 | } | ||
809 | return frames; | ||
810 | } | ||
811 | |||
812 | int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, | ||
813 | size_t samples, int format) | ||
814 | { | ||
815 | /* FIXME: sub byte resolution and odd dst_offset */ | ||
816 | unsigned char *dst; | ||
817 | unsigned int dst_step; | ||
818 | int width; | ||
819 | const unsigned char *silence; | ||
820 | if (!dst_area->addr) | ||
821 | return 0; | ||
822 | dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | ||
823 | width = snd_pcm_format_physical_width(format); | ||
824 | if (width <= 0) | ||
825 | return -EINVAL; | ||
826 | if (dst_area->step == (unsigned int) width && width >= 8) | ||
827 | return snd_pcm_format_set_silence(format, dst, samples); | ||
828 | silence = snd_pcm_format_silence_64(format); | ||
829 | if (! silence) | ||
830 | return -EINVAL; | ||
831 | dst_step = dst_area->step / 8; | ||
832 | if (width == 4) { | ||
833 | /* Ima ADPCM */ | ||
834 | int dstbit = dst_area->first % 8; | ||
835 | int dstbit_step = dst_area->step % 8; | ||
836 | while (samples-- > 0) { | ||
837 | if (dstbit) | ||
838 | *dst &= 0xf0; | ||
839 | else | ||
840 | *dst &= 0x0f; | ||
841 | dst += dst_step; | ||
842 | dstbit += dstbit_step; | ||
843 | if (dstbit == 8) { | ||
844 | dst++; | ||
845 | dstbit = 0; | ||
846 | } | ||
847 | } | ||
848 | } else { | ||
849 | width /= 8; | ||
850 | while (samples-- > 0) { | ||
851 | memcpy(dst, silence, width); | ||
852 | dst += dst_step; | ||
853 | } | ||
854 | } | ||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, | ||
859 | const snd_pcm_channel_area_t *dst_area, size_t dst_offset, | ||
860 | size_t samples, int format) | ||
861 | { | ||
862 | /* FIXME: sub byte resolution and odd dst_offset */ | ||
863 | char *src, *dst; | ||
864 | int width; | ||
865 | int src_step, dst_step; | ||
866 | src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; | ||
867 | if (!src_area->addr) | ||
868 | return snd_pcm_area_silence(dst_area, dst_offset, samples, format); | ||
869 | dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | ||
870 | if (!dst_area->addr) | ||
871 | return 0; | ||
872 | width = snd_pcm_format_physical_width(format); | ||
873 | if (width <= 0) | ||
874 | return -EINVAL; | ||
875 | if (src_area->step == (unsigned int) width && | ||
876 | dst_area->step == (unsigned int) width && width >= 8) { | ||
877 | size_t bytes = samples * width / 8; | ||
878 | memcpy(dst, src, bytes); | ||
879 | return 0; | ||
880 | } | ||
881 | src_step = src_area->step / 8; | ||
882 | dst_step = dst_area->step / 8; | ||
883 | if (width == 4) { | ||
884 | /* Ima ADPCM */ | ||
885 | int srcbit = src_area->first % 8; | ||
886 | int srcbit_step = src_area->step % 8; | ||
887 | int dstbit = dst_area->first % 8; | ||
888 | int dstbit_step = dst_area->step % 8; | ||
889 | while (samples-- > 0) { | ||
890 | unsigned char srcval; | ||
891 | if (srcbit) | ||
892 | srcval = *src & 0x0f; | ||
893 | else | ||
894 | srcval = (*src & 0xf0) >> 4; | ||
895 | if (dstbit) | ||
896 | *dst = (*dst & 0xf0) | srcval; | ||
897 | else | ||
898 | *dst = (*dst & 0x0f) | (srcval << 4); | ||
899 | src += src_step; | ||
900 | srcbit += srcbit_step; | ||
901 | if (srcbit == 8) { | ||
902 | src++; | ||
903 | srcbit = 0; | ||
904 | } | ||
905 | dst += dst_step; | ||
906 | dstbit += dstbit_step; | ||
907 | if (dstbit == 8) { | ||
908 | dst++; | ||
909 | dstbit = 0; | ||
910 | } | ||
911 | } | ||
912 | } else { | ||
913 | width /= 8; | ||
914 | while (samples-- > 0) { | ||
915 | memcpy(dst, src, width); | ||
916 | src += src_step; | ||
917 | dst += dst_step; | ||
918 | } | ||
919 | } | ||
920 | return 0; | ||
921 | } | ||