|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \file pcm/pcm_dmix.c
|
|
Packit Service |
db8eaa |
* \ingroup PCM_Plugins
|
|
Packit Service |
db8eaa |
* \brief PCM Direct Stream Mixing (dmix) Plugin Interface
|
|
Packit Service |
db8eaa |
* \author Jaroslav Kysela <perex@perex.cz>
|
|
Packit Service |
db8eaa |
* \date 2003
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* PCM - Direct Stream Mixing
|
|
Packit Service |
db8eaa |
* Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This library is free software; you can redistribute it and/or modify
|
|
Packit Service |
db8eaa |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit Service |
db8eaa |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit Service |
db8eaa |
* the License, or (at your option) any later version.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
db8eaa |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
db8eaa |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
db8eaa |
* GNU Lesser General Public License for more details.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit Service |
db8eaa |
* License along with this library; if not, write to the Free Software
|
|
Packit Service |
db8eaa |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#include <stdio.h>
|
|
Packit Service |
db8eaa |
#include <stdlib.h>
|
|
Packit Service |
db8eaa |
#include <stddef.h>
|
|
Packit Service |
db8eaa |
#include <unistd.h>
|
|
Packit Service |
db8eaa |
#include <signal.h>
|
|
Packit Service |
db8eaa |
#include <string.h>
|
|
Packit Service |
db8eaa |
#include <fcntl.h>
|
|
Packit Service |
db8eaa |
#include <ctype.h>
|
|
Packit Service |
db8eaa |
#include <grp.h>
|
|
Packit Service |
db8eaa |
#include <sys/ioctl.h>
|
|
Packit Service |
db8eaa |
#include <sys/mman.h>
|
|
Packit Service |
db8eaa |
#include <sys/shm.h>
|
|
Packit Service |
db8eaa |
#include <sys/sem.h>
|
|
Packit Service |
db8eaa |
#include <sys/wait.h>
|
|
Packit Service |
db8eaa |
#include <sys/socket.h>
|
|
Packit Service |
db8eaa |
#include <sys/un.h>
|
|
Packit Service |
db8eaa |
#include <sys/mman.h>
|
|
Packit Service |
db8eaa |
#include "pcm_direct.h"
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef PIC
|
|
Packit Service |
db8eaa |
/* entry for static linking */
|
|
Packit Service |
db8eaa |
const char *_snd_module_pcm_dmix = "";
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
/* start is pending - this state happens when rate plugin does a delayed commit */
|
|
Packit Service |
db8eaa |
#define STATE_RUN_PENDING 1024
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int shm_sum_discard(snd_pcm_direct_t *dmix);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* sum ring buffer shared memory area
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static int shm_sum_create_or_connect(snd_pcm_direct_t *dmix)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
struct shmid_ds buf;
|
|
Packit Service |
db8eaa |
int tmpid, err;
|
|
Packit Service |
db8eaa |
size_t size;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
size = dmix->shmptr->s.channels *
|
|
Packit Service |
db8eaa |
dmix->shmptr->s.buffer_size *
|
|
Packit Service |
db8eaa |
sizeof(signed int);
|
|
Packit Service |
db8eaa |
retryshm:
|
|
Packit Service |
db8eaa |
dmix->u.dmix.shmid_sum = shmget(dmix->ipc_key + 1, size,
|
|
Packit Service |
db8eaa |
IPC_CREAT | dmix->ipc_perm);
|
|
Packit Service |
db8eaa |
err = -errno;
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.shmid_sum < 0) {
|
|
Packit Service |
db8eaa |
if (errno == EINVAL)
|
|
Packit Service |
db8eaa |
if ((tmpid = shmget(dmix->ipc_key + 1, 0, dmix->ipc_perm)) != -1)
|
|
Packit Service |
db8eaa |
if (!shmctl(tmpid, IPC_STAT, &buf))
|
|
Packit Service |
db8eaa |
if (!buf.shm_nattch)
|
|
Packit Service |
db8eaa |
/* no users so destroy the segment */
|
|
Packit Service |
db8eaa |
if (!shmctl(tmpid, IPC_RMID, NULL))
|
|
Packit Service |
db8eaa |
goto retryshm;
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0) {
|
|
Packit Service |
db8eaa |
err = -errno;
|
|
Packit Service |
db8eaa |
shm_sum_discard(dmix);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->ipc_gid >= 0) {
|
|
Packit Service |
db8eaa |
buf.shm_perm.gid = dmix->ipc_gid;
|
|
Packit Service |
db8eaa |
shmctl(dmix->u.dmix.shmid_sum, IPC_SET, &buf;;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
dmix->u.dmix.sum_buffer = shmat(dmix->u.dmix.shmid_sum, 0, 0);
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.sum_buffer == (void *) -1) {
|
|
Packit Service |
db8eaa |
err = -errno;
|
|
Packit Service |
db8eaa |
shm_sum_discard(dmix);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
mlock(dmix->u.dmix.sum_buffer, size);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int shm_sum_discard(snd_pcm_direct_t *dmix)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
struct shmid_ds buf;
|
|
Packit Service |
db8eaa |
int ret = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.shmid_sum < 0)
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.sum_buffer != (void *) -1 && shmdt(dmix->u.dmix.sum_buffer) < 0)
|
|
Packit Service |
db8eaa |
return -errno;
|
|
Packit Service |
db8eaa |
dmix->u.dmix.sum_buffer = (void *) -1;
|
|
Packit Service |
db8eaa |
if (shmctl(dmix->u.dmix.shmid_sum, IPC_STAT, &buf) < 0)
|
|
Packit Service |
db8eaa |
return -errno;
|
|
Packit Service |
db8eaa |
if (buf.shm_nattch == 0) { /* we're the last user, destroy the segment */
|
|
Packit Service |
db8eaa |
if (shmctl(dmix->u.dmix.shmid_sum, IPC_RMID, NULL) < 0)
|
|
Packit Service |
db8eaa |
return -errno;
|
|
Packit Service |
db8eaa |
ret = 1;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
dmix->u.dmix.shmid_sum = -1;
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void dmix_server_free(snd_pcm_direct_t *dmix)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
/* remove the memory region */
|
|
Packit Service |
db8eaa |
shm_sum_create_or_connect(dmix);
|
|
Packit Service |
db8eaa |
shm_sum_discard(dmix);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* the main function of this plugin: mixing
|
|
Packit Service |
db8eaa |
* FIXME: optimize it for different architectures
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#include "pcm_dmix_generic.c"
|
|
Packit Service |
db8eaa |
#if defined(__i386__)
|
|
Packit Service |
db8eaa |
#include "pcm_dmix_i386.c"
|
|
Packit Service |
db8eaa |
#elif defined(__x86_64__)
|
|
Packit Service |
db8eaa |
#include "pcm_dmix_x86_64.c"
|
|
Packit Service |
db8eaa |
#else
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
#define mix_select_callbacks(x) generic_mix_select_callbacks(x)
|
|
Packit Service |
db8eaa |
#define dmix_supported_format generic_dmix_supported_format
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void mix_areas(snd_pcm_direct_t *dmix,
|
|
Packit Service |
db8eaa |
const snd_pcm_channel_area_t *src_areas,
|
|
Packit Service |
db8eaa |
const snd_pcm_channel_area_t *dst_areas,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t src_ofs,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t dst_ofs,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
unsigned int src_step, dst_step;
|
|
Packit Service |
db8eaa |
unsigned int chn, dchn, channels, sample_size;
|
|
Packit Service |
db8eaa |
mix_areas_t *do_mix_areas;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
channels = dmix->channels;
|
|
Packit Service |
db8eaa |
switch (dmix->shmptr->s.format) {
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S16_LE:
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S16_BE:
|
|
Packit Service |
db8eaa |
sample_size = 2;
|
|
Packit Service |
db8eaa |
do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_16;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S32_LE:
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S32_BE:
|
|
Packit Service |
db8eaa |
sample_size = 4;
|
|
Packit Service |
db8eaa |
do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_32;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S24_LE:
|
|
Packit Service |
db8eaa |
sample_size = 4;
|
|
Packit Service |
db8eaa |
do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S24_3LE:
|
|
Packit Service |
db8eaa |
sample_size = 3;
|
|
Packit Service |
db8eaa |
do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_24;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_U8:
|
|
Packit Service |
db8eaa |
sample_size = 1;
|
|
Packit Service |
db8eaa |
do_mix_areas = (mix_areas_t *)dmix->u.dmix.mix_areas_u8;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->interleaved) {
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* process all areas in one loop
|
|
Packit Service |
db8eaa |
* it optimizes the memory accesses for this case
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
do_mix_areas(size * channels,
|
|
Packit Service |
db8eaa |
(unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
|
|
Packit Service |
db8eaa |
(unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
|
|
Packit Service |
db8eaa |
dmix->u.dmix.sum_buffer + dst_ofs * channels,
|
|
Packit Service |
db8eaa |
sample_size,
|
|
Packit Service |
db8eaa |
sample_size,
|
|
Packit Service |
db8eaa |
sizeof(signed int));
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
for (chn = 0; chn < channels; chn++) {
|
|
Packit Service |
db8eaa |
dchn = dmix->bindings ? dmix->bindings[chn] : chn;
|
|
Packit Service |
db8eaa |
if (dchn >= dmix->shmptr->s.channels)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
src_step = src_areas[chn].step / 8;
|
|
Packit Service |
db8eaa |
dst_step = dst_areas[dchn].step / 8;
|
|
Packit Service |
db8eaa |
do_mix_areas(size,
|
|
Packit Service |
db8eaa |
((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
|
|
Packit Service |
db8eaa |
((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
|
|
Packit Service |
db8eaa |
dmix->u.dmix.sum_buffer + dmix->shmptr->s.channels * dst_ofs + dchn,
|
|
Packit Service |
db8eaa |
dst_step,
|
|
Packit Service |
db8eaa |
src_step,
|
|
Packit Service |
db8eaa |
dmix->shmptr->s.channels * sizeof(signed int));
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void remix_areas(snd_pcm_direct_t *dmix,
|
|
Packit Service |
db8eaa |
const snd_pcm_channel_area_t *src_areas,
|
|
Packit Service |
db8eaa |
const snd_pcm_channel_area_t *dst_areas,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t src_ofs,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t dst_ofs,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
unsigned int src_step, dst_step;
|
|
Packit Service |
db8eaa |
unsigned int chn, dchn, channels, sample_size;
|
|
Packit Service |
db8eaa |
mix_areas_t *do_remix_areas;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
channels = dmix->channels;
|
|
Packit Service |
db8eaa |
switch (dmix->shmptr->s.format) {
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S16_LE:
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S16_BE:
|
|
Packit Service |
db8eaa |
sample_size = 2;
|
|
Packit Service |
db8eaa |
do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_16;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S32_LE:
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S32_BE:
|
|
Packit Service |
db8eaa |
sample_size = 4;
|
|
Packit Service |
db8eaa |
do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_32;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S24_LE:
|
|
Packit Service |
db8eaa |
sample_size = 4;
|
|
Packit Service |
db8eaa |
do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_24;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_S24_3LE:
|
|
Packit Service |
db8eaa |
sample_size = 3;
|
|
Packit Service |
db8eaa |
do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_24;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_FORMAT_U8:
|
|
Packit Service |
db8eaa |
sample_size = 1;
|
|
Packit Service |
db8eaa |
do_remix_areas = (mix_areas_t *)dmix->u.dmix.remix_areas_u8;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->interleaved) {
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* process all areas in one loop
|
|
Packit Service |
db8eaa |
* it optimizes the memory accesses for this case
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
do_remix_areas(size * channels,
|
|
Packit Service |
db8eaa |
(unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels,
|
|
Packit Service |
db8eaa |
(unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels,
|
|
Packit Service |
db8eaa |
dmix->u.dmix.sum_buffer + dst_ofs * channels,
|
|
Packit Service |
db8eaa |
sample_size,
|
|
Packit Service |
db8eaa |
sample_size,
|
|
Packit Service |
db8eaa |
sizeof(signed int));
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
for (chn = 0; chn < channels; chn++) {
|
|
Packit Service |
db8eaa |
dchn = dmix->bindings ? dmix->bindings[chn] : chn;
|
|
Packit Service |
db8eaa |
if (dchn >= dmix->shmptr->s.channels)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
src_step = src_areas[chn].step / 8;
|
|
Packit Service |
db8eaa |
dst_step = dst_areas[dchn].step / 8;
|
|
Packit Service |
db8eaa |
do_remix_areas(size,
|
|
Packit Service |
db8eaa |
((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step,
|
|
Packit Service |
db8eaa |
((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step,
|
|
Packit Service |
db8eaa |
dmix->u.dmix.sum_buffer + dmix->shmptr->s.channels * dst_ofs + dchn,
|
|
Packit Service |
db8eaa |
dst_step,
|
|
Packit Service |
db8eaa |
src_step,
|
|
Packit Service |
db8eaa |
dmix->shmptr->s.channels * sizeof(signed int));
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* if no concurrent access is allowed in the mixing routines, we need to protect
|
|
Packit Service |
db8eaa |
* the area via semaphore
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
static void dmix_down_sem(snd_pcm_direct_t *dmix)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.use_sem)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void dmix_up_sem(snd_pcm_direct_t *dmix)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.use_sem)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* synchronize shm ring buffer with hardware
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t slave_hw_ptr, slave_appl_ptr, slave_size;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t appl_ptr, size, transfer;
|
|
Packit Service |
db8eaa |
const snd_pcm_channel_area_t *src_areas, *dst_areas;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* calculate the size to transfer */
|
|
Packit Service |
db8eaa |
/* check the available size in the local buffer
|
|
Packit Service |
db8eaa |
* last_appl_ptr keeps the last updated position
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
size = pcm_frame_diff2(dmix->appl_ptr, dmix->last_appl_ptr, pcm->boundary);
|
|
Packit Service |
db8eaa |
if (! size)
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* the slave_app_ptr can be far behind the slave_hw_ptr */
|
|
Packit Service |
db8eaa |
/* reduce mixing and errors here - just skip not catched writes */
|
|
Packit Service |
db8eaa |
slave_size = pcm_frame_diff(dmix->slave_appl_ptr, dmix->slave_hw_ptr, dmix->slave_boundary);
|
|
Packit Service |
db8eaa |
if (slave_size > dmix->slave_buffer_size) {
|
|
Packit Service |
db8eaa |
transfer = dmix->slave_buffer_size - slave_size;
|
|
Packit Service |
db8eaa |
if (transfer > size)
|
|
Packit Service |
db8eaa |
transfer = size;
|
|
Packit Service |
db8eaa |
dmix->last_appl_ptr += transfer;
|
|
Packit Service |
db8eaa |
dmix->last_appl_ptr %= pcm->boundary;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr += transfer;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr %= dmix->slave_boundary;
|
|
Packit Service |
db8eaa |
size = pcm_frame_diff2(dmix->appl_ptr, dmix->last_appl_ptr, pcm->boundary);
|
|
Packit Service |
db8eaa |
if (! size)
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* check the available size in the slave PCM buffer */
|
|
Packit Service |
db8eaa |
slave_hw_ptr = dmix->slave_hw_ptr;
|
|
Packit Service |
db8eaa |
/* don't write on the last active period - this area may be cleared
|
|
Packit Service |
db8eaa |
* by the driver during mix operation...
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
slave_hw_ptr -= slave_hw_ptr % dmix->slave_period_size;
|
|
Packit Service |
db8eaa |
slave_hw_ptr += dmix->slave_buffer_size;
|
|
Packit Service |
db8eaa |
if (slave_hw_ptr >= dmix->slave_boundary)
|
|
Packit Service |
db8eaa |
slave_hw_ptr -= dmix->slave_boundary;
|
|
Packit Service |
db8eaa |
slave_size = pcm_frame_diff(slave_hw_ptr, dmix->slave_appl_ptr, dmix->slave_boundary);
|
|
Packit Service |
db8eaa |
if (slave_size < size)
|
|
Packit Service |
db8eaa |
size = slave_size;
|
|
Packit Service |
db8eaa |
if (! size)
|
|
Packit Service |
db8eaa |
return;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* add sample areas here */
|
|
Packit Service |
db8eaa |
src_areas = snd_pcm_mmap_areas(pcm);
|
|
Packit Service |
db8eaa |
dst_areas = snd_pcm_mmap_areas(dmix->spcm);
|
|
Packit Service |
db8eaa |
appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
|
|
Packit Service |
db8eaa |
dmix->last_appl_ptr += size;
|
|
Packit Service |
db8eaa |
dmix->last_appl_ptr %= pcm->boundary;
|
|
Packit Service |
db8eaa |
slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr += size;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr %= dmix->slave_boundary;
|
|
Packit Service |
db8eaa |
dmix_down_sem(dmix);
|
|
Packit Service |
db8eaa |
for (;;) {
|
|
Packit Service |
db8eaa |
transfer = size;
|
|
Packit Service |
db8eaa |
if (appl_ptr + transfer > pcm->buffer_size)
|
|
Packit Service |
db8eaa |
transfer = pcm->buffer_size - appl_ptr;
|
|
Packit Service |
db8eaa |
if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
|
|
Packit Service |
db8eaa |
transfer = dmix->slave_buffer_size - slave_appl_ptr;
|
|
Packit Service |
db8eaa |
mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
|
|
Packit Service |
db8eaa |
size -= transfer;
|
|
Packit Service |
db8eaa |
if (! size)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
slave_appl_ptr += transfer;
|
|
Packit Service |
db8eaa |
slave_appl_ptr %= dmix->slave_buffer_size;
|
|
Packit Service |
db8eaa |
appl_ptr += transfer;
|
|
Packit Service |
db8eaa |
appl_ptr %= pcm->buffer_size;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
dmix_up_sem(dmix);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* synchronize hardware pointer (hw_ptr) with ours
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t old_slave_hw_ptr, avail;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t diff;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
old_slave_hw_ptr = dmix->slave_hw_ptr;
|
|
Packit Service |
db8eaa |
dmix->slave_hw_ptr = slave_hw_ptr;
|
|
Packit Service |
db8eaa |
diff = slave_hw_ptr - old_slave_hw_ptr;
|
|
Packit Service |
db8eaa |
if (diff == 0) /* fast path */
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
if (dmix->state != SND_PCM_STATE_RUNNING &&
|
|
Packit Service |
db8eaa |
dmix->state != SND_PCM_STATE_DRAINING)
|
|
Packit Service |
db8eaa |
/* not really started yet - don't update hw_ptr */
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
if (diff < 0) {
|
|
Packit Service |
db8eaa |
slave_hw_ptr += dmix->slave_boundary;
|
|
Packit Service |
db8eaa |
diff = slave_hw_ptr - old_slave_hw_ptr;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
dmix->hw_ptr += diff;
|
|
Packit Service |
db8eaa |
dmix->hw_ptr %= pcm->boundary;
|
|
Packit Service |
db8eaa |
if (pcm->stop_threshold >= pcm->boundary) /* don't care */
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
avail = snd_pcm_mmap_playback_avail(pcm);
|
|
Packit Service |
db8eaa |
if (avail > dmix->avail_max)
|
|
Packit Service |
db8eaa |
dmix->avail_max = avail;
|
|
Packit Service |
db8eaa |
if (avail >= pcm->stop_threshold) {
|
|
Packit Service |
db8eaa |
snd_timer_stop(dmix->timer);
|
|
Packit Service |
db8eaa |
gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type);
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_RUNNING) {
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_XRUN;
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
/* clear queue to remove pending poll events */
|
|
Packit Service |
db8eaa |
snd_pcm_direct_clear_timer_queue(dmix);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (snd_pcm_state(dmix->spcm)) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DISCONNECTED:
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_DISCONNECTED;
|
|
Packit Service |
db8eaa |
return -ENODEV;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
if (dmix->slowptr)
|
|
Packit Service |
db8eaa |
snd_pcm_hwsync(dmix->spcm);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return snd_pcm_dmix_sync_ptr0(pcm, *dmix->spcm->hw.ptr);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* plugin implementation
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
snd_pcm_state_t state;
|
|
Packit Service |
db8eaa |
state = snd_pcm_state(dmix->spcm);
|
|
Packit Service |
db8eaa |
switch (state) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_SUSPENDED:
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_DISCONNECTED:
|
|
Packit Service |
db8eaa |
dmix->state = state;
|
|
Packit Service |
db8eaa |
return state;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_direct_client_chk_xrun(dmix, pcm);
|
|
Packit Service |
db8eaa |
if (dmix->state == STATE_RUN_PENDING)
|
|
Packit Service |
db8eaa |
return SNDRV_PCM_STATE_RUNNING;
|
|
Packit Service |
db8eaa |
return dmix->state;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
memset(status, 0, sizeof(*status));
|
|
Packit Service |
db8eaa |
snd_pcm_status(dmix->spcm, status);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (dmix->state) {
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_sync_ptr0(pcm, status->hw_ptr);
|
|
Packit Service |
01c0b7 |
status->delay = snd_pcm_mmap_playback_delay(pcm);
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
status->state = snd_pcm_dmix_state(pcm);
|
|
Packit Service |
01c0b7 |
status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */
|
|
Packit Service |
db8eaa |
status->trigger_tstamp = dmix->trigger_tstamp;
|
|
Packit Service |
db8eaa |
status->avail = snd_pcm_mmap_playback_avail(pcm);
|
|
Packit Service |
db8eaa |
status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max;
|
|
Packit Service |
db8eaa |
dmix->avail_max = 0;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch(dmix->state) {
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
err = snd_pcm_dmix_sync_ptr(pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
/* fallthru */
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_SUSPENDED:
|
|
Packit Service |
db8eaa |
case STATE_RUN_PENDING:
|
|
Packit Service |
01c0b7 |
*delayp = snd_pcm_mmap_playback_delay(pcm);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_DISCONNECTED:
|
|
Packit Service |
db8eaa |
return -ENODEV;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_hwsync(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch(dmix->state) {
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_DRAINING:
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_RUNNING:
|
|
Packit Service |
db8eaa |
/* sync slave PCM */
|
|
Packit Service |
db8eaa |
return snd_pcm_dmix_sync_ptr(pcm);
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_PREPARED:
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_SUSPENDED:
|
|
Packit Service |
db8eaa |
case STATE_RUN_PENDING:
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
case SNDRV_PCM_STATE_DISCONNECTED:
|
|
Packit Service |
db8eaa |
return -ENODEV;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
dmix->hw_ptr %= pcm->period_size;
|
|
Packit Service |
db8eaa |
dmix->appl_ptr = dmix->last_appl_ptr = dmix->hw_ptr;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
|
|
Packit Service |
db8eaa |
snd_pcm_direct_reset_slave_ptr(pcm, dmix);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_hwsync(dmix->spcm);
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
|
|
Packit Service |
db8eaa |
snd_pcm_direct_reset_slave_ptr(pcm, dmix);
|
|
Packit Service |
db8eaa |
err = snd_timer_start(dmix->timer);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_RUNNING;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_start(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->state != SND_PCM_STATE_PREPARED)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
avail = snd_pcm_mmap_playback_hw_avail(pcm);
|
|
Packit Service |
db8eaa |
if (avail == 0)
|
|
Packit Service |
db8eaa |
dmix->state = STATE_RUN_PENDING;
|
|
Packit Service |
db8eaa |
else if (avail < 0)
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_sync_area(pcm);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
gettimestamp(&dmix->trigger_tstamp, pcm->tstamp_type);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_drop(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_OPEN)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_SETUP;
|
|
Packit Service |
db8eaa |
snd_pcm_direct_timer_stop(dmix);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* locked version */
|
|
Packit Service |
db8eaa |
static int __snd_pcm_dmix_drain(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t stop_threshold;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (snd_pcm_state(dmix->spcm)) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_SUSPENDED:
|
|
Packit Service |
db8eaa |
return -ESTRPIPE;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_OPEN)
|
|
Packit Service |
db8eaa |
return -EBADFD;
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_PREPARED) {
|
|
Packit Service |
db8eaa |
if (snd_pcm_mmap_playback_hw_avail(pcm) > 0)
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_start(pcm);
|
|
Packit Service |
db8eaa |
else {
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_drop(pcm);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_XRUN) {
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_drop(pcm);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
stop_threshold = pcm->stop_threshold;
|
|
Packit Service |
db8eaa |
if (pcm->stop_threshold > pcm->buffer_size)
|
|
Packit Service |
db8eaa |
pcm->stop_threshold = pcm->buffer_size;
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_DRAINING;
|
|
Packit Service |
db8eaa |
do {
|
|
Packit Service |
db8eaa |
err = snd_pcm_dmix_sync_ptr(pcm);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_drop(pcm);
|
|
Packit Service |
db8eaa |
goto done;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_DRAINING) {
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_sync_area(pcm);
|
|
Packit Service |
db8eaa |
if ((pcm->mode & SND_PCM_NONBLOCK) == 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_wait_nocheck(pcm, -1);
|
|
Packit Service |
db8eaa |
snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (snd_pcm_state(dmix->spcm)) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_SUSPENDED:
|
|
Packit Service |
db8eaa |
err = -ESTRPIPE;
|
|
Packit Service |
db8eaa |
goto done;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (pcm->mode & SND_PCM_NONBLOCK) {
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_DRAINING) {
|
|
Packit Service |
db8eaa |
err = -EAGAIN;
|
|
Packit Service |
db8eaa |
goto done;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
} while (dmix->state == SND_PCM_STATE_DRAINING);
|
|
Packit Service |
db8eaa |
done:
|
|
Packit Service |
db8eaa |
pcm->stop_threshold = stop_threshold;
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_drain(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_lock(pcm);
|
|
Packit Service |
db8eaa |
err = __snd_pcm_dmix_drain(pcm);
|
|
Packit Service |
db8eaa |
snd_pcm_unlock(pcm);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_pause(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int enable ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return -EIO;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_rewindable(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return snd_pcm_mmap_playback_hw_rewindable(pcm);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t slave_appl_ptr, slave_size;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t appl_ptr, size, transfer, result, frames_to_remix;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
const snd_pcm_channel_area_t *src_areas, *dst_areas;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_RUNNING ||
|
|
Packit Service |
db8eaa |
dmix->state == SND_PCM_STATE_DRAINING) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_dmix_hwsync(pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* (appl_ptr - last_appl_ptr) indicates the frames which are not
|
|
Packit Service |
db8eaa |
* already mixed
|
|
Packit Service |
db8eaa |
* (last_appl_ptr - hw_ptr) indicates the frames which are already
|
|
Packit Service |
db8eaa |
* mixed but not played yet.
|
|
Packit Service |
db8eaa |
* So they can be remixed.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
size = pcm_frame_diff(dmix->last_appl_ptr, dmix->appl_ptr, pcm->boundary);
|
|
Packit Service |
db8eaa |
if (frames < size)
|
|
Packit Service |
db8eaa |
size = frames;
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_backward(pcm, size);
|
|
Packit Service |
db8eaa |
frames -= size;
|
|
Packit Service |
db8eaa |
if (!frames)
|
|
Packit Service |
db8eaa |
return size;
|
|
Packit Service |
db8eaa |
result = size;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* Always at this point last_appl_ptr == appl_ptr
|
|
Packit Service |
db8eaa |
* So (appl_ptr - hw_ptr) indicates the frames which can be remixed
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
size = pcm_frame_diff(dmix->appl_ptr, dmix->hw_ptr, pcm->boundary);
|
|
Packit Service |
db8eaa |
if (size > frames)
|
|
Packit Service |
db8eaa |
size = frames;
|
|
Packit Service |
db8eaa |
slave_size = pcm_frame_diff(dmix->slave_appl_ptr, dmix->slave_hw_ptr, pcm->boundary);
|
|
Packit Service |
db8eaa |
if (slave_size < size)
|
|
Packit Service |
db8eaa |
size = slave_size;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* frames which should be remixed will be saved
|
|
Packit Service |
db8eaa |
* to also backward the appl pointer on success
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
frames_to_remix = size;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* add sample areas here */
|
|
Packit Service |
db8eaa |
src_areas = snd_pcm_mmap_areas(pcm);
|
|
Packit Service |
db8eaa |
dst_areas = snd_pcm_mmap_areas(dmix->spcm);
|
|
Packit Service |
db8eaa |
dmix->last_appl_ptr -= size;
|
|
Packit Service |
db8eaa |
dmix->last_appl_ptr %= pcm->boundary;
|
|
Packit Service |
db8eaa |
appl_ptr = dmix->last_appl_ptr % pcm->buffer_size;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr -= size;
|
|
Packit Service |
db8eaa |
dmix->slave_appl_ptr %= dmix->slave_boundary;
|
|
Packit Service |
db8eaa |
slave_appl_ptr = dmix->slave_appl_ptr % dmix->slave_buffer_size;
|
|
Packit Service |
db8eaa |
dmix_down_sem(dmix);
|
|
Packit Service |
db8eaa |
for (;;) {
|
|
Packit Service |
db8eaa |
transfer = size;
|
|
Packit Service |
db8eaa |
if (appl_ptr + transfer > pcm->buffer_size)
|
|
Packit Service |
db8eaa |
transfer = pcm->buffer_size - appl_ptr;
|
|
Packit Service |
db8eaa |
if (slave_appl_ptr + transfer > dmix->slave_buffer_size)
|
|
Packit Service |
db8eaa |
transfer = dmix->slave_buffer_size - slave_appl_ptr;
|
|
Packit Service |
db8eaa |
remix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer);
|
|
Packit Service |
db8eaa |
size -= transfer;
|
|
Packit Service |
db8eaa |
if (! size)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
slave_appl_ptr += transfer;
|
|
Packit Service |
db8eaa |
slave_appl_ptr %= dmix->slave_buffer_size;
|
|
Packit Service |
db8eaa |
appl_ptr += transfer;
|
|
Packit Service |
db8eaa |
appl_ptr %= pcm->buffer_size;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
dmix_up_sem(dmix);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_backward(pcm, frames_to_remix);
|
|
Packit Service |
db8eaa |
result += frames_to_remix;
|
|
Packit Service |
db8eaa |
/* At this point last_appl_ptr and appl_ptr has to indicate the
|
|
Packit Service |
db8eaa |
* position of the first not mixed frame
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return result;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_forwardable(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return snd_pcm_mmap_avail(pcm);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
avail = snd_pcm_dmix_forwardable(pcm);
|
|
Packit Service |
db8eaa |
if (frames > (snd_pcm_uframes_t)avail)
|
|
Packit Service |
db8eaa |
frames = avail;
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_forward(pcm, frames);
|
|
Packit Service |
db8eaa |
return frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return -ENODEV;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return -ENODEV;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_close(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->timer)
|
|
Packit Service |
db8eaa |
snd_timer_close(dmix->timer);
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
snd_pcm_close(dmix->spcm);
|
|
Packit Service |
db8eaa |
if (dmix->server)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_server_discard(dmix);
|
|
Packit Service |
db8eaa |
if (dmix->client)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_client_discard(dmix);
|
|
Packit Service |
db8eaa |
shm_sum_discard(dmix);
|
|
Packit Service |
db8eaa |
if (snd_pcm_direct_shm_discard(dmix)) {
|
|
Packit Service |
db8eaa |
if (snd_pcm_direct_semaphore_discard(dmix))
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_final(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
} else
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_final(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
free(dmix->bindings);
|
|
Packit Service |
db8eaa |
pcm->private_data = NULL;
|
|
Packit Service |
db8eaa |
free(dmix);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
switch (snd_pcm_state(dmix->spcm)) {
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_XRUN:
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
case SND_PCM_STATE_SUSPENDED:
|
|
Packit Service |
db8eaa |
return -ESTRPIPE;
|
|
Packit Service |
db8eaa |
default:
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
if (! size)
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
snd_pcm_mmap_appl_forward(pcm, size);
|
|
Packit Service |
db8eaa |
if (dmix->state == STATE_RUN_PENDING) {
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_dmix_start_timer(pcm, dmix)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
} else if (dmix->state == SND_PCM_STATE_RUNNING ||
|
|
Packit Service |
db8eaa |
dmix->state == SND_PCM_STATE_DRAINING) {
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_RUNNING ||
|
|
Packit Service |
db8eaa |
dmix->state == SND_PCM_STATE_DRAINING) {
|
|
Packit Service |
db8eaa |
/* ok, we commit the changes after the validation of area */
|
|
Packit Service |
db8eaa |
/* it's intended, although the result might be crappy */
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_sync_area(pcm);
|
|
Packit Service |
db8eaa |
/* clear timer queue to avoid a bogus return from poll */
|
|
Packit Service |
db8eaa |
if (snd_pcm_mmap_playback_avail(pcm) < pcm->avail_min)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_clear_timer_queue(dmix);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return size;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_dmix_avail_update(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_RUNNING ||
|
|
Packit Service |
db8eaa |
dmix->state == SND_PCM_STATE_DRAINING) {
|
|
Packit Service |
db8eaa |
if ((err = snd_pcm_dmix_sync_ptr(pcm)) < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_XRUN)
|
|
Packit Service |
db8eaa |
return -EPIPE;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return snd_pcm_mmap_playback_avail(pcm);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_htimestamp(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t *avail,
|
|
Packit Service |
db8eaa |
snd_htimestamp_t *tstamp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t avail1;
|
|
Packit Service |
db8eaa |
int ok = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
while (1) {
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_RUNNING ||
|
|
Packit Service |
db8eaa |
dmix->state == SND_PCM_STATE_DRAINING)
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_sync_ptr(pcm);
|
|
Packit Service |
db8eaa |
avail1 = snd_pcm_mmap_playback_avail(pcm);
|
|
Packit Service |
db8eaa |
if (ok && *avail == avail1)
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
*avail = avail1;
|
|
Packit Service |
db8eaa |
*tstamp = snd_pcm_hw_fast_tstamp(dmix->spcm);
|
|
Packit Service |
db8eaa |
ok = 1;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_dmix_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
if (dmix->state == SND_PCM_STATE_RUNNING)
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_sync_area(pcm);
|
|
Packit Service |
db8eaa |
return snd_pcm_direct_poll_revents(pcm, pfds, nfds, revents);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void snd_pcm_dmix_dump(snd_pcm_t *pcm, snd_output_t *out)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = pcm->private_data;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Direct Stream Mixing PCM\n");
|
|
Packit Service |
db8eaa |
if (pcm->setup) {
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Its setup is:\n");
|
|
Packit Service |
db8eaa |
snd_pcm_dump_setup(pcm, out);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (dmix->spcm)
|
|
Packit Service |
db8eaa |
snd_pcm_dump(dmix->spcm, out);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static const snd_pcm_ops_t snd_pcm_dmix_ops = {
|
|
Packit Service |
db8eaa |
.close = snd_pcm_dmix_close,
|
|
Packit Service |
db8eaa |
.info = snd_pcm_direct_info,
|
|
Packit Service |
db8eaa |
.hw_refine = snd_pcm_direct_hw_refine,
|
|
Packit Service |
db8eaa |
.hw_params = snd_pcm_direct_hw_params,
|
|
Packit Service |
db8eaa |
.hw_free = snd_pcm_direct_hw_free,
|
|
Packit Service |
db8eaa |
.sw_params = snd_pcm_direct_sw_params,
|
|
Packit Service |
db8eaa |
.channel_info = snd_pcm_direct_channel_info,
|
|
Packit Service |
db8eaa |
.dump = snd_pcm_dmix_dump,
|
|
Packit Service |
db8eaa |
.nonblock = snd_pcm_direct_nonblock,
|
|
Packit Service |
db8eaa |
.async = snd_pcm_direct_async,
|
|
Packit Service |
db8eaa |
.mmap = snd_pcm_direct_mmap,
|
|
Packit Service |
db8eaa |
.munmap = snd_pcm_direct_munmap,
|
|
Packit Service |
db8eaa |
.query_chmaps = snd_pcm_direct_query_chmaps,
|
|
Packit Service |
db8eaa |
.get_chmap = snd_pcm_direct_get_chmap,
|
|
Packit Service |
db8eaa |
.set_chmap = snd_pcm_direct_set_chmap,
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static const snd_pcm_fast_ops_t snd_pcm_dmix_fast_ops = {
|
|
Packit Service |
db8eaa |
.status = snd_pcm_dmix_status,
|
|
Packit Service |
db8eaa |
.state = snd_pcm_dmix_state,
|
|
Packit Service |
db8eaa |
.hwsync = snd_pcm_dmix_hwsync,
|
|
Packit Service |
db8eaa |
.delay = snd_pcm_dmix_delay,
|
|
Packit Service |
db8eaa |
.prepare = snd_pcm_direct_prepare,
|
|
Packit Service |
db8eaa |
.reset = snd_pcm_dmix_reset,
|
|
Packit Service |
db8eaa |
.start = snd_pcm_dmix_start,
|
|
Packit Service |
db8eaa |
.drop = snd_pcm_dmix_drop,
|
|
Packit Service |
db8eaa |
.drain = snd_pcm_dmix_drain,
|
|
Packit Service |
db8eaa |
.pause = snd_pcm_dmix_pause,
|
|
Packit Service |
db8eaa |
.rewindable = snd_pcm_dmix_rewindable,
|
|
Packit Service |
db8eaa |
.rewind = snd_pcm_dmix_rewind,
|
|
Packit Service |
db8eaa |
.forwardable = snd_pcm_dmix_forwardable,
|
|
Packit Service |
db8eaa |
.forward = snd_pcm_dmix_forward,
|
|
Packit Service |
db8eaa |
.resume = snd_pcm_direct_resume,
|
|
Packit Service |
db8eaa |
.link = NULL,
|
|
Packit Service |
db8eaa |
.link_slaves = NULL,
|
|
Packit Service |
db8eaa |
.unlink = NULL,
|
|
Packit Service |
db8eaa |
.writei = snd_pcm_mmap_writei,
|
|
Packit Service |
db8eaa |
.writen = snd_pcm_mmap_writen,
|
|
Packit Service |
db8eaa |
.readi = snd_pcm_dmix_readi,
|
|
Packit Service |
db8eaa |
.readn = snd_pcm_dmix_readn,
|
|
Packit Service |
db8eaa |
.avail_update = snd_pcm_dmix_avail_update,
|
|
Packit Service |
db8eaa |
.mmap_commit = snd_pcm_dmix_mmap_commit,
|
|
Packit Service |
db8eaa |
.htimestamp = snd_pcm_dmix_htimestamp,
|
|
Packit Service |
db8eaa |
.poll_descriptors = snd_pcm_direct_poll_descriptors,
|
|
Packit Service |
db8eaa |
.poll_descriptors_count = NULL,
|
|
Packit Service |
db8eaa |
.poll_revents = snd_pcm_dmix_poll_revents,
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Creates a new dmix PCM
|
|
Packit Service |
db8eaa |
* \param pcmp Returns created PCM handle
|
|
Packit Service |
db8eaa |
* \param name Name of PCM
|
|
Packit Service |
db8eaa |
* \param opts Direct PCM configurations
|
|
Packit Service |
db8eaa |
* \param params Parameters for slave
|
|
Packit Service |
db8eaa |
* \param root Configuration root
|
|
Packit Service |
db8eaa |
* \param sconf Slave configuration
|
|
Packit Service |
db8eaa |
* \param stream PCM Direction (stream)
|
|
Packit Service |
db8eaa |
* \param mode PCM Mode
|
|
Packit Service |
db8eaa |
* \retval zero on success otherwise a negative error code
|
|
Packit Service |
db8eaa |
* \warning Using of this function might be dangerous in the sense
|
|
Packit Service |
db8eaa |
* of compatibility reasons. The prototype might be freely
|
|
Packit Service |
db8eaa |
* changed in future.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit Service |
db8eaa |
struct snd_pcm_direct_open_conf *opts,
|
|
Packit Service |
db8eaa |
struct slave_params *params,
|
|
Packit Service |
db8eaa |
snd_config_t *root, snd_config_t *sconf,
|
|
Packit Service |
db8eaa |
snd_pcm_stream_t stream, int mode)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm = NULL, *spcm = NULL;
|
|
Packit Service |
db8eaa |
snd_pcm_direct_t *dmix = NULL;
|
|
Packit Service |
db8eaa |
int ret, first_instance;
|
|
Packit Service |
db8eaa |
int fail_sem_loop = 10;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
assert(pcmp);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (stream != SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
SNDERR("The dmix plugin supports only playback stream");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
dmix = calloc(1, sizeof(snd_pcm_direct_t));
|
|
Packit Service |
db8eaa |
if (!dmix) {
|
|
Packit Service |
db8eaa |
ret = -ENOMEM;
|
|
Packit Service |
db8eaa |
goto _err_nosem;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings);
|
|
Packit Service |
db8eaa |
if (ret < 0)
|
|
Packit Service |
db8eaa |
goto _err_nosem;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
dmix->ipc_key = opts->ipc_key;
|
|
Packit Service |
db8eaa |
dmix->ipc_perm = opts->ipc_perm;
|
|
Packit Service |
db8eaa |
dmix->ipc_gid = opts->ipc_gid;
|
|
Packit Service |
db8eaa |
dmix->tstamp_type = opts->tstamp_type;
|
|
Packit Service |
db8eaa |
dmix->semid = -1;
|
|
Packit Service |
db8eaa |
dmix->shmid = -1;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_new(&pcm, dmix->type = SND_PCM_TYPE_DMIX, name, stream, mode);
|
|
Packit Service |
db8eaa |
if (ret < 0)
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
while (1) {
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_semaphore_create_or_connect(dmix);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to create IPC semaphore");
|
|
Packit Service |
db8eaa |
goto _err_nosem;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_discard(dmix);
|
|
Packit Service |
db8eaa |
if (--fail_sem_loop <= 0)
|
|
Packit Service |
db8eaa |
goto _err_nosem;
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to create IPC shm instance");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
pcm->ops = &snd_pcm_dmix_ops;
|
|
Packit Service |
db8eaa |
pcm->fast_ops = &snd_pcm_dmix_fast_ops;
|
|
Packit Service |
db8eaa |
pcm->private_data = dmix;
|
|
Packit Service |
db8eaa |
dmix->state = SND_PCM_STATE_OPEN;
|
|
Packit Service |
db8eaa |
dmix->slowptr = opts->slowptr;
|
|
Packit Service |
db8eaa |
dmix->max_periods = opts->max_periods;
|
|
Packit Service |
db8eaa |
dmix->var_periodsize = opts->var_periodsize;
|
|
Packit Service |
db8eaa |
dmix->hw_ptr_alignment = opts->hw_ptr_alignment;
|
|
Packit Service |
db8eaa |
dmix->sync_ptr = snd_pcm_dmix_sync_ptr;
|
|
Packit Service |
db8eaa |
dmix->direct_memory_access = opts->direct_memory_access;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
retry:
|
|
Packit Service |
db8eaa |
if (first_instance) {
|
|
Packit Service |
db8eaa |
/* recursion is already checked in
|
|
Packit Service |
db8eaa |
snd_pcm_direct_get_slave_ipc_offset() */
|
|
Packit Service |
db8eaa |
ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
|
|
Packit Service |
db8eaa |
mode | SND_PCM_NONBLOCK, NULL);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to open slave");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
|
|
Packit Service |
db8eaa |
SNDERR("dmix plugin can be only connected to hw plugin");
|
|
Packit Service |
db8eaa |
ret = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_initialize_slave(dmix, spcm, params);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to initialize slave");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
dmix->spcm = spcm;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->shmptr->use_server) {
|
|
Packit Service |
db8eaa |
dmix->server_free = dmix_server_free;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_server_create(dmix);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to create server");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
dmix->shmptr->type = spcm->type;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
if (dmix->shmptr->use_server) {
|
|
Packit Service |
db8eaa |
/* up semaphore to avoid deadlock */
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_client_connect(dmix);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to connect client");
|
|
Packit Service |
db8eaa |
goto _err_nosem;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_open_secondary_client(&spcm, dmix, "dmix_client");
|
|
Packit Service |
db8eaa |
if (ret < 0)
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_open_slave(&spcm, root, sconf, stream,
|
|
Packit Service |
db8eaa |
mode | SND_PCM_NONBLOCK |
|
|
Packit Service |
db8eaa |
SND_PCM_APPEND,
|
|
Packit Service |
db8eaa |
NULL);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
/* all other streams have been closed;
|
|
Packit Service |
db8eaa |
* retry as the first instance
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
if (ret == -EBADFD) {
|
|
Packit Service |
db8eaa |
first_instance = 1;
|
|
Packit Service |
db8eaa |
goto retry;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
SNDERR("unable to open slave");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) {
|
|
Packit Service |
db8eaa |
SNDERR("dmix plugin can be only connected to hw plugin");
|
|
Packit Service |
db8eaa |
ret = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_initialize_secondary_slave(dmix, spcm, params);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to initialize slave");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
dmix->spcm = spcm;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = shm_sum_create_or_connect(dmix);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to initialize sum ring buffer");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ret = snd_pcm_direct_initialize_poll_fd(dmix);
|
|
Packit Service |
db8eaa |
if (ret < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("unable to initialize poll_fd");
|
|
Packit Service |
db8eaa |
goto _err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
mix_select_callbacks(dmix);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
pcm->poll_fd = dmix->poll_fd;
|
|
Packit Service |
db8eaa |
pcm->poll_events = POLLIN; /* it's different than other plugins */
|
|
Packit Service |
db8eaa |
pcm->tstamp_type = spcm->tstamp_type;
|
|
Packit Service |
db8eaa |
pcm->mmap_rw = 1;
|
|
Packit Service |
db8eaa |
snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0);
|
|
Packit Service |
db8eaa |
snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (dmix->channels == UINT_MAX)
|
|
Packit Service |
db8eaa |
dmix->channels = dmix->shmptr->s.channels;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
*pcmp = pcm;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
_err:
|
|
Packit Service |
db8eaa |
if (dmix->timer)
|
|
Packit Service |
db8eaa |
snd_timer_close(dmix->timer);
|
|
Packit Service |
db8eaa |
if (dmix->server)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_server_discard(dmix);
|
|
Packit Service |
db8eaa |
if (dmix->client)
|
|
Packit Service |
db8eaa |
snd_pcm_direct_client_discard(dmix);
|
|
Packit Service |
db8eaa |
if (spcm)
|
|
Packit Service |
db8eaa |
snd_pcm_close(spcm);
|
|
Packit Service |
db8eaa |
if (dmix->u.dmix.shmid_sum >= 0)
|
|
Packit Service |
db8eaa |
shm_sum_discard(dmix);
|
|
Packit Service |
db8eaa |
if ((dmix->shmid >= 0) && (snd_pcm_direct_shm_discard(dmix))) {
|
|
Packit Service |
db8eaa |
if (snd_pcm_direct_semaphore_discard(dmix))
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_final(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
} else
|
|
Packit Service |
db8eaa |
snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
|
|
Packit Service |
db8eaa |
_err_nosem:
|
|
Packit Service |
db8eaa |
if (dmix) {
|
|
Packit Service |
db8eaa |
free(dmix->bindings);
|
|
Packit Service |
db8eaa |
free(dmix);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (pcm)
|
|
Packit Service |
db8eaa |
snd_pcm_free(pcm);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*! \page pcm_plugins
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\section pcm_plugins_dmix Plugin: dmix
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
This plugin provides direct mixing of multiple streams. The resolution
|
|
Packit Service |
db8eaa |
for 32-bit mixing is only 24-bit. The low significant byte is filled with
|
|
Packit Service |
db8eaa |
zeros. The extra 8 bits are used for the saturation.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.name {
|
|
Packit Service |
db8eaa |
type dmix # Direct mix
|
|
Packit Service |
db8eaa |
ipc_key INT # unique IPC key
|
|
Packit Service |
db8eaa |
ipc_key_add_uid BOOL # add current uid to unique IPC key
|
|
Packit Service |
db8eaa |
ipc_perm INT # IPC permissions (octal, default 0600)
|
|
Packit Service |
db8eaa |
hw_ptr_alignment STR # Slave application and hw pointer alignment type
|
|
Packit Service |
db8eaa |
# STR can be one of the below strings :
|
|
Packit Service |
db8eaa |
# no
|
|
Packit Service |
db8eaa |
# roundup
|
|
Packit Service |
db8eaa |
# rounddown
|
|
Packit Service |
db8eaa |
# auto (default)
|
|
Packit Service |
db8eaa |
tstamp_type STR # timestamp type
|
|
Packit Service |
db8eaa |
# STR can be one of the below strings :
|
|
Packit Service |
db8eaa |
# default, gettimeofday, monotonic, monotonic_raw
|
|
Packit Service |
db8eaa |
slave STR
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
slave { # Slave definition
|
|
Packit Service |
db8eaa |
pcm STR # slave PCM name
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
pcm { } # slave PCM definition
|
|
Packit Service |
db8eaa |
format STR # format definition
|
|
Packit Service |
db8eaa |
rate INT # rate definition
|
|
Packit Service |
db8eaa |
channels INT
|
|
Packit Service |
db8eaa |
period_time INT # in usec
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
period_size INT # in frames
|
|
Packit Service |
db8eaa |
buffer_time INT # in usec
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
buffer_size INT # in frames
|
|
Packit Service |
db8eaa |
periods INT # when buffer_size or buffer_time is not specified
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
bindings { # note: this is client independent!!!
|
|
Packit Service |
db8eaa |
N INT # maps slave channel to client channel N
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slowptr BOOL # slow but more precise pointer updates
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
ipc_key specfies the unique IPC key in integer.
|
|
Packit Service |
db8eaa |
This number must be unique for each different dmix definition,
|
|
Packit Service |
db8eaa |
since the shared memory is created with this key number.
|
|
Packit Service |
db8eaa |
When ipc_key_add_uid is set true, the uid value is
|
|
Packit Service |
db8eaa |
added to the value set in ipc_key . This will
|
|
Packit Service |
db8eaa |
avoid the confliction of the same IPC key with different users
|
|
Packit Service |
db8eaa |
concurrently.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
hw_ptr_alignment specifies slave application and hw
|
|
Packit Service |
db8eaa |
pointer alignment type. By default hw_ptr_alignment is auto. Below are
|
|
Packit Service |
db8eaa |
the possible configurations:
|
|
Packit Service |
db8eaa |
- no: minimal latency with minimal frames dropped at startup. But
|
|
Packit Service |
db8eaa |
wakeup of application (return from snd_pcm_wait() or poll()) can
|
|
Packit Service |
db8eaa |
take up to 2 * period.
|
|
Packit Service |
db8eaa |
- roundup: It is guaranteed that all frames will be played at
|
|
Packit Service |
db8eaa |
startup. But the latency will increase upto period-1 frames.
|
|
Packit Service |
db8eaa |
- rounddown: It is guaranteed that a wakeup will happen for each
|
|
Packit Service |
db8eaa |
period and frames can be written from application. But on startup
|
|
Packit Service |
db8eaa |
upto period-1 frames will be dropped.
|
|
Packit Service |
db8eaa |
- auto: Selects the best approach depending on the used period and
|
|
Packit Service |
db8eaa |
buffer size.
|
|
Packit Service |
db8eaa |
If the application buffer size is < 2 * application period,
|
|
Packit Service |
db8eaa |
"roundup" will be selected to avoid under runs. If the slave_period
|
|
Packit Service |
db8eaa |
is < 10ms we could expect that there are low latency
|
|
Packit Service |
db8eaa |
requirements. Therefore "rounddown" will be chosen to avoid long
|
|
Packit Service |
db8eaa |
wakeup times. Such wakeup delay could otherwise end up with Xruns in
|
|
Packit Service |
db8eaa |
case of a dependency to another sound device (e.g. forwarding of
|
|
Packit Service |
db8eaa |
microphone to speaker). Else "no" will be chosen.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
Note that the dmix plugin itself supports only a single configuration.
|
|
Packit Service |
db8eaa |
That is, it supports only the fixed rate (default 48000), format
|
|
Packit Service |
db8eaa |
(\c S16), channels (2), and period_time (125000).
|
|
Packit Service |
db8eaa |
For using other configuration, you have to set the value explicitly
|
|
Packit Service |
db8eaa |
in the slave PCM definition. The rate, format and channels can be
|
|
Packit Service |
db8eaa |
covered by an additional \ref pcm_plugins_dmix "plug plugin",
|
|
Packit Service |
db8eaa |
but there is only one base configuration, anyway.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
An example configuration for setting 44100 Hz, \c S32_LE format
|
|
Packit Service |
db8eaa |
as the slave PCM of "hw:0" is like below:
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.dmix_44 {
|
|
Packit Service |
db8eaa |
type dmix
|
|
Packit Service |
db8eaa |
ipc_key 321456 # any unique value
|
|
Packit Service |
db8eaa |
ipc_key_add_uid true
|
|
Packit Service |
db8eaa |
slave {
|
|
Packit Service |
db8eaa |
pcm "hw:0"
|
|
Packit Service |
db8eaa |
format S32_LE
|
|
Packit Service |
db8eaa |
rate 44100
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
You can hear 48000 Hz samples still using this dmix pcm via plug plugin
|
|
Packit Service |
db8eaa |
like:
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
% aplay -Dplug:dmix_44 foo_48k.wav
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
For using the dmix plugin for OSS emulation device, you have to set
|
|
Packit Service |
db8eaa |
the period and the buffer sizes in power of two. For example,
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.dmixoss {
|
|
Packit Service |
db8eaa |
type dmix
|
|
Packit Service |
db8eaa |
ipc_key 321456 # any unique value
|
|
Packit Service |
db8eaa |
ipc_key_add_uid true
|
|
Packit Service |
db8eaa |
slave {
|
|
Packit Service |
db8eaa |
pcm "hw:0"
|
|
Packit Service |
db8eaa |
period_time 0
|
|
Packit Service |
db8eaa |
period_size 1024 # must be power of 2
|
|
Packit Service |
db8eaa |
buffer_size 8192 # ditto
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
period_time 0 must be set, too, for resetting the
|
|
Packit Service |
db8eaa |
default value. In the case of soundcards with multi-channel IO,
|
|
Packit Service |
db8eaa |
adding the bindings would help
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.dmixoss {
|
|
Packit Service |
db8eaa |
...
|
|
Packit Service |
db8eaa |
bindings {
|
|
Packit Service |
db8eaa |
0 0 # map from 0 to 0
|
|
Packit Service |
db8eaa |
1 1 # map from 1 to 1
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
so that only the first two channels are used by dmix.
|
|
Packit Service |
db8eaa |
Also, note that ICE1712 have the limited buffer size, 5513 frames
|
|
Packit Service |
db8eaa |
(corresponding to 640 kB). In this case, reduce the buffer_size
|
|
Packit Service |
db8eaa |
to 4096.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\subsection pcm_plugins_dmix_funcref Function reference
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_dmix_open()
|
|
Packit Service |
db8eaa |
_snd_pcm_dmix_open()
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Creates a new dmix PCM
|
|
Packit Service |
db8eaa |
* \param pcmp Returns created PCM handle
|
|
Packit Service |
db8eaa |
* \param name Name of PCM
|
|
Packit Service |
db8eaa |
* \param root Root configuration node
|
|
Packit Service |
db8eaa |
* \param conf Configuration node with dmix PCM description
|
|
Packit Service |
db8eaa |
* \param stream PCM Stream
|
|
Packit Service |
db8eaa |
* \param mode PCM Mode
|
|
Packit Service |
db8eaa |
* \warning Using of this function might be dangerous in the sense
|
|
Packit Service |
db8eaa |
* of compatibility reasons. The prototype might be freely
|
|
Packit Service |
db8eaa |
* changed in future.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit Service |
db8eaa |
snd_config_t *root, snd_config_t *conf,
|
|
Packit Service |
db8eaa |
snd_pcm_stream_t stream, int mode)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_config_t *sconf;
|
|
Packit Service |
db8eaa |
struct slave_params params;
|
|
Packit Service |
db8eaa |
struct snd_pcm_direct_open_conf dopen;
|
|
Packit Service |
db8eaa |
int bsize, psize;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* the default settings, it might be invalid for some hardware */
|
|
Packit Service |
db8eaa |
params.format = SND_PCM_FORMAT_S16;
|
|
Packit Service |
db8eaa |
params.rate = 48000;
|
|
Packit Service |
db8eaa |
params.channels = 2;
|
|
Packit Service |
db8eaa |
params.period_time = -1;
|
|
Packit Service |
db8eaa |
params.buffer_time = -1;
|
|
Packit Service |
db8eaa |
bsize = psize = -1;
|
|
Packit Service |
db8eaa |
params.periods = 3;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = snd_pcm_slave_conf(root, dopen.slave, &sconf, 8,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, ¶ms.format,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_RATE, 0, ¶ms.rate,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_CHANNELS, 0, ¶ms.channels,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_PERIOD_TIME, 0, ¶ms.period_time,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_BUFFER_TIME, 0, ¶ms.buffer_time,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &psize,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &bsize,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_PERIODS, 0, ¶ms.periods);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* set a reasonable default */
|
|
Packit Service |
db8eaa |
if (psize == -1 && params.period_time == -1)
|
|
Packit Service |
db8eaa |
params.period_time = 125000; /* 0.125 seconds */
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (params.format == -2)
|
|
Packit Service |
db8eaa |
params.format = SND_PCM_FORMAT_UNKNOWN;
|
|
Packit Service |
db8eaa |
else if (!(dmix_supported_format & (1ULL << params.format))) {
|
|
Packit Service |
db8eaa |
/* sorry, limited features */
|
|
Packit Service |
db8eaa |
SNDERR("Unsupported format");
|
|
Packit Service |
db8eaa |
snd_config_delete(sconf);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
params.period_size = psize;
|
|
Packit Service |
db8eaa |
params.buffer_size = bsize;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = snd_pcm_dmix_open(pcmp, name, &dopen, ¶ms,
|
|
Packit Service |
db8eaa |
root, sconf, stream, mode);
|
|
Packit Service |
db8eaa |
snd_config_delete(sconf);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
SND_DLSYM_BUILD_VERSION(_snd_pcm_dmix_open, SND_PCM_DLSYM_VERSION);
|
|
Packit Service |
db8eaa |
#endif
|