]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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, | |
47eaebfd TI |
40 | unsigned long *dst_vmask, |
41 | unsigned long **src_vmask) | |
1da177e4 | 42 | { |
47eaebfd TI |
43 | unsigned long *vmask = plugin->src_vmask; |
44 | bitmap_copy(vmask, dst_vmask, plugin->src_format.channels); | |
1da177e4 LT |
45 | *src_vmask = vmask; |
46 | return 0; | |
47 | } | |
48 | ||
49 | static int snd_pcm_plugin_dst_channels_mask(snd_pcm_plugin_t *plugin, | |
47eaebfd TI |
50 | unsigned long *src_vmask, |
51 | unsigned long **dst_vmask) | |
1da177e4 | 52 | { |
47eaebfd TI |
53 | unsigned long *vmask = plugin->dst_vmask; |
54 | bitmap_copy(vmask, src_vmask, plugin->dst_format.channels); | |
1da177e4 LT |
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); | |
ca2c0966 | 174 | plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL); |
1da177e4 LT |
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 | } | |
47eaebfd | 196 | plugin->src_vmask = bitmap_alloc(src_format->channels); |
1da177e4 LT |
197 | if (plugin->src_vmask == NULL) { |
198 | snd_pcm_plugin_free(plugin); | |
199 | return -ENOMEM; | |
200 | } | |
47eaebfd | 201 | plugin->dst_vmask = bitmap_alloc(dst_format->channels); |
1da177e4 LT |
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, | |
47eaebfd | 654 | unsigned long *client_vmask) |
1da177e4 LT |
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; | |
47eaebfd TI |
661 | DECLARE_BITMAP(bs, schannels); |
662 | unsigned long *srcmask; | |
663 | unsigned long *dstmask = bs; | |
1da177e4 | 664 | int err; |
47eaebfd | 665 | bitmap_fill(dstmask, schannels); |
94f19c9a | 666 | |
1da177e4 LT |
667 | while (1) { |
668 | err = plugin->src_channels_mask(plugin, dstmask, &srcmask); | |
669 | if (err < 0) | |
670 | return err; | |
671 | dstmask = srcmask; | |
672 | if (plugin->prev == NULL) | |
673 | break; | |
674 | plugin = plugin->prev; | |
675 | } | |
47eaebfd | 676 | bitmap_and(client_vmask, client_vmask, dstmask, plugin->src_format.channels); |
1da177e4 LT |
677 | return 0; |
678 | } | |
679 | } | |
680 | ||
681 | static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, | |
682 | snd_pcm_plugin_channel_t *src_channels) | |
683 | { | |
684 | snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); | |
685 | unsigned int nchannels = plugin->src_format.channels; | |
47eaebfd TI |
686 | DECLARE_BITMAP(bs, nchannels); |
687 | unsigned long *srcmask = bs; | |
1da177e4 LT |
688 | int err; |
689 | unsigned int channel; | |
690 | for (channel = 0; channel < nchannels; channel++) { | |
691 | if (src_channels[channel].enabled) | |
47eaebfd | 692 | set_bit(channel, srcmask); |
1da177e4 | 693 | else |
47eaebfd | 694 | clear_bit(channel, srcmask); |
1da177e4 LT |
695 | } |
696 | err = snd_pcm_plug_playback_channels_mask(plug, srcmask); | |
697 | if (err < 0) | |
698 | return err; | |
699 | for (channel = 0; channel < nchannels; channel++) { | |
47eaebfd | 700 | if (!test_bit(channel, srcmask)) |
1da177e4 LT |
701 | src_channels[channel].enabled = 0; |
702 | } | |
703 | return 0; | |
704 | } | |
705 | ||
706 | static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, | |
707 | snd_pcm_plugin_channel_t *src_channels, | |
708 | snd_pcm_plugin_channel_t *client_channels) | |
709 | { | |
710 | snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); | |
711 | unsigned int nchannels = plugin->dst_format.channels; | |
47eaebfd TI |
712 | DECLARE_BITMAP(bs, nchannels); |
713 | unsigned long *dstmask = bs; | |
714 | unsigned long *srcmask; | |
1da177e4 LT |
715 | int err; |
716 | unsigned int channel; | |
717 | for (channel = 0; channel < nchannels; channel++) { | |
718 | if (client_channels[channel].enabled) | |
47eaebfd | 719 | set_bit(channel, dstmask); |
1da177e4 | 720 | else |
47eaebfd | 721 | clear_bit(channel, dstmask); |
1da177e4 LT |
722 | } |
723 | while (plugin) { | |
724 | err = plugin->src_channels_mask(plugin, dstmask, &srcmask); | |
725 | if (err < 0) | |
726 | return err; | |
727 | dstmask = srcmask; | |
728 | plugin = plugin->prev; | |
729 | } | |
730 | plugin = snd_pcm_plug_first(plug); | |
731 | nchannels = plugin->src_format.channels; | |
732 | for (channel = 0; channel < nchannels; channel++) { | |
47eaebfd | 733 | if (!test_bit(channel, dstmask)) |
1da177e4 LT |
734 | src_channels[channel].enabled = 0; |
735 | } | |
736 | return 0; | |
737 | } | |
738 | ||
739 | 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) | |
740 | { | |
741 | snd_pcm_plugin_t *plugin, *next; | |
742 | snd_pcm_plugin_channel_t *dst_channels; | |
743 | int err; | |
744 | snd_pcm_sframes_t frames = size; | |
745 | ||
746 | if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) | |
747 | return err; | |
748 | ||
749 | plugin = snd_pcm_plug_first(plug); | |
750 | while (plugin && frames > 0) { | |
751 | if ((next = plugin->next) != NULL) { | |
752 | snd_pcm_sframes_t frames1 = frames; | |
753 | if (plugin->dst_frames) | |
754 | frames1 = plugin->dst_frames(plugin, frames); | |
755 | if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { | |
756 | return err; | |
757 | } | |
758 | if (err != frames1) { | |
759 | frames = err; | |
760 | if (plugin->src_frames) | |
761 | frames = plugin->src_frames(plugin, frames1); | |
762 | } | |
763 | } else | |
764 | dst_channels = NULL; | |
765 | pdprintf("write plugin: %s, %li\n", plugin->name, frames); | |
766 | if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | |
767 | return frames; | |
768 | src_channels = dst_channels; | |
769 | plugin = next; | |
770 | } | |
771 | return snd_pcm_plug_client_size(plug, frames); | |
772 | } | |
773 | ||
774 | 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) | |
775 | { | |
776 | snd_pcm_plugin_t *plugin, *next; | |
777 | snd_pcm_plugin_channel_t *src_channels, *dst_channels; | |
778 | snd_pcm_sframes_t frames = size; | |
779 | int err; | |
780 | ||
781 | frames = snd_pcm_plug_slave_size(plug, frames); | |
782 | if (frames < 0) | |
783 | return frames; | |
784 | ||
785 | src_channels = NULL; | |
786 | plugin = snd_pcm_plug_first(plug); | |
787 | while (plugin && frames > 0) { | |
788 | if ((next = plugin->next) != NULL) { | |
789 | if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { | |
790 | return err; | |
791 | } | |
792 | frames = err; | |
793 | if (!plugin->prev) { | |
794 | if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final)) < 0) | |
795 | return err; | |
796 | } | |
797 | } else { | |
798 | dst_channels = dst_channels_final; | |
799 | } | |
800 | pdprintf("read plugin: %s, %li\n", plugin->name, frames); | |
801 | if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) | |
802 | return frames; | |
803 | plugin = next; | |
804 | src_channels = dst_channels; | |
805 | } | |
806 | return frames; | |
807 | } | |
808 | ||
809 | int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, | |
810 | size_t samples, int format) | |
811 | { | |
812 | /* FIXME: sub byte resolution and odd dst_offset */ | |
813 | unsigned char *dst; | |
814 | unsigned int dst_step; | |
815 | int width; | |
816 | const unsigned char *silence; | |
817 | if (!dst_area->addr) | |
818 | return 0; | |
819 | dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | |
820 | width = snd_pcm_format_physical_width(format); | |
821 | if (width <= 0) | |
822 | return -EINVAL; | |
823 | if (dst_area->step == (unsigned int) width && width >= 8) | |
824 | return snd_pcm_format_set_silence(format, dst, samples); | |
825 | silence = snd_pcm_format_silence_64(format); | |
826 | if (! silence) | |
827 | return -EINVAL; | |
828 | dst_step = dst_area->step / 8; | |
829 | if (width == 4) { | |
830 | /* Ima ADPCM */ | |
831 | int dstbit = dst_area->first % 8; | |
832 | int dstbit_step = dst_area->step % 8; | |
833 | while (samples-- > 0) { | |
834 | if (dstbit) | |
835 | *dst &= 0xf0; | |
836 | else | |
837 | *dst &= 0x0f; | |
838 | dst += dst_step; | |
839 | dstbit += dstbit_step; | |
840 | if (dstbit == 8) { | |
841 | dst++; | |
842 | dstbit = 0; | |
843 | } | |
844 | } | |
845 | } else { | |
846 | width /= 8; | |
847 | while (samples-- > 0) { | |
848 | memcpy(dst, silence, width); | |
849 | dst += dst_step; | |
850 | } | |
851 | } | |
852 | return 0; | |
853 | } | |
854 | ||
855 | int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, | |
856 | const snd_pcm_channel_area_t *dst_area, size_t dst_offset, | |
857 | size_t samples, int format) | |
858 | { | |
859 | /* FIXME: sub byte resolution and odd dst_offset */ | |
860 | char *src, *dst; | |
861 | int width; | |
862 | int src_step, dst_step; | |
863 | src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; | |
864 | if (!src_area->addr) | |
865 | return snd_pcm_area_silence(dst_area, dst_offset, samples, format); | |
866 | dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; | |
867 | if (!dst_area->addr) | |
868 | return 0; | |
869 | width = snd_pcm_format_physical_width(format); | |
870 | if (width <= 0) | |
871 | return -EINVAL; | |
872 | if (src_area->step == (unsigned int) width && | |
873 | dst_area->step == (unsigned int) width && width >= 8) { | |
874 | size_t bytes = samples * width / 8; | |
875 | memcpy(dst, src, bytes); | |
876 | return 0; | |
877 | } | |
878 | src_step = src_area->step / 8; | |
879 | dst_step = dst_area->step / 8; | |
880 | if (width == 4) { | |
881 | /* Ima ADPCM */ | |
882 | int srcbit = src_area->first % 8; | |
883 | int srcbit_step = src_area->step % 8; | |
884 | int dstbit = dst_area->first % 8; | |
885 | int dstbit_step = dst_area->step % 8; | |
886 | while (samples-- > 0) { | |
887 | unsigned char srcval; | |
888 | if (srcbit) | |
889 | srcval = *src & 0x0f; | |
890 | else | |
891 | srcval = (*src & 0xf0) >> 4; | |
892 | if (dstbit) | |
893 | *dst = (*dst & 0xf0) | srcval; | |
894 | else | |
895 | *dst = (*dst & 0x0f) | (srcval << 4); | |
896 | src += src_step; | |
897 | srcbit += srcbit_step; | |
898 | if (srcbit == 8) { | |
899 | src++; | |
900 | srcbit = 0; | |
901 | } | |
902 | dst += dst_step; | |
903 | dstbit += dstbit_step; | |
904 | if (dstbit == 8) { | |
905 | dst++; | |
906 | dstbit = 0; | |
907 | } | |
908 | } | |
909 | } else { | |
910 | width /= 8; | |
911 | while (samples-- > 0) { | |
912 | memcpy(dst, src, width); | |
913 | src += src_step; | |
914 | dst += dst_step; | |
915 | } | |
916 | } | |
917 | return 0; | |
918 | } |