1 /**
2 * \file pcm/pcm_multi.c
3 * \ingroup PCM_Plugins
4 * \brief PCM Multi Streams to One Conversion Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \date 2000-2001
7 */
8 /*
9 * PCM - Multi
10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11 *
12 *
13 * This library is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as
15 * published by the Free Software Foundation; either version 2.1 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 *
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <math.h>
34 #include "pcm_local.h"
35 #include "pcm_generic.h"
36
37 #ifndef PIC
38 /* entry for static linking */
39 const char *_snd_module_pcm_multi = "";
40 #endif
41
42 #ifndef DOC_HIDDEN
43
44 typedef struct {
45 snd_pcm_t *pcm;
46 unsigned int channels_count;
47 int close_slave;
48 snd_pcm_t *linked;
49 } snd_pcm_multi_slave_t;
50
51 typedef struct {
52 int slave_idx;
53 unsigned int slave_channel;
54 } snd_pcm_multi_channel_t;
55
56 typedef struct {
57 snd_pcm_uframes_t appl_ptr, hw_ptr;
58 unsigned int slaves_count;
59 unsigned int master_slave;
60 snd_pcm_multi_slave_t *slaves;
61 unsigned int channels_count;
62 snd_pcm_multi_channel_t *channels;
63 } snd_pcm_multi_t;
64
65 #endif
66
snd_pcm_multi_close(snd_pcm_t * pcm)67 static int snd_pcm_multi_close(snd_pcm_t *pcm)
68 {
69 snd_pcm_multi_t *multi = pcm->private_data;
70 unsigned int i;
71 int ret = 0;
72 for (i = 0; i < multi->slaves_count; ++i) {
73 snd_pcm_multi_slave_t *slave = &multi->slaves[i];
74 if (slave->close_slave) {
75 int err = snd_pcm_close(slave->pcm);
76 if (err < 0)
77 ret = err;
78 }
79 }
80 free(multi->slaves);
81 free(multi->channels);
82 free(multi);
83 return ret;
84 }
85
snd_pcm_multi_nonblock(snd_pcm_t * pcm ATTRIBUTE_UNUSED,int nonblock ATTRIBUTE_UNUSED)86 static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
87 {
88 return 0;
89 }
90
snd_pcm_multi_async(snd_pcm_t * pcm,int sig,pid_t pid)91 static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
92 {
93 snd_pcm_multi_t *multi = pcm->private_data;
94 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
95 return snd_pcm_async(slave_0, sig, pid);
96 }
97
snd_pcm_multi_poll_descriptors_count(snd_pcm_t * pcm)98 static int snd_pcm_multi_poll_descriptors_count(snd_pcm_t *pcm)
99 {
100 snd_pcm_multi_t *multi = pcm->private_data;
101 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
102 return snd_pcm_poll_descriptors_count(slave_0);
103 }
104
snd_pcm_multi_poll_descriptors(snd_pcm_t * pcm,struct pollfd * pfds,unsigned int space)105 static int snd_pcm_multi_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
106 {
107 snd_pcm_multi_t *multi = pcm->private_data;
108 snd_pcm_t *slave;
109 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
110 int err;
111 unsigned int i;
112
113 for (i = 0; i < multi->slaves_count; ++i) {
114 slave = multi->slaves[i].pcm;
115 if (slave == slave_0)
116 continue;
117 err = snd_pcm_poll_descriptors(slave, pfds, space);
118 if (err < 0)
119 return err;
120 }
121 /* finally overwrite with master's pfds */
122 return snd_pcm_poll_descriptors(slave_0, pfds, space);
123 }
124
snd_pcm_multi_poll_revents(snd_pcm_t * pcm,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)125 static int snd_pcm_multi_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
126 {
127 snd_pcm_multi_t *multi = pcm->private_data;
128 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
129 return snd_pcm_poll_descriptors_revents(slave_0, pfds, nfds, revents);
130 }
131
snd_pcm_multi_info(snd_pcm_t * pcm,snd_pcm_info_t * info)132 static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
133 {
134 snd_pcm_multi_t *multi = pcm->private_data;
135 int err, n;
136 assert(info->subdevice < multi->slaves_count);
137 n = info->subdevice;
138 info->subdevice = 0;
139 err = snd_pcm_info(multi->slaves[n].pcm, info);
140 if (err < 0)
141 return err;
142 info->subdevices_count = multi->slaves_count;
143 return 0;
144 }
145
snd_pcm_multi_hw_refine_cprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)146 static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
147 {
148 snd_pcm_multi_t *multi = pcm->private_data;
149 snd_pcm_access_mask_t access_mask;
150 int err;
151 snd_pcm_access_mask_any(&access_mask);
152 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
153 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
154 &access_mask);
155 if (err < 0)
156 return err;
157 err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
158 multi->channels_count, 0);
159 if (err < 0)
160 return err;
161 params->info = ~0U;
162 return 0;
163 }
164
snd_pcm_multi_hw_refine_sprepare(snd_pcm_t * pcm,unsigned int slave_idx,snd_pcm_hw_params_t * sparams)165 static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx,
166 snd_pcm_hw_params_t *sparams)
167 {
168 snd_pcm_multi_t *multi = pcm->private_data;
169 snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
170 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
171 _snd_pcm_hw_params_any(sparams);
172 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
173 &saccess_mask);
174 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
175 slave->channels_count, 0);
176 return 0;
177 }
178
snd_pcm_multi_hw_refine_schange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,unsigned int slave_idx ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)179 static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
180 unsigned int slave_idx ATTRIBUTE_UNUSED,
181 snd_pcm_hw_params_t *params,
182 snd_pcm_hw_params_t *sparams)
183 {
184 int err;
185 unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
186 SND_PCM_HW_PARBIT_SUBFORMAT |
187 SND_PCM_HW_PARBIT_RATE |
188 SND_PCM_HW_PARBIT_PERIOD_SIZE |
189 SND_PCM_HW_PARBIT_PERIOD_TIME |
190 SND_PCM_HW_PARBIT_PERIODS |
191 SND_PCM_HW_PARBIT_BUFFER_SIZE |
192 SND_PCM_HW_PARBIT_BUFFER_TIME |
193 SND_PCM_HW_PARBIT_TICK_TIME);
194 const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
195 if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
196 !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
197 !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
198 snd_pcm_access_mask_t saccess_mask;
199 snd_pcm_access_mask_any(&saccess_mask);
200 snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
201 err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
202 &saccess_mask);
203 if (err < 0)
204 return err;
205 }
206 err = _snd_pcm_hw_params_refine(sparams, links, params);
207 if (err < 0)
208 return err;
209 return 0;
210 }
211
snd_pcm_multi_hw_refine_cchange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,unsigned int slave_idx ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)212 static int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
213 unsigned int slave_idx ATTRIBUTE_UNUSED,
214 snd_pcm_hw_params_t *params,
215 snd_pcm_hw_params_t *sparams)
216 {
217 int err;
218 unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
219 SND_PCM_HW_PARBIT_SUBFORMAT |
220 SND_PCM_HW_PARBIT_RATE |
221 SND_PCM_HW_PARBIT_PERIOD_SIZE |
222 SND_PCM_HW_PARBIT_PERIOD_TIME |
223 SND_PCM_HW_PARBIT_PERIODS |
224 SND_PCM_HW_PARBIT_BUFFER_SIZE |
225 SND_PCM_HW_PARBIT_BUFFER_TIME |
226 SND_PCM_HW_PARBIT_TICK_TIME);
227 snd_pcm_access_mask_t access_mask;
228 const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
229 snd_pcm_access_mask_any(&access_mask);
230 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
231 if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
232 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
233 if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
234 !snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
235 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
236 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
237 &access_mask);
238 if (err < 0)
239 return err;
240 err = _snd_pcm_hw_params_refine(params, links, sparams);
241 if (err < 0)
242 return err;
243 params->info &= sparams->info;
244 return 0;
245 }
246
snd_pcm_multi_hw_refine_slave(snd_pcm_t * pcm,unsigned int slave_idx,snd_pcm_hw_params_t * sparams)247 static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
248 unsigned int slave_idx,
249 snd_pcm_hw_params_t *sparams)
250 {
251 snd_pcm_multi_t *multi = pcm->private_data;
252 snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
253 return snd_pcm_hw_refine(slave, sparams);
254 }
255
snd_pcm_multi_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)256 static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
257 {
258 snd_pcm_multi_t *multi = pcm->private_data;
259 unsigned int k;
260 snd_pcm_hw_params_t sparams[multi->slaves_count];
261 int err;
262 unsigned int cmask, changed;
263 err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
264 if (err < 0)
265 return err;
266 for (k = 0; k < multi->slaves_count; ++k) {
267 err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
268 if (err < 0) {
269 SNDERR("Slave PCM #%d not usable", k);
270 return err;
271 }
272 }
273 do {
274 cmask = params->cmask;
275 params->cmask = 0;
276 for (k = 0; k < multi->slaves_count; ++k) {
277 err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
278 if (err >= 0)
279 err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
280 if (err < 0) {
281 snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
282 return err;
283 }
284 err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
285 if (err < 0)
286 return err;
287 }
288 err = snd_pcm_hw_refine_soft(pcm, params);
289 changed = params->cmask;
290 params->cmask |= cmask;
291 if (err < 0)
292 return err;
293 } while (changed);
294 return 0;
295 }
296
snd_pcm_multi_hw_params_slave(snd_pcm_t * pcm,unsigned int slave_idx,snd_pcm_hw_params_t * sparams)297 static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
298 unsigned int slave_idx,
299 snd_pcm_hw_params_t *sparams)
300 {
301 snd_pcm_multi_t *multi = pcm->private_data;
302 snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
303 int err = snd_pcm_hw_params(slave, sparams);
304 if (err < 0)
305 return err;
306 err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
307 if (err < 0)
308 return err;
309 if (slave->stopped_areas) {
310 err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
311 if (err < 0)
312 return err;
313 }
314 return 0;
315 }
316
317 /* reset links to the normal state
318 * slave #0 = trigger master
319 * slave #1-(N-1) = trigger slaves, linked is set to #0
320 */
reset_links(snd_pcm_multi_t * multi)321 static void reset_links(snd_pcm_multi_t *multi)
322 {
323 unsigned int i;
324
325 for (i = 0; i < multi->slaves_count; ++i) {
326 if (multi->slaves[i].linked)
327 snd_pcm_unlink(multi->slaves[i].linked);
328 multi->slaves[0].linked = NULL;
329 if (! i)
330 continue;
331 if (snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm) >= 0)
332 multi->slaves[i].linked = multi->slaves[0].pcm;
333 }
334 }
335
snd_pcm_multi_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)336 static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
337 {
338 snd_pcm_multi_t *multi = pcm->private_data;
339 unsigned int i;
340 snd_pcm_hw_params_t sparams[multi->slaves_count];
341 int err;
342 for (i = 0; i < multi->slaves_count; ++i) {
343 err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]);
344 assert(err >= 0);
345 err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]);
346 assert(err >= 0);
347 err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]);
348 if (err < 0) {
349 snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]);
350 return err;
351 }
352 }
353 reset_links(multi);
354 return 0;
355 }
356
snd_pcm_multi_hw_free(snd_pcm_t * pcm)357 static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
358 {
359 snd_pcm_multi_t *multi = pcm->private_data;
360 unsigned int i;
361 int err = 0;
362 for (i = 0; i < multi->slaves_count; ++i) {
363 snd_pcm_t *slave = multi->slaves[i].pcm;
364 int e = snd_pcm_hw_free(slave);
365 if (e < 0)
366 err = e;
367 if (!multi->slaves[i].linked)
368 continue;
369 e = snd_pcm_unlink(slave);
370 if (e < 0)
371 err = e;
372 multi->slaves[i].linked = NULL;
373 }
374 return err;
375 }
376
snd_pcm_multi_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * params)377 static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
378 {
379 snd_pcm_multi_t *multi = pcm->private_data;
380 unsigned int i;
381 int err;
382 for (i = 0; i < multi->slaves_count; ++i) {
383 snd_pcm_t *slave = multi->slaves[i].pcm;
384 err = snd_pcm_sw_params(slave, params);
385 if (err < 0)
386 return err;
387 }
388 return 0;
389 }
390
snd_pcm_multi_status(snd_pcm_t * pcm,snd_pcm_status_t * status)391 static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
392 {
393 snd_pcm_multi_t *multi = pcm->private_data;
394 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
395 return snd_pcm_status(slave, status);
396 }
397
snd_pcm_multi_state(snd_pcm_t * pcm)398 static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
399 {
400 snd_pcm_multi_t *multi = pcm->private_data;
401 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
402 return snd_pcm_state(slave);
403 }
404
snd_pcm_multi_hwptr_update(snd_pcm_t * pcm)405 static void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm)
406 {
407 snd_pcm_multi_t *multi = pcm->private_data;
408 snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail;
409 unsigned int i;
410 /* the logic is really simple, choose the lowest hw_ptr from slaves */
411 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
412 last_avail = 0;
413 for (i = 0; i < multi->slaves_count; ++i) {
414 slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
415 avail = __snd_pcm_playback_avail(pcm, multi->hw_ptr, slave_hw_ptr);
416 if (avail > last_avail) {
417 hw_ptr = slave_hw_ptr;
418 last_avail = avail;
419 }
420 }
421 } else {
422 last_avail = LONG_MAX;
423 for (i = 0; i < multi->slaves_count; ++i) {
424 slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
425 avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr);
426 if (avail < last_avail) {
427 hw_ptr = slave_hw_ptr;
428 last_avail = avail;
429 }
430 }
431 }
432 multi->hw_ptr = hw_ptr;
433 }
434
snd_pcm_multi_hwsync(snd_pcm_t * pcm)435 static int snd_pcm_multi_hwsync(snd_pcm_t *pcm)
436 {
437 snd_pcm_multi_t *multi = pcm->private_data;
438 unsigned int i;
439 int err;
440 for (i = 0; i < multi->slaves_count; ++i) {
441 err = snd_pcm_hwsync(multi->slaves[i].pcm);
442 if (err < 0)
443 return err;
444 }
445 snd_pcm_multi_hwptr_update(pcm);
446 return 0;
447 }
448
snd_pcm_multi_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)449 static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
450 {
451 snd_pcm_multi_t *multi = pcm->private_data;
452 snd_pcm_sframes_t d, dr = 0;
453 unsigned int i;
454 int err;
455 for (i = 0; i < multi->slaves_count; ++i) {
456 err = snd_pcm_delay(multi->slaves[i].pcm, &d);
457 if (err < 0)
458 return err;
459 if (dr < d)
460 dr = d;
461 }
462 *delayp = dr;
463 return 0;
464 }
465
snd_pcm_multi_avail_update(snd_pcm_t * pcm)466 static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
467 {
468 snd_pcm_multi_t *multi = pcm->private_data;
469 snd_pcm_sframes_t ret = LONG_MAX;
470 unsigned int i;
471 for (i = 0; i < multi->slaves_count; ++i) {
472 snd_pcm_sframes_t avail;
473 avail = snd_pcm_avail_update(multi->slaves[i].pcm);
474 if (avail < 0)
475 return avail;
476 if (ret > avail)
477 ret = avail;
478 }
479 snd_pcm_multi_hwptr_update(pcm);
480 return ret;
481 }
482
snd_pcm_multi_htimestamp(snd_pcm_t * pcm,snd_pcm_uframes_t * avail,snd_htimestamp_t * tstamp)483 static int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
484 snd_htimestamp_t *tstamp)
485 {
486 snd_pcm_multi_t *multi = pcm->private_data;
487 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
488 return snd_pcm_htimestamp(slave, avail, tstamp);
489 }
490
snd_pcm_multi_prepare(snd_pcm_t * pcm)491 static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
492 {
493 snd_pcm_multi_t *multi = pcm->private_data;
494 int result = 0, err;
495 unsigned int i;
496 for (i = 0; i < multi->slaves_count; ++i) {
497 /* We call prepare to each slave even if it's linked.
498 * This is to make sure to sync non-mmaped control/status.
499 */
500 err = snd_pcm_prepare(multi->slaves[i].pcm);
501 if (err < 0)
502 result = err;
503 }
504 multi->hw_ptr = multi->appl_ptr = 0;
505 return result;
506 }
507
snd_pcm_multi_reset(snd_pcm_t * pcm)508 static int snd_pcm_multi_reset(snd_pcm_t *pcm)
509 {
510 snd_pcm_multi_t *multi = pcm->private_data;
511 int result = 0, err;
512 unsigned int i;
513 for (i = 0; i < multi->slaves_count; ++i) {
514 /* Reset each slave, as well as in prepare */
515 err = snd_pcm_reset(multi->slaves[i].pcm);
516 if (err < 0)
517 result = err;
518 }
519 multi->hw_ptr = multi->appl_ptr = 0;
520 return result;
521 }
522
523 /* when the first slave PCM is linked, it means that the whole multi
524 * plugin instance is linked manually to another PCM. in this case,
525 * we need to trigger the master.
526 */
snd_pcm_multi_start(snd_pcm_t * pcm)527 static int snd_pcm_multi_start(snd_pcm_t *pcm)
528 {
529 snd_pcm_multi_t *multi = pcm->private_data;
530 int err = 0;
531 unsigned int i;
532 if (multi->slaves[0].linked)
533 return snd_pcm_start(multi->slaves[0].linked);
534 for (i = 0; i < multi->slaves_count; ++i) {
535 if (multi->slaves[i].linked)
536 continue;
537 err = snd_pcm_start(multi->slaves[i].pcm);
538 if (err < 0)
539 return err;
540 }
541 return err;
542 }
543
snd_pcm_multi_drop(snd_pcm_t * pcm)544 static int snd_pcm_multi_drop(snd_pcm_t *pcm)
545 {
546 snd_pcm_multi_t *multi = pcm->private_data;
547 int err = 0;
548 unsigned int i;
549 if (multi->slaves[0].linked)
550 return snd_pcm_drop(multi->slaves[0].linked);
551 for (i = 0; i < multi->slaves_count; ++i) {
552 if (multi->slaves[i].linked)
553 continue;
554 err = snd_pcm_drop(multi->slaves[i].pcm);
555 if (err < 0)
556 return err;
557 }
558 return err;
559 }
560
snd_pcm_multi_drain(snd_pcm_t * pcm)561 static int snd_pcm_multi_drain(snd_pcm_t *pcm)
562 {
563 snd_pcm_multi_t *multi = pcm->private_data;
564 int err = 0;
565 unsigned int i;
566 if (multi->slaves[0].linked)
567 return snd_pcm_drain(multi->slaves[0].linked);
568 for (i = 0; i < multi->slaves_count; ++i) {
569 if (multi->slaves[i].linked)
570 continue;
571 err = snd_pcm_drain(multi->slaves[i].pcm);
572 if (err < 0)
573 return err;
574 }
575 return err;
576 }
577
snd_pcm_multi_pause(snd_pcm_t * pcm,int enable)578 static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
579 {
580 snd_pcm_multi_t *multi = pcm->private_data;
581 int err = 0;
582 unsigned int i;
583 if (multi->slaves[0].linked)
584 return snd_pcm_pause(multi->slaves[0].linked, enable);
585 for (i = 0; i < multi->slaves_count; ++i) {
586 if (multi->slaves[i].linked)
587 continue;
588 err = snd_pcm_pause(multi->slaves[i].pcm, enable);
589 if (err < 0)
590 return err;
591 }
592 return err;
593 }
594
snd_pcm_multi_channel_info(snd_pcm_t * pcm,snd_pcm_channel_info_t * info)595 static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
596 {
597 snd_pcm_multi_t *multi = pcm->private_data;
598 unsigned int channel = info->channel;
599 snd_pcm_multi_channel_t *c = &multi->channels[channel];
600 int err;
601 if (c->slave_idx < 0)
602 return -ENXIO;
603 info->channel = c->slave_channel;
604 err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
605 info->channel = channel;
606 return err;
607 }
608
snd_pcm_multi_rewindable(snd_pcm_t * pcm)609 static snd_pcm_sframes_t snd_pcm_multi_rewindable(snd_pcm_t *pcm)
610 {
611 snd_pcm_multi_t *multi = pcm->private_data;
612 unsigned int i;
613 snd_pcm_sframes_t frames = LONG_MAX;
614
615 for (i = 0; i < multi->slaves_count; ++i) {
616 snd_pcm_sframes_t f = snd_pcm_rewindable(multi->slaves[i].pcm);
617 if (f <= 0)
618 return f;
619 if (f < frames)
620 frames = f;
621 }
622
623 return frames;
624
625 }
626
snd_pcm_multi_forwardable(snd_pcm_t * pcm)627 static snd_pcm_sframes_t snd_pcm_multi_forwardable(snd_pcm_t *pcm)
628 {
629 snd_pcm_multi_t *multi = pcm->private_data;
630 unsigned int i;
631 snd_pcm_sframes_t frames = LONG_MAX;
632
633 for (i = 0; i < multi->slaves_count; ++i) {
634 snd_pcm_sframes_t f = snd_pcm_forwardable(multi->slaves[i].pcm);
635 if (f <= 0)
636 return f;
637 if (f < frames)
638 frames = f;
639 }
640
641 return frames;
642
643 }
644
snd_pcm_multi_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)645 static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
646 {
647 snd_pcm_multi_t *multi = pcm->private_data;
648 unsigned int i;
649 snd_pcm_uframes_t pos[multi->slaves_count];
650 memset(pos, 0, sizeof(pos));
651 for (i = 0; i < multi->slaves_count; ++i) {
652 snd_pcm_t *slave_i = multi->slaves[i].pcm;
653 snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames);
654 if (f < 0)
655 return f;
656 pos[i] = f;
657 frames = f;
658 }
659 /* Realign the pointers */
660 for (i = 0; i < multi->slaves_count; ++i) {
661 snd_pcm_t *slave_i = multi->slaves[i].pcm;
662 snd_pcm_uframes_t f = pos[i] - frames;
663 snd_pcm_sframes_t result;
664 if (f > 0) {
665 result = INTERNAL(snd_pcm_forward)(slave_i, f);
666 if (result < 0)
667 return result;
668 if ((snd_pcm_uframes_t)result != f)
669 return -EIO;
670 }
671 }
672 snd_pcm_mmap_appl_backward(pcm, frames);
673 return frames;
674 }
675
snd_pcm_multi_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)676 static snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
677 {
678 snd_pcm_multi_t *multi = pcm->private_data;
679 unsigned int i;
680 snd_pcm_uframes_t pos[multi->slaves_count];
681 memset(pos, 0, sizeof(pos));
682 for (i = 0; i < multi->slaves_count; ++i) {
683 snd_pcm_t *slave_i = multi->slaves[i].pcm;
684 snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames);
685 if (f < 0)
686 return f;
687 pos[i] = f;
688 frames = f;
689 }
690 /* Realign the pointers */
691 for (i = 0; i < multi->slaves_count; ++i) {
692 snd_pcm_t *slave_i = multi->slaves[i].pcm;
693 snd_pcm_uframes_t f = pos[i] - frames;
694 snd_pcm_sframes_t result;
695 if (f > 0) {
696 result = snd_pcm_rewind(slave_i, f);
697 if (result < 0)
698 return result;
699 if ((snd_pcm_uframes_t)result != f)
700 return -EIO;
701 }
702 }
703 snd_pcm_mmap_appl_forward(pcm, frames);
704 return frames;
705 }
706
snd_pcm_multi_resume(snd_pcm_t * pcm)707 static int snd_pcm_multi_resume(snd_pcm_t *pcm)
708 {
709 snd_pcm_multi_t *multi = pcm->private_data;
710 int err = 0;
711 unsigned int i;
712 if (multi->slaves[0].linked)
713 return snd_pcm_resume(multi->slaves[0].linked);
714 for (i = 0; i < multi->slaves_count; ++i) {
715 if (multi->slaves[i].linked)
716 continue;
717 err = snd_pcm_resume(multi->slaves[i].pcm);
718 if (err < 0)
719 return err;
720 }
721 return err;
722 }
723
724 /* if a multi plugin instance is linked as slaves, every slave PCMs
725 * including the first one has to be relinked to the given master.
726 */
snd_pcm_multi_link_slaves(snd_pcm_t * pcm,snd_pcm_t * master)727 static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
728 {
729 snd_pcm_multi_t *multi = pcm->private_data;
730 unsigned int i;
731 int err;
732
733 for (i = 0; i < multi->slaves_count; ++i) {
734 snd_pcm_unlink(multi->slaves[i].pcm);
735 multi->slaves[i].linked = NULL;
736 err = snd_pcm_link(master, multi->slaves[i].pcm);
737 if (err < 0) {
738 reset_links(multi);
739 return err;
740 }
741 multi->slaves[i].linked = master;
742 }
743 return 0;
744 }
745
746 /* linking to a multi as a master is easy - simply link to the first
747 * slave element as its own slaves are already linked.
748 */
snd_pcm_multi_link(snd_pcm_t * pcm1,snd_pcm_t * pcm2)749 static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
750 {
751 snd_pcm_multi_t *multi = pcm1->private_data;
752 if (multi->slaves[0].pcm->fast_ops->link)
753 return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2);
754 return -ENOSYS;
755 }
756
snd_pcm_multi_unlink(snd_pcm_t * pcm)757 static int snd_pcm_multi_unlink(snd_pcm_t *pcm)
758 {
759 snd_pcm_multi_t *multi = pcm->private_data;
760 unsigned int i;
761
762 for (i = 0; i < multi->slaves_count; ++i) {
763 if (multi->slaves[i].linked)
764 snd_pcm_unlink(multi->slaves[i].linked);
765 multi->slaves[0].linked = NULL;
766 }
767 return 0;
768 }
769
snd_pcm_multi_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset,snd_pcm_uframes_t size)770 static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
771 snd_pcm_uframes_t offset,
772 snd_pcm_uframes_t size)
773 {
774 snd_pcm_multi_t *multi = pcm->private_data;
775 snd_pcm_t *slave;
776 unsigned int i;
777 snd_pcm_sframes_t result;
778
779 for (i = 0; i < multi->slaves_count; ++i) {
780 slave = multi->slaves[i].pcm;
781 result = snd_pcm_mmap_commit(slave, offset, size);
782 if (result < 0)
783 return result;
784 if ((snd_pcm_uframes_t)result != size)
785 return -EIO;
786 }
787 snd_pcm_mmap_appl_forward(pcm, size);
788 return size;
789 }
790
snd_pcm_multi_munmap(snd_pcm_t * pcm)791 static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
792 {
793 free(pcm->mmap_channels);
794 free(pcm->running_areas);
795 pcm->mmap_channels = NULL;
796 pcm->running_areas = NULL;
797 return 0;
798 }
799
snd_pcm_multi_mmap(snd_pcm_t * pcm)800 static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
801 {
802 snd_pcm_multi_t *multi = pcm->private_data;
803 unsigned int c;
804
805 pcm->mmap_channels = calloc(pcm->channels,
806 sizeof(pcm->mmap_channels[0]));
807 pcm->running_areas = calloc(pcm->channels,
808 sizeof(pcm->running_areas[0]));
809 if (!pcm->mmap_channels || !pcm->running_areas) {
810 snd_pcm_multi_munmap(pcm);
811 return -ENOMEM;
812 }
813
814 /* Copy the slave mmapped buffer data */
815 for (c = 0; c < pcm->channels; c++) {
816 snd_pcm_multi_channel_t *chan = &multi->channels[c];
817 snd_pcm_t *slave;
818 if (chan->slave_idx < 0) {
819 snd_pcm_multi_munmap(pcm);
820 return -ENXIO;
821 }
822 slave = multi->slaves[chan->slave_idx].pcm;
823 pcm->mmap_channels[c] =
824 slave->mmap_channels[chan->slave_channel];
825 pcm->mmap_channels[c].channel = c;
826 pcm->running_areas[c] =
827 slave->running_areas[chan->slave_channel];
828 }
829 return 0;
830 }
831
snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t * pcm,snd_pcm_uframes_t avail)832 static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
833 {
834 snd_pcm_multi_t *multi = pcm->private_data;
835 unsigned int i;
836 for (i = 0; i < multi->slaves_count; ++i) {
837 if (snd_pcm_may_wait_for_avail_min(multi->slaves[i].pcm, avail))
838 return 1;
839 }
840 return 0;
841 }
842
snd_pcm_multi_query_chmaps(snd_pcm_t * pcm)843 static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm)
844 {
845 snd_pcm_multi_t *multi = pcm->private_data;
846 snd_pcm_chmap_query_t **slave_maps[multi->slaves_count];
847 snd_pcm_chmap_query_t **maps;
848 unsigned int i;
849 int err = -ENOMEM;
850
851 memset(slave_maps, 0, sizeof(slave_maps));
852 maps = calloc(2, sizeof(*maps));
853 if (!maps)
854 return NULL;
855 maps[0] = calloc(multi->channels_count + 2, sizeof(int *));
856 if (!maps[0])
857 goto error;
858 maps[0]->type = SND_CHMAP_TYPE_FIXED;
859 maps[0]->map.channels = multi->channels_count;
860
861 for (i = 0; i < multi->slaves_count; i++) {
862 slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm);
863 if (!slave_maps[i])
864 goto error;
865 }
866
867 for (i = 0; i < multi->channels_count; i++) {
868 snd_pcm_multi_channel_t *bind = &multi->channels[i];
869 unsigned int slave_channels =
870 multi->slaves[bind->slave_idx].channels_count;
871 snd_pcm_chmap_query_t **p;
872
873 for (p = slave_maps[bind->slave_idx]; *p; p++) {
874 if ((*p)->map.channels == slave_channels) {
875 maps[0]->map.pos[i] =
876 (*p)->map.pos[bind->slave_channel];
877 break;
878 }
879 }
880 }
881 err = 0;
882
883 error:
884 for (i = 0; i < multi->slaves_count; i++) {
885 if (slave_maps[i])
886 snd_pcm_free_chmaps(slave_maps[i]);
887 }
888
889 if (err) {
890 snd_pcm_free_chmaps(maps);
891 return NULL;
892 }
893
894 return maps;
895 }
896
snd_pcm_multi_get_chmap(snd_pcm_t * pcm)897 static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm)
898 {
899 snd_pcm_multi_t *multi = pcm->private_data;
900 snd_pcm_chmap_t *map;
901 snd_pcm_chmap_t *slave_maps[multi->slaves_count];
902 unsigned int i;
903 int err = -ENOMEM;
904
905 memset(slave_maps, 0, sizeof(slave_maps));
906 map = calloc(multi->channels_count + 1, sizeof(int));
907 if (!map)
908 return NULL;
909
910 for (i = 0; i < multi->slaves_count; i++) {
911 slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm);
912 if (!slave_maps[i])
913 goto error;
914 }
915
916 map->channels = multi->channels_count;
917 for (i = 0; i < multi->channels_count; i++) {
918 snd_pcm_multi_channel_t *bind = &multi->channels[i];
919 map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel];
920 }
921 err = 0;
922
923 error:
924 for (i = 0; i < multi->slaves_count; i++)
925 free(slave_maps[i]);
926
927 if (err) {
928 free(map);
929 return NULL;
930 }
931
932 return map;
933 }
934
snd_pcm_multi_set_chmap(snd_pcm_t * pcm,const snd_pcm_chmap_t * map)935 static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
936 {
937 snd_pcm_multi_t *multi = pcm->private_data;
938 snd_pcm_chmap_t *slave_maps[multi->slaves_count];
939 unsigned int i;
940 int err = 0;
941
942 if (map->channels != multi->channels_count)
943 return -EINVAL;
944
945 for (i = 0; i < multi->slaves_count; i++) {
946 slave_maps[i] = calloc(multi->slaves[i].channels_count + 1,
947 sizeof(int));
948 if (!slave_maps[i]) {
949 for (i++; i < multi->slaves_count; i++)
950 slave_maps[i] = NULL;
951 err = -ENOMEM;
952 goto error;
953 }
954 }
955
956 for (i = 0; i < multi->channels_count; i++) {
957 snd_pcm_multi_channel_t *bind = &multi->channels[i];
958 slave_maps[bind->slave_idx]->pos[bind->slave_channel] =
959 map->pos[i];
960 }
961
962 for (i = 0; i < multi->slaves_count; i++) {
963 err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]);
964 if (err < 0)
965 goto error;
966 }
967
968 error:
969 for (i = 0; i < multi->slaves_count; i++)
970 free(slave_maps[i]);
971
972 return err;
973 }
974
snd_pcm_multi_dump(snd_pcm_t * pcm,snd_output_t * out)975 static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
976 {
977 snd_pcm_multi_t *multi = pcm->private_data;
978 unsigned int k;
979 snd_output_printf(out, "Multi PCM\n");
980 snd_output_printf(out, " Channel bindings:\n");
981 for (k = 0; k < multi->channels_count; ++k) {
982 snd_pcm_multi_channel_t *c = &multi->channels[k];
983 if (c->slave_idx < 0)
984 continue;
985 snd_output_printf(out, " %d: slave %d, channel %d\n",
986 k, c->slave_idx, c->slave_channel);
987 }
988 if (pcm->setup) {
989 snd_output_printf(out, "Its setup is:\n");
990 snd_pcm_dump_setup(pcm, out);
991 }
992 for (k = 0; k < multi->slaves_count; ++k) {
993 snd_output_printf(out, "Slave #%d: ", k);
994 snd_pcm_dump(multi->slaves[k].pcm, out);
995 }
996 }
997
998 static const snd_pcm_ops_t snd_pcm_multi_ops = {
999 .close = snd_pcm_multi_close,
1000 .info = snd_pcm_multi_info,
1001 .hw_refine = snd_pcm_multi_hw_refine,
1002 .hw_params = snd_pcm_multi_hw_params,
1003 .hw_free = snd_pcm_multi_hw_free,
1004 .sw_params = snd_pcm_multi_sw_params,
1005 .channel_info = snd_pcm_multi_channel_info,
1006 .dump = snd_pcm_multi_dump,
1007 .nonblock = snd_pcm_multi_nonblock,
1008 .async = snd_pcm_multi_async,
1009 .mmap = snd_pcm_multi_mmap,
1010 .munmap = snd_pcm_multi_munmap,
1011 .query_chmaps = snd_pcm_multi_query_chmaps,
1012 .get_chmap = snd_pcm_multi_get_chmap,
1013 .set_chmap = snd_pcm_multi_set_chmap,
1014 };
1015
1016 static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
1017 .status = snd_pcm_multi_status,
1018 .state = snd_pcm_multi_state,
1019 .hwsync = snd_pcm_multi_hwsync,
1020 .delay = snd_pcm_multi_delay,
1021 .prepare = snd_pcm_multi_prepare,
1022 .reset = snd_pcm_multi_reset,
1023 .start = snd_pcm_multi_start,
1024 .drop = snd_pcm_multi_drop,
1025 .drain = snd_pcm_multi_drain,
1026 .pause = snd_pcm_multi_pause,
1027 .writei = snd_pcm_mmap_writei,
1028 .writen = snd_pcm_mmap_writen,
1029 .readi = snd_pcm_mmap_readi,
1030 .readn = snd_pcm_mmap_readn,
1031 .rewindable = snd_pcm_multi_rewindable,
1032 .rewind = snd_pcm_multi_rewind,
1033 .forwardable = snd_pcm_multi_forwardable,
1034 .forward = snd_pcm_multi_forward,
1035 .resume = snd_pcm_multi_resume,
1036 .link = snd_pcm_multi_link,
1037 .link_slaves = snd_pcm_multi_link_slaves,
1038 .unlink = snd_pcm_multi_unlink,
1039 .avail_update = snd_pcm_multi_avail_update,
1040 .mmap_commit = snd_pcm_multi_mmap_commit,
1041 .htimestamp = snd_pcm_multi_htimestamp,
1042 .poll_descriptors_count = snd_pcm_multi_poll_descriptors_count,
1043 .poll_descriptors = snd_pcm_multi_poll_descriptors,
1044 .poll_revents = snd_pcm_multi_poll_revents,
1045 .may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min,
1046 };
1047
1048 /**
1049 * \brief Creates a new Multi PCM
1050 * \param pcmp Returns created PCM handle
1051 * \param name Name of PCM
1052 * \param slaves_count Count of slaves
1053 * \param master_slave Master slave number
1054 * \param slaves_pcm Array with slave PCMs
1055 * \param schannels_count Array with slave channel counts
1056 * \param channels_count Count of channels
1057 * \param sidxs Array with channels indexes to slaves
1058 * \param schannels Array with slave channels
1059 * \param close_slaves When set, the slave PCM handle is closed
1060 * \retval zero on success otherwise a negative error code
1061 * \warning Using of this function might be dangerous in the sense
1062 * of compatibility reasons. The prototype might be freely
1063 * changed in future.
1064 */
snd_pcm_multi_open(snd_pcm_t ** pcmp,const char * name,unsigned int slaves_count,unsigned int master_slave,snd_pcm_t ** slaves_pcm,unsigned int * schannels_count,unsigned int channels_count,int * sidxs,unsigned int * schannels,int close_slaves)1065 int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
1066 unsigned int slaves_count, unsigned int master_slave,
1067 snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
1068 unsigned int channels_count,
1069 int *sidxs, unsigned int *schannels,
1070 int close_slaves)
1071 {
1072 snd_pcm_t *pcm;
1073 snd_pcm_multi_t *multi;
1074 unsigned int i;
1075 snd_pcm_stream_t stream;
1076 int err;
1077
1078 assert(pcmp);
1079 assert(slaves_count > 0 && slaves_pcm && schannels_count);
1080 assert(channels_count > 0 && sidxs && schannels);
1081 assert(master_slave < slaves_count);
1082
1083 multi = calloc(1, sizeof(snd_pcm_multi_t));
1084 if (!multi) {
1085 return -ENOMEM;
1086 }
1087
1088 stream = slaves_pcm[0]->stream;
1089
1090 multi->slaves_count = slaves_count;
1091 multi->master_slave = master_slave;
1092 multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
1093 if (!multi->slaves) {
1094 free(multi);
1095 return -ENOMEM;
1096 }
1097 multi->channels_count = channels_count;
1098 multi->channels = calloc(channels_count, sizeof(*multi->channels));
1099 if (!multi->channels) {
1100 free(multi->slaves);
1101 free(multi);
1102 return -ENOMEM;
1103 }
1104 for (i = 0; i < slaves_count; ++i) {
1105 snd_pcm_multi_slave_t *slave = &multi->slaves[i];
1106 assert(slaves_pcm[i]->stream == stream);
1107 slave->pcm = slaves_pcm[i];
1108 slave->channels_count = schannels_count[i];
1109 slave->close_slave = close_slaves;
1110 }
1111 for (i = 0; i < channels_count; ++i) {
1112 snd_pcm_multi_channel_t *bind = &multi->channels[i];
1113 assert(sidxs[i] < (int)slaves_count);
1114 assert(schannels[i] < schannels_count[sidxs[i]]);
1115 bind->slave_idx = sidxs[i];
1116 bind->slave_channel = schannels[i];
1117 if (sidxs[i] < 0)
1118 continue;
1119 }
1120 multi->channels_count = channels_count;
1121
1122 err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream,
1123 multi->slaves[0].pcm->mode);
1124 if (err < 0) {
1125 free(multi->slaves);
1126 free(multi->channels);
1127 free(multi);
1128 return err;
1129 }
1130 pcm->mmap_rw = 1;
1131 pcm->mmap_shadow = 1; /* has own mmap method */
1132 pcm->ops = &snd_pcm_multi_ops;
1133 pcm->fast_ops = &snd_pcm_multi_fast_ops;
1134 pcm->private_data = multi;
1135 pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd;
1136 pcm->poll_events = multi->slaves[master_slave].pcm->poll_events;
1137 pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type;
1138 snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0);
1139 snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0);
1140 *pcmp = pcm;
1141 return 0;
1142 }
1143
1144 /*! \page pcm_plugins
1145
1146 \section pcm_plugins_multi Plugin: Multiple streams to One
1147
1148 This plugin converts multiple streams to one.
1149
1150 \code
1151 pcm.name {
1152 type multi # Multiple streams conversion PCM
1153 slaves { # Slaves definition
1154 ID STR # Slave PCM name
1155 # or
1156 ID {
1157 pcm STR # Slave PCM name
1158 # or
1159 pcm { } # Slave PCM definition
1160 channels INT # Slave channels
1161 }
1162 }
1163 bindings { # Bindings table
1164 N {
1165 slave STR # Slave key
1166 channel INT # Slave channel
1167 }
1168 }
1169 [master INT] # Define the master slave
1170 }
1171 \endcode
1172
1173 For example, to bind two PCM streams with two-channel stereo (hw:0,0 and
1174 hw:0,1) as one 4-channel stereo PCM stream, define like this:
1175 \code
1176 pcm.quad {
1177 type multi
1178
1179 slaves.a.pcm "hw:0,0"
1180 slaves.a.channels 2
1181 slaves.b.pcm "hw:0,1"
1182 slaves.b.channels 2
1183
1184 bindings.0.slave a
1185 bindings.0.channel 0
1186 bindings.1.slave a
1187 bindings.1.channel 1
1188 bindings.2.slave b
1189 bindings.2.channel 0
1190 bindings.3.slave b
1191 bindings.3.channel 1
1192 }
1193 \endcode
1194 Note that the resultant pcm "quad" is not in the interleaved format
1195 but in the "complex" format. Hence, it's not accessible by applications
1196 which can handle only the interleaved (or the non-interleaved) format.
1197 In such a case, wrap this PCM with \ref pcm_plugins_route "route" or
1198 \ref pcm_plugins_plug "plug" plugin.
1199 \code
1200 pcm.quad2 {
1201 type route
1202 slave.pcm "quad"
1203 ttable.0.0 1
1204 ttable.1.1 1
1205 ttable.2.2 1
1206 ttable.3.3 1
1207 }
1208 \endcode
1209
1210 \subsection pcm_plugins_multi_funcref Function reference
1211
1212 <UL>
1213 <LI>snd_pcm_multi_open()
1214 <LI>_snd_pcm_multi_open()
1215 </UL>
1216
1217 */
1218
1219 /**
1220 * \brief Creates a new Multi PCM
1221 * \param pcmp Returns created PCM handle
1222 * \param name Name of PCM
1223 * \param root Root configuration node
1224 * \param conf Configuration node with Multi PCM description
1225 * \param stream Stream type
1226 * \param mode Stream mode
1227 * \retval zero on success otherwise a negative error code
1228 * \warning Using of this function might be dangerous in the sense
1229 * of compatibility reasons. The prototype might be freely
1230 * changed in future.
1231 */
_snd_pcm_multi_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)1232 int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
1233 snd_config_t *root, snd_config_t *conf,
1234 snd_pcm_stream_t stream, int mode)
1235 {
1236 snd_config_iterator_t i, inext, j, jnext;
1237 snd_config_t *slaves = NULL;
1238 snd_config_t *bindings = NULL;
1239 int err;
1240 unsigned int idx;
1241 const char **slaves_id = NULL;
1242 snd_config_t **slaves_conf = NULL;
1243 snd_pcm_t **slaves_pcm = NULL;
1244 unsigned int *slaves_channels = NULL;
1245 int *channels_sidx = NULL;
1246 unsigned int *channels_schannel = NULL;
1247 unsigned int slaves_count = 0;
1248 long master_slave = 0;
1249 unsigned int channels_count = 0;
1250 snd_config_for_each(i, inext, conf) {
1251 snd_config_t *n = snd_config_iterator_entry(i);
1252 const char *id;
1253 if (snd_config_get_id(n, &id) < 0)
1254 continue;
1255 if (snd_pcm_conf_generic_id(id))
1256 continue;
1257 if (strcmp(id, "slaves") == 0) {
1258 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1259 SNDERR("Invalid type for %s", id);
1260 return -EINVAL;
1261 }
1262 slaves = n;
1263 continue;
1264 }
1265 if (strcmp(id, "bindings") == 0) {
1266 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1267 SNDERR("Invalid type for %s", id);
1268 return -EINVAL;
1269 }
1270 bindings = n;
1271 continue;
1272 }
1273 if (strcmp(id, "master") == 0) {
1274 if (snd_config_get_integer(n, &master_slave) < 0) {
1275 SNDERR("Invalid type for %s", id);
1276 return -EINVAL;
1277 }
1278 continue;
1279 }
1280 SNDERR("Unknown field %s", id);
1281 return -EINVAL;
1282 }
1283 if (!slaves) {
1284 SNDERR("slaves is not defined");
1285 return -EINVAL;
1286 }
1287 if (!bindings) {
1288 SNDERR("bindings is not defined");
1289 return -EINVAL;
1290 }
1291 snd_config_for_each(i, inext, slaves) {
1292 ++slaves_count;
1293 }
1294 if (master_slave < 0 || master_slave >= (long)slaves_count) {
1295 SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1);
1296 return -EINVAL;
1297 }
1298 snd_config_for_each(i, inext, bindings) {
1299 long cchannel;
1300 snd_config_t *m = snd_config_iterator_entry(i);
1301 const char *id;
1302 if (snd_config_get_id(m, &id) < 0)
1303 continue;
1304 err = safe_strtol(id, &cchannel);
1305 if (err < 0 || cchannel < 0) {
1306 SNDERR("Invalid channel number: %s", id);
1307 return -EINVAL;
1308 }
1309 if ((unsigned long)cchannel >= channels_count)
1310 channels_count = cchannel + 1;
1311 }
1312 if (channels_count == 0) {
1313 SNDERR("No channels defined");
1314 return -EINVAL;
1315 }
1316 slaves_id = calloc(slaves_count, sizeof(*slaves_id));
1317 slaves_conf = calloc(slaves_count, sizeof(*slaves_conf));
1318 slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
1319 slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
1320 channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
1321 channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
1322 if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels ||
1323 !channels_sidx || !channels_schannel) {
1324 err = -ENOMEM;
1325 goto _free;
1326 }
1327 for (idx = 0; idx < channels_count; ++idx)
1328 channels_sidx[idx] = -1;
1329 idx = 0;
1330 snd_config_for_each(i, inext, slaves) {
1331 snd_config_t *m = snd_config_iterator_entry(i);
1332 const char *id;
1333 int channels;
1334 if (snd_config_get_id(m, &id) < 0)
1335 continue;
1336 slaves_id[idx] = id;
1337 err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
1338 SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
1339 if (err < 0)
1340 goto _free;
1341 slaves_channels[idx] = channels;
1342 ++idx;
1343 }
1344
1345 snd_config_for_each(i, inext, bindings) {
1346 snd_config_t *m = snd_config_iterator_entry(i);
1347 long cchannel = -1;
1348 long schannel = -1;
1349 int slave = -1;
1350 long val;
1351 const char *str;
1352 const char *id;
1353 if (snd_config_get_id(m, &id) < 0)
1354 continue;
1355 err = safe_strtol(id, &cchannel);
1356 if (err < 0 || cchannel < 0) {
1357 SNDERR("Invalid channel number: %s", id);
1358 err = -EINVAL;
1359 goto _free;
1360 }
1361 snd_config_for_each(j, jnext, m) {
1362 snd_config_t *n = snd_config_iterator_entry(j);
1363 const char *id;
1364 if (snd_config_get_id(n, &id) < 0)
1365 continue;
1366 if (strcmp(id, "comment") == 0)
1367 continue;
1368 if (strcmp(id, "slave") == 0) {
1369 char buf[32];
1370 unsigned int k;
1371 err = snd_config_get_string(n, &str);
1372 if (err < 0) {
1373 err = snd_config_get_integer(n, &val);
1374 if (err < 0) {
1375 SNDERR("Invalid value for %s", id);
1376 goto _free;
1377 }
1378 sprintf(buf, "%ld", val);
1379 str = buf;
1380 }
1381 for (k = 0; k < slaves_count; ++k) {
1382 if (strcmp(slaves_id[k], str) == 0)
1383 slave = k;
1384 }
1385 continue;
1386 }
1387 if (strcmp(id, "channel") == 0) {
1388 err = snd_config_get_integer(n, &schannel);
1389 if (err < 0) {
1390 SNDERR("Invalid type for %s", id);
1391 goto _free;
1392 }
1393 continue;
1394 }
1395 SNDERR("Unknown field %s", id);
1396 err = -EINVAL;
1397 goto _free;
1398 }
1399 if (slave < 0 || (unsigned int)slave >= slaves_count) {
1400 SNDERR("Invalid or missing sidx for channel %s", id);
1401 err = -EINVAL;
1402 goto _free;
1403 }
1404 if (schannel < 0 ||
1405 (unsigned int) schannel >= slaves_channels[slave]) {
1406 SNDERR("Invalid or missing schannel for channel %s", id);
1407 err = -EINVAL;
1408 goto _free;
1409 }
1410 channels_sidx[cchannel] = slave;
1411 channels_schannel[cchannel] = schannel;
1412 }
1413
1414 for (idx = 0; idx < slaves_count; ++idx) {
1415 err = snd_pcm_open_slave(&slaves_pcm[idx], root,
1416 slaves_conf[idx], stream, mode,
1417 conf);
1418 if (err < 0)
1419 goto _free;
1420 snd_config_delete(slaves_conf[idx]);
1421 slaves_conf[idx] = NULL;
1422 }
1423 err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave,
1424 slaves_pcm, slaves_channels,
1425 channels_count,
1426 channels_sidx, channels_schannel,
1427 1);
1428 _free:
1429 if (err < 0) {
1430 for (idx = 0; idx < slaves_count; ++idx) {
1431 if (slaves_pcm[idx])
1432 snd_pcm_close(slaves_pcm[idx]);
1433 }
1434 }
1435 if (slaves_conf) {
1436 for (idx = 0; idx < slaves_count; ++idx) {
1437 if (slaves_conf[idx])
1438 snd_config_delete(slaves_conf[idx]);
1439 }
1440 free(slaves_conf);
1441 }
1442 free(slaves_pcm);
1443 free(slaves_channels);
1444 free(channels_sidx);
1445 free(channels_schannel);
1446 free(slaves_id);
1447 return err;
1448 }
1449 #ifndef DOC_HIDDEN
1450 SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION);
1451 #endif
1452