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> | |
21a3479a JK |
28 | |
29 | #ifdef CONFIG_SND_PCM_OSS_PLUGINS | |
30 | ||
1da177e4 LT |
31 | #include <linux/slab.h> |
32 | #include <linux/time.h> | |
33 | #include <linux/vmalloc.h> | |
34 | #include <sound/core.h> | |
35 | #include <sound/pcm.h> | |
36 | #include <sound/pcm_params.h> | |
37 | #include "pcm_plugin.h" | |
38 | ||
39 | #define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) | |
40 | #define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) | |
41 | ||
6ac77bc1 | 42 | static int snd_pcm_plugin_src_channels_mask(struct snd_pcm_plugin *plugin, |
47eaebfd TI |
43 | unsigned long *dst_vmask, |
44 | unsigned long **src_vmask) | |
1da177e4 | 45 | { |
47eaebfd TI |
46 | unsigned long *vmask = plugin->src_vmask; |
47 | bitmap_copy(vmask, dst_vmask, plugin->src_format.channels); | |
1da177e4 LT |
48 | *src_vmask = vmask; |
49 | return 0; | |
50 | } | |
51 | ||
6ac77bc1 | 52 | static int snd_pcm_plugin_dst_channels_mask(struct snd_pcm_plugin *plugin, |
47eaebfd TI |
53 | unsigned long *src_vmask, |
54 | unsigned long **dst_vmask) | |
1da177e4 | 55 | { |
47eaebfd TI |
56 | unsigned long *vmask = plugin->dst_vmask; |
57 | bitmap_copy(vmask, src_vmask, plugin->dst_format.channels); | |
1da177e4 LT |
58 | *dst_vmask = vmask; |
59 | return 0; | |
60 | } | |
61 | ||
62 | /* | |
63 | * because some cards might have rates "very close", we ignore | |
64 | * all "resampling" requests within +-5% | |
65 | */ | |
66 | static int rate_match(unsigned int src_rate, unsigned int dst_rate) | |
67 | { | |
68 | unsigned int low = (src_rate * 95) / 100; | |
69 | unsigned int high = (src_rate * 105) / 100; | |
70 | return dst_rate >= low && dst_rate <= high; | |
71 | } | |
72 | ||
6ac77bc1 | 73 | static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames) |
1da177e4 | 74 | { |
6ac77bc1 | 75 | struct snd_pcm_plugin_format *format; |
1da177e4 LT |
76 | ssize_t width; |
77 | size_t size; | |
78 | unsigned int channel; | |
6ac77bc1 | 79 | struct snd_pcm_plugin_channel *c; |
1da177e4 LT |
80 | |
81 | if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
82 | format = &plugin->src_format; | |
83 | } else { | |
84 | format = &plugin->dst_format; | |
85 | } | |
86 | if ((width = snd_pcm_format_physical_width(format->format)) < 0) | |
87 | return width; | |
88 | size = frames * format->channels * width; | |
89 | snd_assert((size % 8) == 0, return -ENXIO); | |
90 | size /= 8; | |
91 | if (plugin->buf_frames < frames) { | |
92 | vfree(plugin->buf); | |
93 | plugin->buf = vmalloc(size); | |
94 | plugin->buf_frames = frames; | |
95 | } | |
96 | if (!plugin->buf) { | |
97 | plugin->buf_frames = 0; | |
98 | return -ENOMEM; | |
99 | } | |
100 | c = plugin->buf_channels; | |
101 | if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { | |
102 | for (channel = 0; channel < format->channels; channel++, c++) { | |
103 | c->frames = frames; | |
104 | c->enabled = 1; | |
105 | c->wanted = 0; | |
106 | c->area.addr = plugin->buf; | |
107 | c->area.first = channel * width; | |
108 | c->area.step = format->channels * width; | |
109 | } | |
110 | } else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { | |
111 | snd_assert((size % format->channels) == 0,); | |
112 | size /= format->channels; | |
113 | for (channel = 0; channel < format->channels; channel++, c++) { | |
114 | c->frames = frames; | |
115 | c->enabled = 1; | |
116 | c->wanted = 0; | |
117 | c->area.addr = plugin->buf + (channel * size); | |
118 | c->area.first = 0; | |
119 | c->area.step = width; | |
120 | } | |
121 | } else | |
122 | return -EINVAL; | |
123 | return 0; | |
124 | } | |
125 | ||
6ac77bc1 | 126 | int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) |
1da177e4 LT |
127 | { |
128 | int err; | |
129 | snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); | |
130 | if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) { | |
6ac77bc1 | 131 | struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug); |
1da177e4 LT |
132 | while (plugin->next) { |
133 | if (plugin->dst_frames) | |
134 | frames = plugin->dst_frames(plugin, frames); | |
135 | snd_assert(frames > 0, return -ENXIO); | |
136 | plugin = plugin->next; | |
137 | err = snd_pcm_plugin_alloc(plugin, frames); | |
138 | if (err < 0) | |
139 | return err; | |
140 | } | |
141 | } else { | |
6ac77bc1 | 142 | struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug); |
1da177e4 LT |
143 | while (plugin->prev) { |
144 | if (plugin->src_frames) | |
145 | frames = plugin->src_frames(plugin, frames); | |
146 | snd_assert(frames > 0, return -ENXIO); | |
147 | plugin = plugin->prev; | |
148 | err = snd_pcm_plugin_alloc(plugin, frames); | |
149 | if (err < 0) | |
150 | return err; | |
151 | } | |
152 | } | |
153 | return 0; | |
154 | } | |
155 | ||
156 | ||
6ac77bc1 | 157 | snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, |
1da177e4 | 158 | snd_pcm_uframes_t frames, |
6ac77bc1 | 159 | struct snd_pcm_plugin_channel **channels) |
1da177e4 LT |
160 | { |
161 | *channels = plugin->buf_channels; | |
162 | return frames; | |
163 | } | |
164 | ||
6ac77bc1 | 165 | int snd_pcm_plugin_build(struct snd_pcm_substream *plug, |
1da177e4 | 166 | const char *name, |
6ac77bc1 TI |
167 | struct snd_pcm_plugin_format *src_format, |
168 | struct snd_pcm_plugin_format *dst_format, | |
1da177e4 | 169 | size_t extra, |
6ac77bc1 | 170 | struct snd_pcm_plugin **ret) |
1da177e4 | 171 | { |
6ac77bc1 | 172 | struct snd_pcm_plugin *plugin; |
1da177e4 LT |
173 | unsigned int channels; |
174 | ||
175 | snd_assert(plug != NULL, return -ENXIO); | |
176 | snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); | |
ca2c0966 | 177 | plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL); |
1da177e4 LT |
178 | if (plugin == NULL) |
179 | return -ENOMEM; | |
180 | plugin->name = name; | |
181 | plugin->plug = plug; | |
182 | plugin->stream = snd_pcm_plug_stream(plug); | |
183 | plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | |
184 | plugin->src_format = *src_format; | |
185 | plugin->src_width = snd_pcm_format_physical_width(src_format->format); | |
186 | snd_assert(plugin->src_width > 0, ); | |
187 | plugin->dst_format = *dst_format; | |
188 | plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); | |
189 | snd_assert(plugin->dst_width > 0, ); | |
190 | if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
191 | channels = src_format->channels; | |
192 | else | |
193 | channels = dst_format->channels; | |
194 | plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL); | |
195 | if (plugin->buf_channels == NULL) { | |
196 | snd_pcm_plugin_free(plugin); | |
197 | return -ENOMEM; | |
198 | } | |
47eaebfd | 199 | plugin->src_vmask = bitmap_alloc(src_format->channels); |
1da177e4 LT |
200 | if (plugin->src_vmask == NULL) { |
201 | snd_pcm_plugin_free(plugin); | |
202 | return -ENOMEM; | |
203 | } | |
47eaebfd | 204 | plugin->dst_vmask = bitmap_alloc(dst_format->channels); |
1da177e4 LT |
205 | if (plugin->dst_vmask == NULL) { |
206 | snd_pcm_plugin_free(plugin); | |
207 | return -ENOMEM; | |
208 | } | |
209 | plugin->client_channels = snd_pcm_plugin_client_channels; | |
210 | plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; | |
211 | plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; | |
212 | *ret = plugin; | |
213 | return 0; | |
214 | } | |
215 | ||
6ac77bc1 | 216 | int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin) |
1da177e4 LT |
217 | { |
218 | if (! plugin) | |
219 | return 0; | |
220 | if (plugin->private_free) | |
221 | plugin->private_free(plugin); | |
222 | kfree(plugin->buf_channels); | |
223 | vfree(plugin->buf); | |
224 | kfree(plugin->src_vmask); | |
225 | kfree(plugin->dst_vmask); | |
226 | kfree(plugin); | |
227 | return 0; | |
228 | } | |
229 | ||
6ac77bc1 | 230 | snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames) |
1da177e4 | 231 | { |
6ac77bc1 | 232 | struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; |
1da177e4 LT |
233 | int stream = snd_pcm_plug_stream(plug); |
234 | ||
235 | snd_assert(plug != NULL, return -ENXIO); | |
236 | if (drv_frames == 0) | |
237 | return 0; | |
238 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
239 | plugin = snd_pcm_plug_last(plug); | |
240 | while (plugin && drv_frames > 0) { | |
241 | plugin_prev = plugin->prev; | |
242 | if (plugin->src_frames) | |
243 | drv_frames = plugin->src_frames(plugin, drv_frames); | |
244 | plugin = plugin_prev; | |
245 | } | |
246 | } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | |
247 | plugin = snd_pcm_plug_first(plug); | |
248 | while (plugin && drv_frames > 0) { | |
249 | plugin_next = plugin->next; | |
250 | if (plugin->dst_frames) | |
251 | drv_frames = plugin->dst_frames(plugin, drv_frames); | |
252 | plugin = plugin_next; | |
253 | } | |
254 | } else | |
255 | snd_BUG(); | |
256 | return drv_frames; | |
257 | } | |
258 | ||
6ac77bc1 | 259 | snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames) |
1da177e4 | 260 | { |
6ac77bc1 | 261 | struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next; |
1da177e4 LT |
262 | snd_pcm_sframes_t frames; |
263 | int stream = snd_pcm_plug_stream(plug); | |
264 | ||
265 | snd_assert(plug != NULL, return -ENXIO); | |
266 | if (clt_frames == 0) | |
267 | return 0; | |
268 | frames = clt_frames; | |
269 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
270 | plugin = snd_pcm_plug_first(plug); | |
271 | while (plugin && frames > 0) { | |
272 | plugin_next = plugin->next; | |
273 | if (plugin->dst_frames) { | |
274 | frames = plugin->dst_frames(plugin, frames); | |
275 | if (frames < 0) | |
276 | return frames; | |
277 | } | |
278 | plugin = plugin_next; | |
279 | } | |
280 | } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { | |
281 | plugin = snd_pcm_plug_last(plug); | |
282 | while (plugin) { | |
283 | plugin_prev = plugin->prev; | |
284 | if (plugin->src_frames) { | |
285 | frames = plugin->src_frames(plugin, frames); | |
286 | if (frames < 0) | |
287 | return frames; | |
288 | } | |
289 | plugin = plugin_prev; | |
290 | } | |
291 | } else | |
292 | snd_BUG(); | |
293 | return frames; | |
294 | } | |
295 | ||
6ac77bc1 | 296 | static int snd_pcm_plug_formats(struct snd_mask *mask, int format) |
1da177e4 | 297 | { |
6ac77bc1 | 298 | struct snd_mask formats = *mask; |
1da177e4 LT |
299 | u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | |
300 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | | |
301 | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | | |
302 | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | | |
303 | SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | | |
304 | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | | |
305 | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); | |
306 | snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); | |
307 | ||
308 | if (formats.bits[0] & (u32)linfmts) | |
309 | formats.bits[0] |= (u32)linfmts; | |
310 | if (formats.bits[1] & (u32)(linfmts >> 32)) | |
311 | formats.bits[1] |= (u32)(linfmts >> 32); | |
312 | return snd_mask_test(&formats, format); | |
313 | } | |
314 | ||
315 | static int preferred_formats[] = { | |
316 | SNDRV_PCM_FORMAT_S16_LE, | |
317 | SNDRV_PCM_FORMAT_S16_BE, | |
318 | SNDRV_PCM_FORMAT_U16_LE, | |
319 | SNDRV_PCM_FORMAT_U16_BE, | |
320 | SNDRV_PCM_FORMAT_S24_LE, | |
321 | SNDRV_PCM_FORMAT_S24_BE, | |
322 | SNDRV_PCM_FORMAT_U24_LE, | |
323 | SNDRV_PCM_FORMAT_U24_BE, | |
324 | SNDRV_PCM_FORMAT_S32_LE, | |
325 | SNDRV_PCM_FORMAT_S32_BE, | |
326 | SNDRV_PCM_FORMAT_U32_LE, | |
327 | SNDRV_PCM_FORMAT_U32_BE, | |
328 | SNDRV_PCM_FORMAT_S8, | |
329 | SNDRV_PCM_FORMAT_U8 | |
330 | }; | |
331 | ||
6ac77bc1 | 332 | int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) |
1da177e4 LT |
333 | { |
334 | if (snd_mask_test(format_mask, format)) | |
335 | return format; | |
336 | if (! snd_pcm_plug_formats(format_mask, format)) | |
337 | return -EINVAL; | |
338 | if (snd_pcm_format_linear(format)) { | |
339 | int width = snd_pcm_format_width(format); | |
340 | int unsignd = snd_pcm_format_unsigned(format); | |
341 | int big = snd_pcm_format_big_endian(format); | |
342 | int format1; | |
343 | int wid, width1=width; | |
344 | int dwidth1 = 8; | |
345 | for (wid = 0; wid < 4; ++wid) { | |
346 | int end, big1 = big; | |
347 | for (end = 0; end < 2; ++end) { | |
348 | int sgn, unsignd1 = unsignd; | |
349 | for (sgn = 0; sgn < 2; ++sgn) { | |
350 | format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); | |
351 | if (format1 >= 0 && | |
352 | snd_mask_test(format_mask, format1)) | |
353 | goto _found; | |
354 | unsignd1 = !unsignd1; | |
355 | } | |
356 | big1 = !big1; | |
357 | } | |
358 | if (width1 == 32) { | |
359 | dwidth1 = -dwidth1; | |
360 | width1 = width; | |
361 | } | |
362 | width1 += dwidth1; | |
363 | } | |
364 | return -EINVAL; | |
365 | _found: | |
366 | return format1; | |
367 | } else { | |
368 | unsigned int i; | |
369 | switch (format) { | |
370 | case SNDRV_PCM_FORMAT_MU_LAW: | |
371 | for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { | |
372 | int format1 = preferred_formats[i]; | |
373 | if (snd_mask_test(format_mask, format1)) | |
374 | return format1; | |
375 | } | |
376 | default: | |
377 | return -EINVAL; | |
378 | } | |
379 | } | |
380 | } | |
381 | ||
6ac77bc1 TI |
382 | int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, |
383 | struct snd_pcm_hw_params *params, | |
384 | struct snd_pcm_hw_params *slave_params) | |
1da177e4 | 385 | { |
6ac77bc1 TI |
386 | struct snd_pcm_plugin_format tmpformat; |
387 | struct snd_pcm_plugin_format dstformat; | |
388 | struct snd_pcm_plugin_format srcformat; | |
1da177e4 | 389 | int src_access, dst_access; |
6ac77bc1 | 390 | struct snd_pcm_plugin *plugin = NULL; |
1da177e4 LT |
391 | int err; |
392 | int stream = snd_pcm_plug_stream(plug); | |
393 | int slave_interleaved = (params_channels(slave_params) == 1 || | |
394 | params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED); | |
395 | ||
396 | switch (stream) { | |
397 | case SNDRV_PCM_STREAM_PLAYBACK: | |
398 | dstformat.format = params_format(slave_params); | |
399 | dstformat.rate = params_rate(slave_params); | |
400 | dstformat.channels = params_channels(slave_params); | |
401 | srcformat.format = params_format(params); | |
402 | srcformat.rate = params_rate(params); | |
403 | srcformat.channels = params_channels(params); | |
404 | src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | |
405 | dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | |
406 | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | |
407 | break; | |
408 | case SNDRV_PCM_STREAM_CAPTURE: | |
409 | dstformat.format = params_format(params); | |
410 | dstformat.rate = params_rate(params); | |
411 | dstformat.channels = params_channels(params); | |
412 | srcformat.format = params_format(slave_params); | |
413 | srcformat.rate = params_rate(slave_params); | |
414 | srcformat.channels = params_channels(slave_params); | |
415 | src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED : | |
416 | SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); | |
417 | dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; | |
418 | break; | |
419 | default: | |
420 | snd_BUG(); | |
421 | return -EINVAL; | |
422 | } | |
423 | tmpformat = srcformat; | |
424 | ||
425 | pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", | |
426 | srcformat.format, | |
427 | srcformat.rate, | |
428 | srcformat.channels); | |
429 | pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", | |
430 | dstformat.format, | |
431 | dstformat.rate, | |
432 | dstformat.channels); | |
433 | ||
434 | /* Format change (linearization) */ | |
435 | if ((srcformat.format != dstformat.format || | |
436 | !rate_match(srcformat.rate, dstformat.rate) || | |
437 | srcformat.channels != dstformat.channels) && | |
438 | !snd_pcm_format_linear(srcformat.format)) { | |
439 | if (snd_pcm_format_linear(dstformat.format)) | |
440 | tmpformat.format = dstformat.format; | |
441 | else | |
442 | tmpformat.format = SNDRV_PCM_FORMAT_S16; | |
443 | switch (srcformat.format) { | |
444 | case SNDRV_PCM_FORMAT_MU_LAW: | |
445 | err = snd_pcm_plugin_build_mulaw(plug, | |
446 | &srcformat, &tmpformat, | |
447 | &plugin); | |
448 | break; | |
449 | default: | |
450 | return -EINVAL; | |
451 | } | |
452 | pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | |
453 | if (err < 0) | |
454 | return err; | |
455 | err = snd_pcm_plugin_append(plugin); | |
456 | if (err < 0) { | |
457 | snd_pcm_plugin_free(plugin); | |
458 | return err; | |
459 | } | |
460 | srcformat = tmpformat; | |
461 | src_access = dst_access; | |
462 | } | |
463 | ||
464 | /* channels reduction */ | |
465 | if (srcformat.channels > dstformat.channels) { | |
466 | int sv = srcformat.channels; | |
467 | int dv = dstformat.channels; | |
6ac77bc1 | 468 | int *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); |
1da177e4 LT |
469 | if (ttable == NULL) |
470 | return -ENOMEM; | |
471 | #if 1 | |
472 | if (sv == 2 && dv == 1) { | |
473 | ttable[0] = HALF; | |
474 | ttable[1] = HALF; | |
475 | } else | |
476 | #endif | |
477 | { | |
478 | int v; | |
479 | for (v = 0; v < dv; ++v) | |
480 | ttable[v * sv + v] = FULL; | |
481 | } | |
482 | tmpformat.channels = dstformat.channels; | |
483 | if (rate_match(srcformat.rate, dstformat.rate) && | |
484 | snd_pcm_format_linear(dstformat.format)) | |
485 | tmpformat.format = dstformat.format; | |
486 | err = snd_pcm_plugin_build_route(plug, | |
487 | &srcformat, &tmpformat, | |
488 | ttable, &plugin); | |
489 | kfree(ttable); | |
490 | pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | |
491 | if (err < 0) { | |
492 | snd_pcm_plugin_free(plugin); | |
493 | return err; | |
494 | } | |
495 | err = snd_pcm_plugin_append(plugin); | |
496 | if (err < 0) { | |
497 | snd_pcm_plugin_free(plugin); | |
498 | return err; | |
499 | } | |
500 | srcformat = tmpformat; | |
501 | src_access = dst_access; | |
502 | } | |
503 | ||
504 | /* rate resampling */ | |
505 | if (!rate_match(srcformat.rate, dstformat.rate)) { | |
506 | tmpformat.rate = dstformat.rate; | |
507 | if (srcformat.channels == dstformat.channels && | |
508 | snd_pcm_format_linear(dstformat.format)) | |
509 | tmpformat.format = dstformat.format; | |
510 | err = snd_pcm_plugin_build_rate(plug, | |
511 | &srcformat, &tmpformat, | |
512 | &plugin); | |
513 | pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err); | |
514 | if (err < 0) { | |
515 | snd_pcm_plugin_free(plugin); | |
516 | return err; | |
517 | } | |
518 | err = snd_pcm_plugin_append(plugin); | |
519 | if (err < 0) { | |
520 | snd_pcm_plugin_free(plugin); | |
521 | return err; | |
522 | } | |
523 | srcformat = tmpformat; | |
524 | src_access = dst_access; | |
525 | } | |
526 | ||
527 | /* channels extension */ | |
528 | if (srcformat.channels < dstformat.channels) { | |
529 | int sv = srcformat.channels; | |
530 | int dv = dstformat.channels; | |
6ac77bc1 | 531 | int *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); |
1da177e4 LT |
532 | if (ttable == NULL) |
533 | return -ENOMEM; | |
534 | #if 0 | |
535 | { | |
536 | int v; | |
537 | for (v = 0; v < sv; ++v) | |
538 | ttable[v * sv + v] = FULL; | |
539 | } | |
540 | #else | |
541 | { | |
542 | /* Playback is spreaded on all channels */ | |
543 | int vd, vs; | |
544 | for (vd = 0, vs = 0; vd < dv; ++vd) { | |
545 | ttable[vd * sv + vs] = FULL; | |
546 | vs++; | |
547 | if (vs == sv) | |
548 | vs = 0; | |
549 | } | |
550 | } | |
551 | #endif | |
552 | tmpformat.channels = dstformat.channels; | |
553 | if (snd_pcm_format_linear(dstformat.format)) | |
554 | tmpformat.format = dstformat.format; | |
555 | err = snd_pcm_plugin_build_route(plug, | |
556 | &srcformat, &tmpformat, | |
557 | ttable, &plugin); | |
558 | kfree(ttable); | |
559 | pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err); | |
560 | if (err < 0) { | |
561 | snd_pcm_plugin_free(plugin); | |
562 | return err; | |
563 | } | |
564 | err = snd_pcm_plugin_append(plugin); | |
565 | if (err < 0) { | |
566 | snd_pcm_plugin_free(plugin); | |
567 | return err; | |
568 | } | |
569 | srcformat = tmpformat; | |
570 | src_access = dst_access; | |
571 | } | |
572 | ||
573 | /* format change */ | |
574 | if (srcformat.format != dstformat.format) { | |
575 | tmpformat.format = dstformat.format; | |
576 | if (tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) { | |
577 | err = snd_pcm_plugin_build_mulaw(plug, | |
578 | &srcformat, &tmpformat, | |
579 | &plugin); | |
580 | } | |
581 | else if (snd_pcm_format_linear(srcformat.format) && | |
582 | snd_pcm_format_linear(tmpformat.format)) { | |
583 | err = snd_pcm_plugin_build_linear(plug, | |
584 | &srcformat, &tmpformat, | |
585 | &plugin); | |
586 | } | |
587 | else | |
588 | return -EINVAL; | |
589 | pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err); | |
590 | if (err < 0) | |
591 | return err; | |
592 | err = snd_pcm_plugin_append(plugin); | |
593 | if (err < 0) { | |
594 | snd_pcm_plugin_free(plugin); | |
595 | return err; | |
596 | } | |
597 | srcformat = tmpformat; | |
598 | src_access = dst_access; | |
599 | } | |
600 | ||
601 | /* de-interleave */ | |
602 | if (src_access != dst_access) { | |
603 | err = snd_pcm_plugin_build_copy(plug, | |
604 | &srcformat, | |
605 | &tmpformat, | |
606 | &plugin); | |
607 | pdprintf("interleave change (copy: returns %i)\n", err); | |
608 | if (err < 0) | |
609 | return err; | |
610 | err = snd_pcm_plugin_append(plugin); | |
611 | if (err < 0) { | |
612 | snd_pcm_plugin_free(plugin); | |
613 | return err; | |
614 | } | |
615 | } | |
616 | ||
617 | return 0; | |
618 | } | |
619 | ||
6ac77bc1 | 620 | snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug, |
1da177e4 LT |
621 | char *buf, |
622 | snd_pcm_uframes_t count, | |
6ac77bc1 | 623 | struct snd_pcm_plugin_channel **channels) |
1da177e4 | 624 | { |
6ac77bc1 TI |
625 | struct snd_pcm_plugin *plugin; |
626 | struct snd_pcm_plugin_channel *v; | |
627 | struct snd_pcm_plugin_format *format; | |
1da177e4 LT |
628 | int width, nchannels, channel; |
629 | int stream = snd_pcm_plug_stream(plug); | |
630 | ||
631 | snd_assert(buf != NULL, return -ENXIO); | |
632 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
633 | plugin = snd_pcm_plug_first(plug); | |
634 | format = &plugin->src_format; | |
635 | } else { | |
636 | plugin = snd_pcm_plug_last(plug); | |
637 | format = &plugin->dst_format; | |
638 | } | |
639 | v = plugin->buf_channels; | |
640 | *channels = v; | |
641 | if ((width = snd_pcm_format_physical_width(format->format)) < 0) | |
642 | return width; | |
643 | nchannels = format->channels; | |
644 | snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); | |
645 | for (channel = 0; channel < nchannels; channel++, v++) { | |
646 | v->frames = count; | |
647 | v->enabled = 1; | |
648 | v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE); | |
649 | v->area.addr = buf; | |
650 | v->area.first = channel * width; | |
651 | v->area.step = nchannels * width; | |
652 | } | |
653 | return count; | |
654 | } | |
655 | ||
6ac77bc1 | 656 | static int snd_pcm_plug_playback_channels_mask(struct snd_pcm_substream *plug, |
47eaebfd | 657 | unsigned long *client_vmask) |
1da177e4 | 658 | { |
6ac77bc1 | 659 | struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug); |
1da177e4 LT |
660 | if (plugin == NULL) { |
661 | return 0; | |
662 | } else { | |
663 | int schannels = plugin->dst_format.channels; | |
47eaebfd TI |
664 | DECLARE_BITMAP(bs, schannels); |
665 | unsigned long *srcmask; | |
666 | unsigned long *dstmask = bs; | |
1da177e4 | 667 | int err; |
47eaebfd | 668 | bitmap_fill(dstmask, schannels); |
94f19c9a | 669 | |
1da177e4 LT |
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 | } | |
47eaebfd | 679 | bitmap_and(client_vmask, client_vmask, dstmask, plugin->src_format.channels); |
1da177e4 LT |
680 | return 0; |
681 | } | |
682 | } | |
683 | ||
6ac77bc1 TI |
684 | static int snd_pcm_plug_playback_disable_useless_channels(struct snd_pcm_substream *plug, |
685 | struct snd_pcm_plugin_channel *src_channels) | |
1da177e4 | 686 | { |
6ac77bc1 | 687 | struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug); |
1da177e4 | 688 | unsigned int nchannels = plugin->src_format.channels; |
47eaebfd TI |
689 | DECLARE_BITMAP(bs, nchannels); |
690 | unsigned long *srcmask = bs; | |
1da177e4 LT |
691 | int err; |
692 | unsigned int channel; | |
693 | for (channel = 0; channel < nchannels; channel++) { | |
694 | if (src_channels[channel].enabled) | |
47eaebfd | 695 | set_bit(channel, srcmask); |
1da177e4 | 696 | else |
47eaebfd | 697 | clear_bit(channel, srcmask); |
1da177e4 LT |
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++) { | |
47eaebfd | 703 | if (!test_bit(channel, srcmask)) |
1da177e4 LT |
704 | src_channels[channel].enabled = 0; |
705 | } | |
706 | return 0; | |
707 | } | |
708 | ||
6ac77bc1 TI |
709 | static int snd_pcm_plug_capture_disable_useless_channels(struct snd_pcm_substream *plug, |
710 | struct snd_pcm_plugin_channel *src_channels, | |
711 | struct snd_pcm_plugin_channel *client_channels) | |
1da177e4 | 712 | { |
6ac77bc1 | 713 | struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug); |
1da177e4 | 714 | unsigned int nchannels = plugin->dst_format.channels; |
47eaebfd TI |
715 | DECLARE_BITMAP(bs, nchannels); |
716 | unsigned long *dstmask = bs; | |
717 | unsigned long *srcmask; | |
1da177e4 LT |
718 | int err; |
719 | unsigned int channel; | |
720 | for (channel = 0; channel < nchannels; channel++) { | |
721 | if (client_channels[channel].enabled) | |
47eaebfd | 722 | set_bit(channel, dstmask); |
1da177e4 | 723 | else |
47eaebfd | 724 | clear_bit(channel, dstmask); |
1da177e4 LT |
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++) { | |
47eaebfd | 736 | if (!test_bit(channel, dstmask)) |
1da177e4 LT |
737 | src_channels[channel].enabled = 0; |
738 | } | |
739 | return 0; | |
740 | } | |
741 | ||
6ac77bc1 | 742 | snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *src_channels, snd_pcm_uframes_t size) |
1da177e4 | 743 | { |
6ac77bc1 TI |
744 | struct snd_pcm_plugin *plugin, *next; |
745 | struct snd_pcm_plugin_channel *dst_channels; | |
1da177e4 LT |
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 | ||
6ac77bc1 | 777 | snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size) |
1da177e4 | 778 | { |
6ac77bc1 TI |
779 | struct snd_pcm_plugin *plugin, *next; |
780 | struct snd_pcm_plugin_channel *src_channels, *dst_channels; | |
1da177e4 LT |
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 | ||
6ac77bc1 | 812 | int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, |
1da177e4 LT |
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 | ||
6ac77bc1 TI |
858 | int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, |
859 | const struct snd_pcm_channel_area *dst_area, size_t dst_offset, | |
1da177e4 LT |
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 | } | |
21a3479a JK |
922 | |
923 | #endif |