2 * \file pcm/pcm_multi.c
4 * \brief PCM Multi Streams to One Conversion Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
10 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include "pcm_local.h"
35 #include "pcm_generic.h"
38 /* entry for static linking */
39 const char *_snd_module_pcm_multi = "";
46 unsigned int channels_count;
49 } snd_pcm_multi_slave_t;
53 unsigned int slave_channel;
54 } snd_pcm_multi_channel_t;
57 unsigned int slaves_count;
58 unsigned int master_slave;
59 snd_pcm_multi_slave_t *slaves;
60 unsigned int channels_count;
61 snd_pcm_multi_channel_t *channels;
66 static int snd_pcm_multi_close(snd_pcm_t *pcm)
68 snd_pcm_multi_t *multi = pcm->private_data;
71 for (i = 0; i < multi->slaves_count; ++i) {
72 snd_pcm_multi_slave_t *slave = &multi->slaves[i];
73 if (slave->close_slave) {
74 int err = snd_pcm_close(slave->pcm);
80 free(multi->channels);
85 static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
90 static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
92 snd_pcm_multi_t *multi = pcm->private_data;
93 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
94 return snd_pcm_async(slave_0, sig, pid);
97 static int snd_pcm_multi_poll_descriptors_count(snd_pcm_t *pcm)
99 snd_pcm_multi_t *multi = pcm->private_data;
100 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
101 return snd_pcm_poll_descriptors_count(slave_0);
104 static int snd_pcm_multi_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
106 snd_pcm_multi_t *multi = pcm->private_data;
108 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
112 for (i = 0; i < multi->slaves_count; ++i) {
113 slave = multi->slaves[i].pcm;
114 if (slave == slave_0)
116 err = snd_pcm_poll_descriptors(slave, pfds, space);
120 /* finally overwrite with master's pfds */
121 return snd_pcm_poll_descriptors(slave_0, pfds, space);
124 static int snd_pcm_multi_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
126 snd_pcm_multi_t *multi = pcm->private_data;
127 snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
128 return snd_pcm_poll_descriptors_revents(slave_0, pfds, nfds, revents);
131 static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
133 snd_pcm_multi_t *multi = pcm->private_data;
135 assert(info->subdevice < multi->slaves_count);
138 err = snd_pcm_info(multi->slaves[n].pcm, info);
141 info->subdevices_count = multi->slaves_count;
145 static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
147 snd_pcm_multi_t *multi = pcm->private_data;
148 snd_pcm_access_mask_t access_mask;
150 snd_pcm_access_mask_any(&access_mask);
151 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
152 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
156 err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
157 multi->channels_count, 0);
164 static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx,
165 snd_pcm_hw_params_t *sparams)
167 snd_pcm_multi_t *multi = pcm->private_data;
168 snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
169 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
170 _snd_pcm_hw_params_any(sparams);
171 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
173 _snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
174 slave->channels_count, 0);
178 static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
179 unsigned int slave_idx ATTRIBUTE_UNUSED,
180 snd_pcm_hw_params_t *params,
181 snd_pcm_hw_params_t *sparams)
184 unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
185 SND_PCM_HW_PARBIT_SUBFORMAT |
186 SND_PCM_HW_PARBIT_RATE |
187 SND_PCM_HW_PARBIT_PERIOD_SIZE |
188 SND_PCM_HW_PARBIT_PERIOD_TIME |
189 SND_PCM_HW_PARBIT_PERIODS |
190 SND_PCM_HW_PARBIT_BUFFER_SIZE |
191 SND_PCM_HW_PARBIT_BUFFER_TIME |
192 SND_PCM_HW_PARBIT_TICK_TIME);
193 const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
194 if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
195 !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
196 !snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
197 snd_pcm_access_mask_t saccess_mask;
198 snd_pcm_access_mask_any(&saccess_mask);
199 snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
200 err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
205 err = _snd_pcm_hw_params_refine(sparams, links, params);
211 static int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
212 unsigned int slave_idx ATTRIBUTE_UNUSED,
213 snd_pcm_hw_params_t *params,
214 snd_pcm_hw_params_t *sparams)
217 unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
218 SND_PCM_HW_PARBIT_SUBFORMAT |
219 SND_PCM_HW_PARBIT_RATE |
220 SND_PCM_HW_PARBIT_PERIOD_SIZE |
221 SND_PCM_HW_PARBIT_PERIOD_TIME |
222 SND_PCM_HW_PARBIT_PERIODS |
223 SND_PCM_HW_PARBIT_BUFFER_SIZE |
224 SND_PCM_HW_PARBIT_BUFFER_TIME |
225 SND_PCM_HW_PARBIT_TICK_TIME);
226 snd_pcm_access_mask_t access_mask;
227 const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
228 snd_pcm_access_mask_any(&access_mask);
229 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
230 if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
231 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
232 if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
233 !snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
234 snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
235 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
239 err = _snd_pcm_hw_params_refine(params, links, sparams);
242 params->info &= sparams->info;
246 static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
247 unsigned int slave_idx,
248 snd_pcm_hw_params_t *sparams)
250 snd_pcm_multi_t *multi = pcm->private_data;
251 snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
252 return snd_pcm_hw_refine(slave, sparams);
255 static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
257 snd_pcm_multi_t *multi = pcm->private_data;
259 snd_pcm_hw_params_t sparams[multi->slaves_count];
261 unsigned int cmask, changed;
262 err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
265 for (k = 0; k < multi->slaves_count; ++k) {
266 err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
268 SNDERR("Slave PCM #%d not usable", k);
273 cmask = params->cmask;
275 for (k = 0; k < multi->slaves_count; ++k) {
276 err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
278 err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
280 snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
283 err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
287 err = snd_pcm_hw_refine_soft(pcm, params);
288 changed = params->cmask;
289 params->cmask |= cmask;
296 static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
297 unsigned int slave_idx,
298 snd_pcm_hw_params_t *sparams)
300 snd_pcm_multi_t *multi = pcm->private_data;
301 snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
302 int err = snd_pcm_hw_params(slave, sparams);
305 err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
308 if (slave->stopped_areas) {
309 err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
316 /* reset links to the normal state
317 * slave #0 = trigger master
318 * slave #1-(N-1) = trigger slaves, linked is set to #0
320 static void reset_links(snd_pcm_multi_t *multi)
324 for (i = 0; i < multi->slaves_count; ++i) {
325 if (multi->slaves[i].linked)
326 snd_pcm_unlink(multi->slaves[i].linked);
327 multi->slaves[0].linked = NULL;
330 if (snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm) >= 0)
331 multi->slaves[i].linked = multi->slaves[0].pcm;
335 static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
337 snd_pcm_multi_t *multi = pcm->private_data;
339 snd_pcm_hw_params_t sparams[multi->slaves_count];
341 for (i = 0; i < multi->slaves_count; ++i) {
342 err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]);
344 err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]);
346 err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]);
348 snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]);
356 static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
358 snd_pcm_multi_t *multi = pcm->private_data;
361 for (i = 0; i < multi->slaves_count; ++i) {
362 snd_pcm_t *slave = multi->slaves[i].pcm;
363 int e = snd_pcm_hw_free(slave);
366 if (!multi->slaves[i].linked)
368 e = snd_pcm_unlink(slave);
371 multi->slaves[i].linked = NULL;
376 static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
378 snd_pcm_multi_t *multi = pcm->private_data;
381 for (i = 0; i < multi->slaves_count; ++i) {
382 snd_pcm_t *slave = multi->slaves[i].pcm;
383 err = snd_pcm_sw_params(slave, params);
390 static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
392 snd_pcm_multi_t *multi = pcm->private_data;
393 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
394 return snd_pcm_status(slave, status);
397 static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
399 snd_pcm_multi_t *multi = pcm->private_data;
400 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
401 return snd_pcm_state(slave);
404 static int snd_pcm_multi_hwsync(snd_pcm_t *pcm)
406 snd_pcm_multi_t *multi = pcm->private_data;
407 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
408 return snd_pcm_hwsync(slave);
411 static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
413 snd_pcm_multi_t *multi = pcm->private_data;
414 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
415 return snd_pcm_delay(slave, delayp);
418 static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
420 snd_pcm_multi_t *multi = pcm->private_data;
421 snd_pcm_sframes_t ret = LONG_MAX;
423 for (i = 0; i < multi->slaves_count; ++i) {
424 snd_pcm_sframes_t avail;
425 avail = snd_pcm_avail_update(multi->slaves[i].pcm);
434 static int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
435 snd_htimestamp_t *tstamp)
437 snd_pcm_multi_t *multi = pcm->private_data;
438 snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
439 return snd_pcm_htimestamp(slave, avail, tstamp);
442 static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
444 snd_pcm_multi_t *multi = pcm->private_data;
447 for (i = 0; i < multi->slaves_count; ++i) {
448 /* We call prepare to each slave even if it's linked.
449 * This is to make sure to sync non-mmaped control/status.
451 err = snd_pcm_prepare(multi->slaves[i].pcm);
458 static int snd_pcm_multi_reset(snd_pcm_t *pcm)
460 snd_pcm_multi_t *multi = pcm->private_data;
463 for (i = 0; i < multi->slaves_count; ++i) {
464 /* Reset each slave, as well as in prepare */
465 err = snd_pcm_reset(multi->slaves[i].pcm);
472 /* when the first slave PCM is linked, it means that the whole multi
473 * plugin instance is linked manually to another PCM. in this case,
474 * we need to trigger the master.
476 static int snd_pcm_multi_start(snd_pcm_t *pcm)
478 snd_pcm_multi_t *multi = pcm->private_data;
481 if (multi->slaves[0].linked)
482 return snd_pcm_start(multi->slaves[0].linked);
483 for (i = 0; i < multi->slaves_count; ++i) {
484 if (multi->slaves[i].linked)
486 err = snd_pcm_start(multi->slaves[i].pcm);
493 static int snd_pcm_multi_drop(snd_pcm_t *pcm)
495 snd_pcm_multi_t *multi = pcm->private_data;
498 if (multi->slaves[0].linked)
499 return snd_pcm_drop(multi->slaves[0].linked);
500 for (i = 0; i < multi->slaves_count; ++i) {
501 if (multi->slaves[i].linked)
503 err = snd_pcm_drop(multi->slaves[i].pcm);
510 static int snd_pcm_multi_drain(snd_pcm_t *pcm)
512 snd_pcm_multi_t *multi = pcm->private_data;
515 if (multi->slaves[0].linked)
516 return snd_pcm_drain(multi->slaves[0].linked);
517 for (i = 0; i < multi->slaves_count; ++i) {
518 if (multi->slaves[i].linked)
520 err = snd_pcm_drain(multi->slaves[i].pcm);
527 static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
529 snd_pcm_multi_t *multi = pcm->private_data;
532 if (multi->slaves[0].linked)
533 return snd_pcm_pause(multi->slaves[0].linked, enable);
534 for (i = 0; i < multi->slaves_count; ++i) {
535 if (multi->slaves[i].linked)
537 err = snd_pcm_pause(multi->slaves[i].pcm, enable);
544 static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
546 snd_pcm_multi_t *multi = pcm->private_data;
547 unsigned int channel = info->channel;
548 snd_pcm_multi_channel_t *c = &multi->channels[channel];
550 if (c->slave_idx < 0)
552 info->channel = c->slave_channel;
553 err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
554 info->channel = channel;
558 static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
560 snd_pcm_multi_t *multi = pcm->private_data;
562 snd_pcm_uframes_t pos[multi->slaves_count];
563 memset(pos, 0, sizeof(pos));
564 for (i = 0; i < multi->slaves_count; ++i) {
565 snd_pcm_t *slave_i = multi->slaves[i].pcm;
566 snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames);
572 /* Realign the pointers */
573 for (i = 0; i < multi->slaves_count; ++i) {
574 snd_pcm_t *slave_i = multi->slaves[i].pcm;
575 snd_pcm_uframes_t f = pos[i] - frames;
576 snd_pcm_sframes_t result;
578 result = INTERNAL(snd_pcm_forward)(slave_i, f);
581 if ((snd_pcm_uframes_t)result != f)
588 static snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
590 snd_pcm_multi_t *multi = pcm->private_data;
592 snd_pcm_uframes_t pos[multi->slaves_count];
593 memset(pos, 0, sizeof(pos));
594 for (i = 0; i < multi->slaves_count; ++i) {
595 snd_pcm_t *slave_i = multi->slaves[i].pcm;
596 snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames);
602 /* Realign the pointers */
603 for (i = 0; i < multi->slaves_count; ++i) {
604 snd_pcm_t *slave_i = multi->slaves[i].pcm;
605 snd_pcm_uframes_t f = pos[i] - frames;
606 snd_pcm_sframes_t result;
608 result = snd_pcm_rewind(slave_i, f);
611 if ((snd_pcm_uframes_t)result != f)
618 static int snd_pcm_multi_resume(snd_pcm_t *pcm)
620 snd_pcm_multi_t *multi = pcm->private_data;
623 if (multi->slaves[0].linked)
624 return snd_pcm_resume(multi->slaves[0].linked);
625 for (i = 0; i < multi->slaves_count; ++i) {
626 if (multi->slaves[i].linked)
628 err = snd_pcm_resume(multi->slaves[i].pcm);
635 /* if a multi plugin instance is linked as slaves, every slave PCMs
636 * including the first one has to be relinked to the given master.
638 static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
640 snd_pcm_multi_t *multi = pcm->private_data;
644 for (i = 0; i < multi->slaves_count; ++i) {
645 snd_pcm_unlink(multi->slaves[i].pcm);
646 multi->slaves[i].linked = NULL;
647 err = snd_pcm_link(master, multi->slaves[i].pcm);
652 multi->slaves[i].linked = master;
657 /* linking to a multi as a master is easy - simply link to the first
658 * slave element as its own slaves are already linked.
660 static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
662 snd_pcm_multi_t *multi = pcm1->private_data;
663 if (multi->slaves[0].pcm->fast_ops->link)
664 return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2);
668 static int snd_pcm_multi_unlink(snd_pcm_t *pcm)
670 snd_pcm_multi_t *multi = pcm->private_data;
673 for (i = 0; i < multi->slaves_count; ++i) {
674 if (multi->slaves[i].linked)
675 snd_pcm_unlink(multi->slaves[i].linked);
676 multi->slaves[0].linked = NULL;
681 static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
682 snd_pcm_uframes_t offset,
683 snd_pcm_uframes_t size)
685 snd_pcm_multi_t *multi = pcm->private_data;
688 snd_pcm_sframes_t result;
690 for (i = 0; i < multi->slaves_count; ++i) {
691 slave = multi->slaves[i].pcm;
692 result = snd_pcm_mmap_commit(slave, offset, size);
695 if ((snd_pcm_uframes_t)result != size)
701 static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
703 free(pcm->mmap_channels);
704 free(pcm->running_areas);
705 pcm->mmap_channels = NULL;
706 pcm->running_areas = NULL;
710 static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
712 snd_pcm_multi_t *multi = pcm->private_data;
715 pcm->mmap_channels = calloc(pcm->channels,
716 sizeof(pcm->mmap_channels[0]));
717 pcm->running_areas = calloc(pcm->channels,
718 sizeof(pcm->running_areas[0]));
719 if (!pcm->mmap_channels || !pcm->running_areas) {
720 snd_pcm_multi_munmap(pcm);
724 /* Copy the slave mmapped buffer data */
725 for (c = 0; c < pcm->channels; c++) {
726 snd_pcm_multi_channel_t *chan = &multi->channels[c];
728 if (chan->slave_idx < 0) {
729 snd_pcm_multi_munmap(pcm);
732 slave = multi->slaves[chan->slave_idx].pcm;
733 pcm->mmap_channels[c] =
734 slave->mmap_channels[chan->slave_channel];
735 pcm->mmap_channels[c].channel = c;
736 pcm->running_areas[c] =
737 slave->running_areas[chan->slave_channel];
742 static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
744 snd_pcm_multi_t *multi = pcm->private_data;
746 snd_output_printf(out, "Multi PCM\n");
747 snd_output_printf(out, " Channel bindings:\n");
748 for (k = 0; k < multi->channels_count; ++k) {
749 snd_pcm_multi_channel_t *c = &multi->channels[k];
750 if (c->slave_idx < 0)
752 snd_output_printf(out, " %d: slave %d, channel %d\n",
753 k, c->slave_idx, c->slave_channel);
756 snd_output_printf(out, "Its setup is:\n");
757 snd_pcm_dump_setup(pcm, out);
759 for (k = 0; k < multi->slaves_count; ++k) {
760 snd_output_printf(out, "Slave #%d: ", k);
761 snd_pcm_dump(multi->slaves[k].pcm, out);
765 static const snd_pcm_ops_t snd_pcm_multi_ops = {
766 .close = snd_pcm_multi_close,
767 .info = snd_pcm_multi_info,
768 .hw_refine = snd_pcm_multi_hw_refine,
769 .hw_params = snd_pcm_multi_hw_params,
770 .hw_free = snd_pcm_multi_hw_free,
771 .sw_params = snd_pcm_multi_sw_params,
772 .channel_info = snd_pcm_multi_channel_info,
773 .dump = snd_pcm_multi_dump,
774 .nonblock = snd_pcm_multi_nonblock,
775 .async = snd_pcm_multi_async,
776 .mmap = snd_pcm_multi_mmap,
777 .munmap = snd_pcm_multi_munmap,
780 static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
781 .status = snd_pcm_multi_status,
782 .state = snd_pcm_multi_state,
783 .hwsync = snd_pcm_multi_hwsync,
784 .delay = snd_pcm_multi_delay,
785 .prepare = snd_pcm_multi_prepare,
786 .reset = snd_pcm_multi_reset,
787 .start = snd_pcm_multi_start,
788 .drop = snd_pcm_multi_drop,
789 .drain = snd_pcm_multi_drain,
790 .pause = snd_pcm_multi_pause,
791 .writei = snd_pcm_mmap_writei,
792 .writen = snd_pcm_mmap_writen,
793 .readi = snd_pcm_mmap_readi,
794 .readn = snd_pcm_mmap_readn,
795 .rewind = snd_pcm_multi_rewind,
796 .forward = snd_pcm_multi_forward,
797 .resume = snd_pcm_multi_resume,
798 .link = snd_pcm_multi_link,
799 .link_slaves = snd_pcm_multi_link_slaves,
800 .unlink = snd_pcm_multi_unlink,
801 .avail_update = snd_pcm_multi_avail_update,
802 .mmap_commit = snd_pcm_multi_mmap_commit,
803 .htimestamp = snd_pcm_multi_htimestamp,
804 .poll_descriptors_count = snd_pcm_multi_poll_descriptors_count,
805 .poll_descriptors = snd_pcm_multi_poll_descriptors,
806 .poll_revents = snd_pcm_multi_poll_revents,
810 * \brief Creates a new Multi PCM
811 * \param pcmp Returns created PCM handle
812 * \param name Name of PCM
813 * \param slaves_count Count of slaves
814 * \param master_slave Master slave number
815 * \param slaves_pcm Array with slave PCMs
816 * \param schannels_count Array with slave channel counts
817 * \param channels_count Count of channels
818 * \param sidxs Array with channels indexes to slaves
819 * \param schannels Array with slave channels
820 * \param close_slaves When set, the slave PCM handle is closed
821 * \retval zero on success otherwise a negative error code
822 * \warning Using of this function might be dangerous in the sense
823 * of compatibility reasons. The prototype might be freely
826 int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
827 unsigned int slaves_count, unsigned int master_slave,
828 snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
829 unsigned int channels_count,
830 int *sidxs, unsigned int *schannels,
834 snd_pcm_multi_t *multi;
836 snd_pcm_stream_t stream;
837 char slave_map[64][64] = { { 0 } };
841 assert(slaves_count > 0 && slaves_pcm && schannels_count);
842 assert(channels_count > 0 && sidxs && schannels);
843 assert(master_slave < slaves_count);
845 multi = calloc(1, sizeof(snd_pcm_multi_t));
850 stream = slaves_pcm[0]->stream;
852 multi->slaves_count = slaves_count;
853 multi->master_slave = master_slave;
854 multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
855 if (!multi->slaves) {
859 multi->channels_count = channels_count;
860 multi->channels = calloc(channels_count, sizeof(*multi->channels));
861 if (!multi->channels) {
866 for (i = 0; i < slaves_count; ++i) {
867 snd_pcm_multi_slave_t *slave = &multi->slaves[i];
868 assert(slaves_pcm[i]->stream == stream);
869 slave->pcm = slaves_pcm[i];
870 slave->channels_count = schannels_count[i];
871 slave->close_slave = close_slaves;
873 for (i = 0; i < channels_count; ++i) {
874 snd_pcm_multi_channel_t *bind = &multi->channels[i];
875 assert(sidxs[i] < (int)slaves_count);
876 assert(schannels[i] < schannels_count[sidxs[i]]);
877 bind->slave_idx = sidxs[i];
878 bind->slave_channel = schannels[i];
881 assert(!slave_map[sidxs[i]][schannels[i]]);
882 slave_map[sidxs[i]][schannels[i]] = 1;
884 multi->channels_count = channels_count;
886 err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream,
887 multi->slaves[0].pcm->mode);
890 free(multi->channels);
895 pcm->mmap_shadow = 1; /* has own mmap method */
896 pcm->ops = &snd_pcm_multi_ops;
897 pcm->fast_ops = &snd_pcm_multi_fast_ops;
898 pcm->private_data = multi;
899 pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd;
900 pcm->poll_events = multi->slaves[master_slave].pcm->poll_events;
901 pcm->monotonic = multi->slaves[master_slave].pcm->monotonic;
902 snd_pcm_link_hw_ptr(pcm, multi->slaves[master_slave].pcm);
903 snd_pcm_link_appl_ptr(pcm, multi->slaves[master_slave].pcm);
908 /*! \page pcm_plugins
910 \section pcm_plugins_multi Plugin: Multiple streams to One
912 This plugin converts multiple streams to one.
916 type multi # Multiple streams conversion PCM
917 slaves { # Slaves definition
918 ID STR # Slave PCM name
921 pcm STR # Slave PCM name
923 pcm { } # Slave PCM definition
924 channels INT # Slave channels
927 bindings { # Bindings table
929 slave STR # Slave key
930 channel INT # Slave channel
933 [master INT] # Define the master slave
937 For example, to bind two PCM streams with two-channel stereo (hw:0,0 and
938 hw:0,1) as one 4-channel stereo PCM stream, define like this:
943 slaves.a.pcm "hw:0,0"
945 slaves.b.pcm "hw:0,1"
958 Note that the resultant pcm "quad" is not in the interleaved format
959 but in the "complex" format. Hence, it's not accessible by applications
960 which can handle only the interleaved (or the non-interleaved) format.
961 In such a case, wrap this PCM with \ref pcm_plugins_route "route" or
962 \ref pcm_plugins_plug "plug" plugin.
974 \subsection pcm_plugins_multi_funcref Function reference
977 <LI>snd_pcm_multi_open()
978 <LI>_snd_pcm_multi_open()
984 * \brief Creates a new Multi PCM
985 * \param pcmp Returns created PCM handle
986 * \param name Name of PCM
987 * \param root Root configuration node
988 * \param conf Configuration node with Multi PCM description
989 * \param stream Stream type
990 * \param mode Stream mode
991 * \retval zero on success otherwise a negative error code
992 * \warning Using of this function might be dangerous in the sense
993 * of compatibility reasons. The prototype might be freely
996 int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
997 snd_config_t *root, snd_config_t *conf,
998 snd_pcm_stream_t stream, int mode)
1000 snd_config_iterator_t i, inext, j, jnext;
1001 snd_config_t *slaves = NULL;
1002 snd_config_t *bindings = NULL;
1005 const char **slaves_id = NULL;
1006 snd_config_t **slaves_conf = NULL;
1007 snd_pcm_t **slaves_pcm = NULL;
1008 unsigned int *slaves_channels = NULL;
1009 int *channels_sidx = NULL;
1010 unsigned int *channels_schannel = NULL;
1011 unsigned int slaves_count = 0;
1012 long master_slave = 0;
1013 unsigned int channels_count = 0;
1014 snd_config_for_each(i, inext, conf) {
1015 snd_config_t *n = snd_config_iterator_entry(i);
1017 if (snd_config_get_id(n, &id) < 0)
1019 if (snd_pcm_conf_generic_id(id))
1021 if (strcmp(id, "slaves") == 0) {
1022 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1023 SNDERR("Invalid type for %s", id);
1029 if (strcmp(id, "bindings") == 0) {
1030 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
1031 SNDERR("Invalid type for %s", id);
1037 if (strcmp(id, "master") == 0) {
1038 if (snd_config_get_integer(n, &master_slave) < 0) {
1039 SNDERR("Invalid type for %s", id);
1044 SNDERR("Unknown field %s", id);
1048 SNDERR("slaves is not defined");
1052 SNDERR("bindings is not defined");
1055 snd_config_for_each(i, inext, slaves) {
1058 if (master_slave < 0 || master_slave >= (long)slaves_count) {
1059 SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1);
1062 snd_config_for_each(i, inext, bindings) {
1064 snd_config_t *m = snd_config_iterator_entry(i);
1066 if (snd_config_get_id(m, &id) < 0)
1068 err = safe_strtol(id, &cchannel);
1069 if (err < 0 || cchannel < 0) {
1070 SNDERR("Invalid channel number: %s", id);
1073 if ((unsigned long)cchannel >= channels_count)
1074 channels_count = cchannel + 1;
1076 if (channels_count == 0) {
1077 SNDERR("No channels defined");
1080 slaves_id = calloc(slaves_count, sizeof(*slaves_id));
1081 slaves_conf = calloc(slaves_count, sizeof(*slaves_conf));
1082 slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
1083 slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
1084 channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
1085 channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
1086 if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels ||
1087 !channels_sidx || !channels_schannel) {
1092 for (idx = 0; idx < channels_count; ++idx)
1093 channels_sidx[idx] = -1;
1095 snd_config_for_each(i, inext, slaves) {
1096 snd_config_t *m = snd_config_iterator_entry(i);
1099 if (snd_config_get_id(m, &id) < 0)
1101 slaves_id[idx] = id;
1102 err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
1103 SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
1106 slaves_channels[idx] = channels;
1110 snd_config_for_each(i, inext, bindings) {
1111 snd_config_t *m = snd_config_iterator_entry(i);
1118 if (snd_config_get_id(m, &id) < 0)
1120 err = safe_strtol(id, &cchannel);
1121 if (err < 0 || cchannel < 0) {
1122 SNDERR("Invalid channel number: %s", id);
1126 snd_config_for_each(j, jnext, m) {
1127 snd_config_t *n = snd_config_iterator_entry(j);
1129 if (snd_config_get_id(n, &id) < 0)
1131 if (strcmp(id, "comment") == 0)
1133 if (strcmp(id, "slave") == 0) {
1136 err = snd_config_get_string(n, &str);
1138 err = snd_config_get_integer(n, &val);
1140 SNDERR("Invalid value for %s", id);
1143 sprintf(buf, "%ld", val);
1146 for (k = 0; k < slaves_count; ++k) {
1147 if (strcmp(slaves_id[k], str) == 0)
1152 if (strcmp(id, "channel") == 0) {
1153 err = snd_config_get_integer(n, &schannel);
1155 SNDERR("Invalid type for %s", id);
1160 SNDERR("Unknown field %s", id);
1164 if (slave < 0 || (unsigned int)slave >= slaves_count) {
1165 SNDERR("Invalid or missing sidx for channel %s", id);
1170 (unsigned int) schannel >= slaves_channels[slave]) {
1171 SNDERR("Invalid or missing schannel for channel %s", id);
1175 channels_sidx[cchannel] = slave;
1176 channels_schannel[cchannel] = schannel;
1179 for (idx = 0; idx < slaves_count; ++idx) {
1180 err = snd_pcm_open_slave(&slaves_pcm[idx], root,
1181 slaves_conf[idx], stream, mode,
1185 snd_config_delete(slaves_conf[idx]);
1186 slaves_conf[idx] = NULL;
1188 err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave,
1189 slaves_pcm, slaves_channels,
1191 channels_sidx, channels_schannel,
1195 for (idx = 0; idx < slaves_count; ++idx) {
1196 if (slaves_pcm[idx])
1197 snd_pcm_close(slaves_pcm[idx]);
1201 for (idx = 0; idx < slaves_count; ++idx) {
1202 if (slaves_conf[idx])
1203 snd_config_delete(slaves_conf[idx]);
1208 free(slaves_channels);
1209 free(channels_sidx);
1210 free(channels_schannel);
1215 SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION);