2 * \file pcm/pcm_plugin.c
5 * \author Jaroslav Kysela <perex@perex.cz>
6 * \author Abramo Bagnara <abramo@alsa-project.org>
10 * PCM - Common plugin code
11 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
14 * This library is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License as
16 * published by the Free Software Foundation; either version 2.1 of
17 * the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 \page pcm_plugins PCM (digital audio) plugins
34 PCM plugins extends functionality and features of PCM devices.
35 The plugins take care about various sample conversions, sample
36 copying among channels and so on.
38 \section pcm_plugins_slave Slave definition
40 The slave plugin can be specified directly with a string or the definition
41 can be entered inside a compound configuration node. Some restrictions can
42 be also specified (like static rate or count of channels).
48 pcm { } # PCM definition
49 format STR # Format or "unchanged"
50 channels INT # Count of channels or "unchanged" string
51 rate INT # Rate in Hz or "unchanged" string
52 period_time INT # Period time in us or "unchanged" string
53 buffer_time INT # Buffer time in us or "unchanged" string
60 pcm_slave.slave_rate44100Hz {
67 slave slave_rate44100Hz
71 The equivalent configuration (in one compound):
86 #include "pcm_local.h"
87 #include "pcm_plugin.h"
91 static snd_pcm_sframes_t
92 snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
93 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
94 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
95 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
96 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
101 static snd_pcm_sframes_t
102 snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
103 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
104 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
105 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
106 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
112 snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
113 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
114 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
115 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
116 snd_pcm_uframes_t slave_undo_size)
118 return slave_undo_size;
122 snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
123 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
124 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
125 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
126 snd_pcm_uframes_t slave_undo_size)
128 return slave_undo_size;
131 void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
133 memset(plugin, 0, sizeof(snd_pcm_plugin_t));
134 plugin->undo_read = snd_pcm_plugin_undo_read;
135 plugin->undo_write = snd_pcm_plugin_undo_write;
138 static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
140 snd_pcm_plugin_t *plugin = pcm->private_data;
141 snd_pcm_sframes_t sd;
142 int err = snd_pcm_delay(plugin->gen.slave, &sd);
149 static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
151 snd_pcm_plugin_t *plugin = pcm->private_data;
153 err = snd_pcm_prepare(plugin->gen.slave);
159 err = plugin->init(pcm);
166 static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
168 snd_pcm_plugin_t *plugin = pcm->private_data;
170 err = snd_pcm_reset(plugin->gen.slave);
176 err = plugin->init(pcm);
183 static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
185 return snd_pcm_mmap_hw_rewindable(pcm);
188 snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
190 snd_pcm_plugin_t *plugin = pcm->private_data;
191 snd_pcm_sframes_t n = snd_pcm_plugin_rewindable(pcm);
192 snd_pcm_sframes_t sframes;
194 if ((snd_pcm_uframes_t)n < frames)
200 sframes = snd_pcm_rewind(plugin->gen.slave, sframes);
203 snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes);
204 return (snd_pcm_sframes_t) sframes;
207 static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
209 return snd_pcm_mmap_avail(pcm);
212 snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
214 snd_pcm_plugin_t *plugin = pcm->private_data;
215 snd_pcm_sframes_t n = snd_pcm_plugin_forwardable(pcm);
216 snd_pcm_sframes_t sframes;
218 if ((snd_pcm_uframes_t)n < frames)
224 sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes);
227 snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames);
228 return (snd_pcm_sframes_t) frames;
231 static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm,
232 const snd_pcm_channel_area_t *areas,
233 snd_pcm_uframes_t offset,
234 snd_pcm_uframes_t size)
236 snd_pcm_plugin_t *plugin = pcm->private_data;
237 snd_pcm_t *slave = plugin->gen.slave;
238 snd_pcm_uframes_t xfer = 0;
239 snd_pcm_sframes_t result;
243 snd_pcm_uframes_t frames = size;
244 const snd_pcm_channel_area_t *slave_areas;
245 snd_pcm_uframes_t slave_offset;
246 snd_pcm_uframes_t slave_frames = ULONG_MAX;
248 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
253 if (slave_frames == 0)
255 frames = plugin->write(pcm, areas, offset, frames,
256 slave_areas, slave_offset, &slave_frames);
257 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) {
258 SNDMSG("write overflow %ld > %ld", slave_frames,
259 snd_pcm_mmap_playback_avail(slave));
263 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
264 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
265 snd_pcm_sframes_t res;
266 res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
277 snd_pcm_mmap_appl_forward(pcm, frames);
282 return (snd_pcm_sframes_t)xfer;
285 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
288 static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm,
289 const snd_pcm_channel_area_t *areas,
290 snd_pcm_uframes_t offset,
291 snd_pcm_uframes_t size)
293 snd_pcm_plugin_t *plugin = pcm->private_data;
294 snd_pcm_t *slave = plugin->gen.slave;
295 snd_pcm_uframes_t xfer = 0;
296 snd_pcm_sframes_t result;
300 snd_pcm_uframes_t frames = size;
301 const snd_pcm_channel_area_t *slave_areas;
302 snd_pcm_uframes_t slave_offset;
303 snd_pcm_uframes_t slave_frames = ULONG_MAX;
305 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
310 if (slave_frames == 0)
312 frames = (plugin->read)(pcm, areas, offset, frames,
313 slave_areas, slave_offset, &slave_frames);
314 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) {
315 SNDMSG("read overflow %ld > %ld", slave_frames,
316 snd_pcm_mmap_playback_avail(slave));
320 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
321 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
322 snd_pcm_sframes_t res;
324 res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result);
335 snd_pcm_mmap_appl_forward(pcm, frames);
340 return (snd_pcm_sframes_t)xfer;
343 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
347 static snd_pcm_sframes_t
348 snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
350 snd_pcm_channel_area_t areas[pcm->channels];
351 snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
352 return snd_pcm_write_areas(pcm, areas, 0, size,
353 snd_pcm_plugin_write_areas);
356 static snd_pcm_sframes_t
357 snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
359 snd_pcm_channel_area_t areas[pcm->channels];
360 snd_pcm_areas_from_bufs(pcm, areas, bufs);
361 return snd_pcm_write_areas(pcm, areas, 0, size,
362 snd_pcm_plugin_write_areas);
365 static snd_pcm_sframes_t
366 snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
368 snd_pcm_channel_area_t areas[pcm->channels];
369 snd_pcm_areas_from_buf(pcm, areas, buffer);
370 return snd_pcm_read_areas(pcm, areas, 0, size,
371 snd_pcm_plugin_read_areas);
374 static snd_pcm_sframes_t
375 snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
377 snd_pcm_channel_area_t areas[pcm->channels];
378 snd_pcm_areas_from_bufs(pcm, areas, bufs);
379 return snd_pcm_read_areas(pcm, areas, 0, size,
380 snd_pcm_plugin_read_areas);
383 static snd_pcm_sframes_t
384 snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
385 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
386 snd_pcm_uframes_t size)
388 snd_pcm_plugin_t *plugin = pcm->private_data;
389 snd_pcm_t *slave = plugin->gen.slave;
390 const snd_pcm_channel_area_t *areas;
391 snd_pcm_uframes_t appl_offset;
392 snd_pcm_sframes_t slave_size;
393 snd_pcm_sframes_t xfer;
396 if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
397 snd_pcm_mmap_appl_forward(pcm, size);
400 slave_size = snd_pcm_avail_update(slave);
403 areas = snd_pcm_mmap_areas(pcm);
404 appl_offset = snd_pcm_mmap_offset(pcm);
406 while (size > 0 && slave_size > 0) {
407 snd_pcm_uframes_t frames = size;
408 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
409 const snd_pcm_channel_area_t *slave_areas;
410 snd_pcm_uframes_t slave_offset;
411 snd_pcm_uframes_t slave_frames = ULONG_MAX;
412 snd_pcm_sframes_t result;
414 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
421 frames = plugin->write(pcm, areas, appl_offset, frames,
422 slave_areas, slave_offset, &slave_frames);
423 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
424 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
425 snd_pcm_sframes_t res;
427 res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
438 snd_pcm_mmap_appl_forward(pcm, frames);
442 appl_offset += result;
444 slave_size -= frames;
447 if (CHECK_SANITY(size)) {
448 SNDMSG("short commit: %ld", size);
454 return xfer > 0 ? xfer : err;
457 static snd_pcm_sframes_t
458 snd_pcm_plugin_sync_hw_ptr_capture(snd_pcm_t *pcm,
459 snd_pcm_sframes_t slave_size)
461 snd_pcm_plugin_t *plugin = pcm->private_data;
462 snd_pcm_t *slave = plugin->gen.slave;
463 const snd_pcm_channel_area_t *areas;
464 snd_pcm_uframes_t xfer, hw_offset, size;
467 xfer = snd_pcm_mmap_capture_avail(pcm);
468 size = pcm->buffer_size - xfer;
469 areas = snd_pcm_mmap_areas(pcm);
470 hw_offset = snd_pcm_mmap_hw_offset(pcm);
471 while (size > 0 && slave_size > 0) {
472 snd_pcm_uframes_t frames = size;
473 snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
474 const snd_pcm_channel_area_t *slave_areas;
475 snd_pcm_uframes_t slave_offset;
476 snd_pcm_uframes_t slave_frames = ULONG_MAX;
477 snd_pcm_sframes_t result;
478 /* As mentioned in the ALSA API (see pcm/pcm.c:942):
479 * The function #snd_pcm_avail_update()
480 * have to be called before any mmap begin+commit operation.
481 * Otherwise the snd_pcm_areas_copy will not called a second time.
482 * But this is needed, if the ring buffer wrap is reached and
483 * there is more data available.
485 slave_size = snd_pcm_avail_update(slave);
486 result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
493 frames = (plugin->read)(pcm, areas, hw_offset, frames,
494 slave_areas, slave_offset, &slave_frames);
495 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
496 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
497 snd_pcm_sframes_t res;
498 res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
509 snd_pcm_mmap_hw_forward(pcm, frames);
515 slave_size -= slave_frames;
518 return (snd_pcm_sframes_t)xfer;
520 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
523 static snd_pcm_sframes_t snd_pcm_plugin_sync_hw_ptr(snd_pcm_t *pcm,
524 snd_pcm_uframes_t slave_hw_ptr,
525 snd_pcm_sframes_t slave_size)
527 if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
528 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
529 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
530 return snd_pcm_plugin_sync_hw_ptr_capture(pcm, slave_size);
531 *pcm->hw.ptr = slave_hw_ptr;
535 static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
537 snd_pcm_plugin_t *plugin = pcm->private_data;
538 snd_pcm_t *slave = plugin->gen.slave;
539 snd_pcm_sframes_t slave_size;
541 slave_size = snd_pcm_avail_update(slave);
542 return snd_pcm_plugin_sync_hw_ptr(pcm, *slave->hw.ptr, slave_size);
545 static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
547 snd_pcm_plugin_t *plugin = pcm->private_data;
548 snd_pcm_sframes_t err, diff;
550 err = snd_pcm_status(plugin->gen.slave, status);
553 snd_pcm_plugin_sync_hw_ptr(pcm, status->hw_ptr, status->avail);
555 * For capture stream, the situation is more complicated, because
556 * snd_pcm_plugin_avail_update() commits the data to the slave pcm.
557 * It means that the slave appl_ptr is updated. Calculate diff and
558 * update the delay and avail.
560 * This resolves the data inconsistency for immediate calls:
561 * snd_pcm_avail_update()
564 if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
565 status->appl_ptr = *pcm->appl.ptr;
566 diff = pcm_frame_diff(status->appl_ptr, *pcm->appl.ptr, pcm->boundary);
567 status->avail += diff;
568 status->delay += diff;
570 assert(status->appl_ptr == *pcm->appl.ptr);
575 int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm,
576 snd_pcm_uframes_t avail)
578 if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
579 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
580 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
581 /* mmap access on capture device already consumes data from
582 * slave in avail_update operation. Entering snd_pcm_wait after
583 * having already consumed some fragments leads to waiting for
584 * too long time, as slave will unnecessarily wait for avail_min
585 * condition reached again. To avoid unnecessary wait times we
586 * adapt the avail_min threshold on slave dynamically. Just
587 * modifying slave->avail_min as a shortcut and lightweight
588 * solution does not work for all slave plugin types and in
589 * addition it will not propagate the change through all
590 * downstream plugins, so we have to use the sw_params API.
591 * note: reading fragmental parts from slave will only happen
593 * a) the slave can provide contineous hw_ptr between periods
594 * b) avail_min does not match one slave_period
596 snd_pcm_plugin_t *plugin = pcm->private_data;
597 snd_pcm_t *slave = plugin->gen.slave;
598 snd_pcm_uframes_t needed_slave_avail_min;
599 snd_pcm_sframes_t available;
601 /* update, as it might have changed. This will also call
602 * avail_update on slave and also can return error
604 available = snd_pcm_avail_update(pcm);
608 if ((snd_pcm_uframes_t)available >= pcm->avail_min)
609 /* don't wait at all. As we can't configure avail_min
610 * of slave to 0 return here
614 needed_slave_avail_min = pcm->avail_min - available;
615 if (slave->avail_min != needed_slave_avail_min) {
616 snd_pcm_sw_params_t *swparams;
617 snd_pcm_sw_params_alloca(&swparams);
618 /* pray that changing sw_params while running is
619 * properly implemented in all downstream plugins...
620 * it's legal but not commonly used.
622 snd_pcm_sw_params_current(slave, swparams);
623 /* snd_pcm_sw_params_set_avail_min() restricts setting
624 * to >= period size. This conflicts at least with our
625 * dshare patch which allows combining multiple periods
626 * or with slaves which return hw postions between
627 * periods -> set directly in sw_param structure
629 swparams->avail_min = needed_slave_avail_min;
630 snd_pcm_sw_params(slave, swparams);
634 return snd_pcm_generic_may_wait_for_avail_min(pcm, avail);
637 const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
638 .status = snd_pcm_plugin_status,
639 .state = snd_pcm_generic_state,
640 .hwsync = snd_pcm_generic_hwsync,
641 .delay = snd_pcm_plugin_delay,
642 .prepare = snd_pcm_plugin_prepare,
643 .reset = snd_pcm_plugin_reset,
644 .start = snd_pcm_generic_start,
645 .drop = snd_pcm_generic_drop,
646 .drain = snd_pcm_generic_drain,
647 .pause = snd_pcm_generic_pause,
648 .rewindable = snd_pcm_plugin_rewindable,
649 .rewind = snd_pcm_plugin_rewind,
650 .forwardable = snd_pcm_plugin_forwardable,
651 .forward = snd_pcm_plugin_forward,
652 .resume = snd_pcm_generic_resume,
653 .link = snd_pcm_generic_link,
654 .link_slaves = snd_pcm_generic_link_slaves,
655 .unlink = snd_pcm_generic_unlink,
656 .writei = snd_pcm_plugin_writei,
657 .writen = snd_pcm_plugin_writen,
658 .readi = snd_pcm_plugin_readi,
659 .readn = snd_pcm_plugin_readn,
660 .avail_update = snd_pcm_plugin_avail_update,
661 .mmap_commit = snd_pcm_plugin_mmap_commit,
662 .htimestamp = snd_pcm_generic_htimestamp,
663 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
664 .poll_descriptors = snd_pcm_generic_poll_descriptors,
665 .poll_revents = snd_pcm_generic_poll_revents,
666 .may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,