/** * \file pcm/pcm_null.c * \ingroup PCM_Plugins * \brief PCM Null Plugin Interface * \author Abramo Bagnara * \date 2000-2001 */ /* * PCM - Null plugin * Copyright (c) 2000 by Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "bswap.h" #include #include "pcm_local.h" #include "pcm_plugin.h" #ifndef PIC /* entry for static linking */ const char *_snd_module_pcm_null = ""; #endif #ifndef DOC_HIDDEN typedef struct { snd_htimestamp_t trigger_tstamp; snd_pcm_state_t state; snd_pcm_uframes_t appl_ptr; snd_pcm_uframes_t hw_ptr; int poll_fd; snd_pcm_chmap_query_t **chmap; } snd_pcm_null_t; #endif static int snd_pcm_null_close(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; close(null->poll_fd); free(null); return 0; } static int snd_pcm_null_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_null_async(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int sig ATTRIBUTE_UNUSED, pid_t pid ATTRIBUTE_UNUSED) { return -ENOSYS; } static int snd_pcm_null_info(snd_pcm_t *pcm, snd_pcm_info_t * info) { memset(info, 0, sizeof(*info)); info->stream = pcm->stream; info->card = -1; if (pcm->name) { snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id)); snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name)); snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname)); } info->subdevices_count = 1; return 0; } static snd_pcm_sframes_t snd_pcm_null_avail_update(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; if (null->state == SND_PCM_STATE_PREPARED) { /* it is required to return the correct avail count for */ /* the prepared stream, otherwise the start is not called */ return snd_pcm_mmap_avail(pcm); } return pcm->buffer_size; } static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { snd_pcm_null_t *null = pcm->private_data; memset(status, 0, sizeof(*status)); status->state = null->state; status->trigger_tstamp = null->trigger_tstamp; gettimestamp(&status->tstamp, pcm->tstamp_type); status->avail = snd_pcm_null_avail_update(pcm); status->avail_max = pcm->buffer_size; return 0; } static snd_pcm_state_t snd_pcm_null_state(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; return null->state; } static int snd_pcm_null_hwsync(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_null_delay(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sframes_t *delayp) { *delayp = 0; return 0; } static int snd_pcm_null_reset(snd_pcm_t *pcm) { *pcm->appl.ptr = 0; *pcm->hw.ptr = 0; return 0; } static int snd_pcm_null_prepare(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; null->state = SND_PCM_STATE_PREPARED; return snd_pcm_null_reset(pcm); } static int snd_pcm_null_start(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; assert(null->state == SND_PCM_STATE_PREPARED); null->state = SND_PCM_STATE_RUNNING; if (pcm->stream == SND_PCM_STREAM_CAPTURE) *pcm->hw.ptr = *pcm->appl.ptr + pcm->buffer_size; else *pcm->hw.ptr = *pcm->appl.ptr; return 0; } static int snd_pcm_null_drop(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; assert(null->state != SND_PCM_STATE_OPEN); null->state = SND_PCM_STATE_SETUP; return 0; } static int snd_pcm_null_drain(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; assert(null->state != SND_PCM_STATE_OPEN); null->state = SND_PCM_STATE_SETUP; return 0; } static int snd_pcm_null_pause(snd_pcm_t *pcm, int enable) { snd_pcm_null_t *null = pcm->private_data; if (enable) { if (null->state != SND_PCM_STATE_RUNNING) return -EBADFD; null->state = SND_PCM_STATE_PAUSED; } else { if (null->state != SND_PCM_STATE_PAUSED) return -EBADFD; null->state = SND_PCM_STATE_RUNNING; } return 0; } static snd_pcm_sframes_t snd_pcm_null_rewindable(snd_pcm_t *pcm) { return pcm->buffer_size; } static snd_pcm_sframes_t snd_pcm_null_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; } static snd_pcm_sframes_t snd_pcm_null_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_null_t *null = pcm->private_data; switch (null->state) { case SND_PCM_STATE_RUNNING: snd_pcm_mmap_hw_backward(pcm, frames); /* Fall through */ case SND_PCM_STATE_PREPARED: snd_pcm_mmap_appl_backward(pcm, frames); return frames; default: return -EBADFD; } } static snd_pcm_sframes_t snd_pcm_null_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { snd_pcm_null_t *null = pcm->private_data; switch (null->state) { case SND_PCM_STATE_RUNNING: snd_pcm_mmap_hw_forward(pcm, frames); /* Fall through */ case SND_PCM_STATE_PREPARED: snd_pcm_mmap_appl_forward(pcm, frames); return frames; default: return -EBADFD; } } static int snd_pcm_null_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; } static snd_pcm_sframes_t snd_pcm_null_xfer_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED, snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { snd_pcm_mmap_appl_forward(pcm, size); snd_pcm_mmap_hw_forward(pcm, size); return size; } static snd_pcm_sframes_t snd_pcm_null_writei(snd_pcm_t *pcm, const void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas); } static snd_pcm_sframes_t snd_pcm_null_writen(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { return snd_pcm_write_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas); } static snd_pcm_sframes_t snd_pcm_null_readi(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas); } static snd_pcm_sframes_t snd_pcm_null_readn(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { return snd_pcm_read_areas(pcm, NULL, 0, size, snd_pcm_null_xfer_areas); } static snd_pcm_sframes_t snd_pcm_null_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, snd_pcm_uframes_t size) { return snd_pcm_null_forward(pcm, size); } static int snd_pcm_null_hw_refine(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) { int err; /* Do not return a period size of 0 because for example portaudio cannot * handle it. */ err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_PERIOD_SIZE, 1, 0); if (err < 0) return err; err = snd_pcm_hw_refine_soft(pcm, params); params->info = SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID | SND_PCM_INFO_RESUME | SND_PCM_INFO_PAUSE; params->fifo_size = 0; return err; } static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_null_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { return 0; } static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED) { return 0; } static snd_pcm_chmap_query_t **snd_pcm_null_query_chmaps(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; if (null->chmap) return _snd_pcm_copy_chmap_query(null->chmap); return NULL; } static snd_pcm_chmap_t *snd_pcm_null_get_chmap(snd_pcm_t *pcm) { snd_pcm_null_t *null = pcm->private_data; if (null->chmap) return _snd_pcm_choose_fixed_chmap(pcm, null->chmap); return NULL; } static void snd_pcm_null_dump(snd_pcm_t *pcm, snd_output_t *out) { snd_output_printf(out, "Null PCM\n"); if (pcm->setup) { snd_output_printf(out, "Its setup is:\n"); snd_pcm_dump_setup(pcm, out); } } static const snd_pcm_ops_t snd_pcm_null_ops = { .close = snd_pcm_null_close, .info = snd_pcm_null_info, .hw_refine = snd_pcm_null_hw_refine, .hw_params = snd_pcm_null_hw_params, .hw_free = snd_pcm_null_hw_free, .sw_params = snd_pcm_null_sw_params, .channel_info = snd_pcm_generic_channel_info, .dump = snd_pcm_null_dump, .nonblock = snd_pcm_null_nonblock, .async = snd_pcm_null_async, .mmap = snd_pcm_generic_mmap, .munmap = snd_pcm_generic_munmap, .query_chmaps = snd_pcm_null_query_chmaps, .get_chmap = snd_pcm_null_get_chmap, .set_chmap = NULL, }; static const snd_pcm_fast_ops_t snd_pcm_null_fast_ops = { .status = snd_pcm_null_status, .state = snd_pcm_null_state, .hwsync = snd_pcm_null_hwsync, .delay = snd_pcm_null_delay, .prepare = snd_pcm_null_prepare, .reset = snd_pcm_null_reset, .start = snd_pcm_null_start, .drop = snd_pcm_null_drop, .drain = snd_pcm_null_drain, .pause = snd_pcm_null_pause, .rewindable = snd_pcm_null_rewindable, .rewind = snd_pcm_null_rewind, .forwardable = snd_pcm_null_forwardable, .forward = snd_pcm_null_forward, .resume = snd_pcm_null_resume, .writei = snd_pcm_null_writei, .writen = snd_pcm_null_writen, .readi = snd_pcm_null_readi, .readn = snd_pcm_null_readn, .avail_update = snd_pcm_null_avail_update, .mmap_commit = snd_pcm_null_mmap_commit, .htimestamp = snd_pcm_generic_real_htimestamp, }; /** * \brief Creates a new null PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param stream Stream type * \param mode Stream mode * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely * changed in future. */ int snd_pcm_null_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode) { snd_pcm_t *pcm; snd_pcm_null_t *null; int fd; int err; assert(pcmp); if (stream == SND_PCM_STREAM_PLAYBACK) { fd = open("/dev/null", O_WRONLY); if (fd < 0) { SYSERR("Cannot open /dev/null"); return -errno; } } else { fd = open("/dev/full", O_RDONLY); if (fd < 0) { SYSERR("Cannot open /dev/full"); return -errno; } } null = calloc(1, sizeof(snd_pcm_null_t)); if (!null) { close(fd); return -ENOMEM; } null->poll_fd = fd; null->state = SND_PCM_STATE_OPEN; err = snd_pcm_new(&pcm, SND_PCM_TYPE_NULL, name, stream, mode); if (err < 0) { close(fd); free(null); return err; } pcm->ops = &snd_pcm_null_ops; pcm->fast_ops = &snd_pcm_null_fast_ops; pcm->private_data = null; pcm->poll_fd = fd; pcm->poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; snd_pcm_set_hw_ptr(pcm, &null->hw_ptr, -1, 0); snd_pcm_set_appl_ptr(pcm, &null->appl_ptr, -1, 0); *pcmp = pcm; return 0; } /*! \page pcm_plugins \section pcm_plugins_null Plugin: Null This plugin discards contents of a PCM stream or creates a stream with zero samples. Note: This implementation uses devices /dev/null (playback, must be writable) and /dev/full (capture, must be readable). \code pcm.name { type null # Null PCM [chmap MAP] # Provide channel maps; MAP is a string array } \endcode \subsection pcm_plugins_null_funcref Function reference
  • snd_pcm_null_open()
  • _snd_pcm_null_open()
*/ /** * \brief Creates a new Null PCM * \param pcmp Returns created PCM handle * \param name Name of PCM * \param root Root configuration node * \param conf Configuration node with Null PCM description * \param stream Stream type * \param mode Stream mode * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely * changed in future. */ int _snd_pcm_null_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, snd_pcm_stream_t stream, int mode) { snd_config_iterator_t i, next; snd_pcm_null_t *null; snd_pcm_chmap_query_t **chmap = NULL; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; if (snd_pcm_conf_generic_id(id)) continue; if (strcmp(id, "chmap") == 0) { snd_pcm_free_chmaps(chmap); chmap = _snd_pcm_parse_config_chmaps(n); if (!chmap) { SNDERR("Invalid channel map for %s", id); return -EINVAL; } continue; } SNDERR("Unknown field %s", id); snd_pcm_free_chmaps(chmap); return -EINVAL; } err = snd_pcm_null_open(pcmp, name, stream, mode); if (err < 0) { snd_pcm_free_chmaps(chmap); return err; } null = (*pcmp)->private_data; null->chmap = chmap; return 0; } #ifndef DOC_HIDDEN SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION); #endif