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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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):
87 #include "pcm_local.h"
88 #include "pcm_plugin.h"
92 static snd_pcm_sframes_t
93 snd_pcm_plugin_undo_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
94 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
95 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
96 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
97 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
102 static snd_pcm_sframes_t
103 snd_pcm_plugin_undo_write(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
104 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
105 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
106 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
107 snd_pcm_uframes_t slave_undo_size ATTRIBUTE_UNUSED)
113 snd_pcm_plugin_undo_read_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
114 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
115 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
116 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
117 snd_pcm_uframes_t slave_undo_size)
119 return slave_undo_size;
123 snd_pcm_plugin_undo_write_generic(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
124 const snd_pcm_channel_area_t *res_areas ATTRIBUTE_UNUSED,
125 snd_pcm_uframes_t res_offset ATTRIBUTE_UNUSED,
126 snd_pcm_uframes_t res_size ATTRIBUTE_UNUSED,
127 snd_pcm_uframes_t slave_undo_size)
129 return slave_undo_size;
132 void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin)
134 memset(plugin, 0, sizeof(snd_pcm_plugin_t));
135 plugin->undo_read = snd_pcm_plugin_undo_read;
136 plugin->undo_write = snd_pcm_plugin_undo_write;
137 snd_atomic_write_init(&plugin->watom);
140 static int snd_pcm_plugin_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
142 snd_pcm_plugin_t *plugin = pcm->private_data;
143 snd_pcm_sframes_t sd;
144 int err = snd_pcm_delay(plugin->gen.slave, &sd);
147 if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
148 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
149 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
150 sd += snd_pcm_mmap_capture_avail(pcm);
157 static int snd_pcm_plugin_prepare(snd_pcm_t *pcm)
159 snd_pcm_plugin_t *plugin = pcm->private_data;
161 snd_atomic_write_begin(&plugin->watom);
162 err = snd_pcm_prepare(plugin->gen.slave);
164 snd_atomic_write_end(&plugin->watom);
169 snd_atomic_write_end(&plugin->watom);
171 err = plugin->init(pcm);
178 static int snd_pcm_plugin_reset(snd_pcm_t *pcm)
180 snd_pcm_plugin_t *plugin = pcm->private_data;
182 snd_atomic_write_begin(&plugin->watom);
183 err = snd_pcm_reset(plugin->gen.slave);
185 snd_atomic_write_end(&plugin->watom);
190 snd_atomic_write_end(&plugin->watom);
192 err = plugin->init(pcm);
199 static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm)
201 return snd_pcm_mmap_hw_avail(pcm);
204 snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
206 snd_pcm_plugin_t *plugin = pcm->private_data;
207 snd_pcm_sframes_t n = snd_pcm_mmap_hw_avail(pcm);
208 snd_pcm_sframes_t sframes;
210 if ((snd_pcm_uframes_t)n < frames)
216 snd_atomic_write_begin(&plugin->watom);
217 sframes = snd_pcm_rewind(plugin->gen.slave, sframes);
219 snd_atomic_write_end(&plugin->watom);
222 snd_pcm_mmap_appl_backward(pcm, (snd_pcm_uframes_t) sframes);
223 snd_atomic_write_end(&plugin->watom);
224 return (snd_pcm_sframes_t) sframes;
227 static snd_pcm_sframes_t snd_pcm_plugin_forwardable(snd_pcm_t *pcm)
229 return snd_pcm_mmap_avail(pcm);
232 snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
234 snd_pcm_plugin_t *plugin = pcm->private_data;
235 snd_pcm_sframes_t n = snd_pcm_mmap_avail(pcm);
236 snd_pcm_sframes_t sframes;
238 if ((snd_pcm_uframes_t)n < frames)
244 snd_atomic_write_begin(&plugin->watom);
245 sframes = INTERNAL(snd_pcm_forward)(plugin->gen.slave, sframes);
247 snd_atomic_write_end(&plugin->watom);
250 snd_pcm_mmap_appl_forward(pcm, (snd_pcm_uframes_t) frames);
251 snd_atomic_write_end(&plugin->watom);
252 return (snd_pcm_sframes_t) frames;
255 static snd_pcm_sframes_t snd_pcm_plugin_write_areas(snd_pcm_t *pcm,
256 const snd_pcm_channel_area_t *areas,
257 snd_pcm_uframes_t offset,
258 snd_pcm_uframes_t size)
260 snd_pcm_plugin_t *plugin = pcm->private_data;
261 snd_pcm_t *slave = plugin->gen.slave;
262 snd_pcm_uframes_t xfer = 0;
263 snd_pcm_sframes_t result;
267 snd_pcm_uframes_t frames = size;
268 const snd_pcm_channel_area_t *slave_areas;
269 snd_pcm_uframes_t slave_offset;
270 snd_pcm_uframes_t slave_frames = ULONG_MAX;
272 err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
273 if (err < 0 || slave_frames == 0)
275 frames = plugin->write(pcm, areas, offset, frames,
276 slave_areas, slave_offset, &slave_frames);
277 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_playback_avail(slave))) {
278 SNDMSG("write overflow %ld > %ld", slave_frames,
279 snd_pcm_mmap_playback_avail(slave));
282 snd_atomic_write_begin(&plugin->watom);
283 snd_pcm_mmap_appl_forward(pcm, frames);
284 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
285 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
286 snd_pcm_sframes_t res;
287 res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
289 return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
292 snd_atomic_write_end(&plugin->watom);
294 return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
299 return (snd_pcm_sframes_t)xfer;
302 static snd_pcm_sframes_t snd_pcm_plugin_read_areas(snd_pcm_t *pcm,
303 const snd_pcm_channel_area_t *areas,
304 snd_pcm_uframes_t offset,
305 snd_pcm_uframes_t size)
307 snd_pcm_plugin_t *plugin = pcm->private_data;
308 snd_pcm_t *slave = plugin->gen.slave;
309 snd_pcm_uframes_t xfer = 0;
310 snd_pcm_sframes_t result;
313 snd_pcm_uframes_t frames = size;
314 const snd_pcm_channel_area_t *slave_areas;
315 snd_pcm_uframes_t slave_offset;
316 snd_pcm_uframes_t slave_frames = ULONG_MAX;
318 snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
319 if (slave_frames == 0)
321 frames = (plugin->read)(pcm, areas, offset, frames,
322 slave_areas, slave_offset, &slave_frames);
323 if (CHECK_SANITY(slave_frames > snd_pcm_mmap_capture_avail(slave))) {
324 SNDMSG("read overflow %ld > %ld", slave_frames,
325 snd_pcm_mmap_playback_avail(slave));
328 snd_atomic_write_begin(&plugin->watom);
329 snd_pcm_mmap_appl_forward(pcm, frames);
330 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
331 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
332 snd_pcm_sframes_t res;
334 res = plugin->undo_read(slave, areas, offset, frames, slave_frames - result);
336 return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
339 snd_atomic_write_end(&plugin->watom);
341 return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
346 return (snd_pcm_sframes_t)xfer;
350 static snd_pcm_sframes_t
351 snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
353 snd_pcm_channel_area_t areas[pcm->channels];
354 snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
355 return snd_pcm_write_areas(pcm, areas, 0, size,
356 snd_pcm_plugin_write_areas);
359 static snd_pcm_sframes_t
360 snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
362 snd_pcm_channel_area_t areas[pcm->channels];
363 snd_pcm_areas_from_bufs(pcm, areas, bufs);
364 return snd_pcm_write_areas(pcm, areas, 0, size,
365 snd_pcm_plugin_write_areas);
368 static snd_pcm_sframes_t
369 snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
371 snd_pcm_channel_area_t areas[pcm->channels];
372 snd_pcm_areas_from_buf(pcm, areas, buffer);
373 return snd_pcm_read_areas(pcm, areas, 0, size,
374 snd_pcm_plugin_read_areas);
377 static snd_pcm_sframes_t
378 snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
380 snd_pcm_channel_area_t areas[pcm->channels];
381 snd_pcm_areas_from_bufs(pcm, areas, bufs);
382 return snd_pcm_read_areas(pcm, areas, 0, size,
383 snd_pcm_plugin_read_areas);
386 static snd_pcm_sframes_t
387 snd_pcm_plugin_mmap_commit(snd_pcm_t *pcm,
388 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
389 snd_pcm_uframes_t size)
391 snd_pcm_plugin_t *plugin = pcm->private_data;
392 snd_pcm_t *slave = plugin->gen.slave;
393 const snd_pcm_channel_area_t *areas;
394 snd_pcm_uframes_t appl_offset;
395 snd_pcm_sframes_t slave_size;
396 snd_pcm_sframes_t xfer;
398 if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
399 snd_atomic_write_begin(&plugin->watom);
400 snd_pcm_mmap_appl_forward(pcm, size);
401 snd_atomic_write_end(&plugin->watom);
404 slave_size = snd_pcm_avail_update(slave);
407 areas = snd_pcm_mmap_areas(pcm);
408 appl_offset = snd_pcm_mmap_offset(pcm);
410 while (size > 0 && slave_size > 0) {
411 snd_pcm_uframes_t frames = size;
412 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
413 const snd_pcm_channel_area_t *slave_areas;
414 snd_pcm_uframes_t slave_offset;
415 snd_pcm_uframes_t slave_frames = ULONG_MAX;
416 snd_pcm_sframes_t result;
419 err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
421 return xfer > 0 ? xfer : err;
424 frames = plugin->write(pcm, areas, appl_offset, frames,
425 slave_areas, slave_offset, &slave_frames);
426 snd_atomic_write_begin(&plugin->watom);
427 snd_pcm_mmap_appl_forward(pcm, frames);
428 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
429 snd_atomic_write_end(&plugin->watom);
430 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
431 snd_pcm_sframes_t res;
433 res = plugin->undo_write(pcm, slave_areas, slave_offset + result, slave_frames, slave_frames - result);
435 return xfer > 0 ? xfer : res;
439 return xfer > 0 ? xfer : result;
443 appl_offset += result;
445 slave_size -= frames;
448 if (CHECK_SANITY(size)) {
449 SNDMSG("short commit: %ld", size);
455 static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
457 snd_pcm_plugin_t *plugin = pcm->private_data;
458 snd_pcm_t *slave = plugin->gen.slave;
459 snd_pcm_sframes_t slave_size;
461 slave_size = snd_pcm_avail_update(slave);
462 if (pcm->stream == SND_PCM_STREAM_CAPTURE &&
463 pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED &&
464 pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED)
466 *pcm->hw.ptr = *slave->hw.ptr;
470 const snd_pcm_channel_area_t *areas;
471 snd_pcm_uframes_t xfer, hw_offset, size;
473 xfer = snd_pcm_mmap_capture_avail(pcm);
474 size = pcm->buffer_size - xfer;
475 areas = snd_pcm_mmap_areas(pcm);
476 hw_offset = snd_pcm_mmap_hw_offset(pcm);
477 while (size > 0 && slave_size > 0) {
478 snd_pcm_uframes_t frames = size;
479 snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
480 const snd_pcm_channel_area_t *slave_areas;
481 snd_pcm_uframes_t slave_offset;
482 snd_pcm_uframes_t slave_frames = ULONG_MAX;
483 snd_pcm_sframes_t result;
486 err = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames);
488 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
491 frames = (plugin->read)(pcm, areas, hw_offset, frames,
492 slave_areas, slave_offset, &slave_frames);
493 snd_atomic_write_begin(&plugin->watom);
494 snd_pcm_mmap_hw_forward(pcm, frames);
495 result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames);
496 snd_atomic_write_end(&plugin->watom);
497 if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) {
498 snd_pcm_sframes_t res;
500 res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result);
502 return xfer > 0 ? (snd_pcm_sframes_t)xfer : res;
506 return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
512 slave_size -= slave_frames;
515 return (snd_pcm_sframes_t)xfer;
519 static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
521 snd_pcm_plugin_t *plugin = pcm->private_data;
522 snd_pcm_sframes_t err;
523 snd_atomic_read_t ratom;
524 snd_atomic_read_init(&ratom, &plugin->watom);
526 snd_atomic_read_begin(&ratom);
527 /* sync with the latest hw and appl ptrs */
528 snd_pcm_plugin_avail_update(pcm);
530 err = snd_pcm_status(plugin->gen.slave, status);
532 snd_atomic_read_ok(&ratom);
535 status->appl_ptr = *pcm->appl.ptr;
536 status->hw_ptr = *pcm->hw.ptr;
537 if (!snd_atomic_read_ok(&ratom)) {
538 snd_atomic_read_wait(&ratom);
544 const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = {
545 .status = snd_pcm_plugin_status,
546 .state = snd_pcm_generic_state,
547 .hwsync = snd_pcm_generic_hwsync,
548 .delay = snd_pcm_plugin_delay,
549 .prepare = snd_pcm_plugin_prepare,
550 .reset = snd_pcm_plugin_reset,
551 .start = snd_pcm_generic_start,
552 .drop = snd_pcm_generic_drop,
553 .drain = snd_pcm_generic_drain,
554 .pause = snd_pcm_generic_pause,
555 .rewindable = snd_pcm_plugin_rewindable,
556 .rewind = snd_pcm_plugin_rewind,
557 .forwardable = snd_pcm_plugin_forwardable,
558 .forward = snd_pcm_plugin_forward,
559 .resume = snd_pcm_generic_resume,
560 .link = snd_pcm_generic_link,
561 .link_slaves = snd_pcm_generic_link_slaves,
562 .unlink = snd_pcm_generic_unlink,
563 .writei = snd_pcm_plugin_writei,
564 .writen = snd_pcm_plugin_writen,
565 .readi = snd_pcm_plugin_readi,
566 .readn = snd_pcm_plugin_readn,
567 .avail_update = snd_pcm_plugin_avail_update,
568 .mmap_commit = snd_pcm_plugin_mmap_commit,
569 .htimestamp = snd_pcm_generic_htimestamp,
570 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
571 .poll_descriptors = snd_pcm_generic_poll_descriptors,
572 .poll_revents = snd_pcm_generic_poll_revents,
573 .may_wait_for_avail_min = snd_pcm_generic_may_wait_for_avail_min,