[alsa-devel] [PATCH] Per Application Volume Control plugin for ALSA
Takashi Iwai
tiwai at suse.de
Sun Oct 17 10:55:57 CEST 2010
At Wed, 29 Sep 2010 18:56:59 +0200,
James Huk wrote:
>
> Hello everybody.
>
> This is my first "post" here, so please be "gentle".
>
> I have created PAVC plugin for ALSA, it is based on COPY plugin, and
> use SOFTVOL plugin for sound output. As far as I can tell from current
> tests - It works perfectly, and it doesn't break compatibility with
> any apps I use (and PulseAudio does). So I was wondering - is there
> any chance this plugin would be added to the next official ALSA
> release?
>
> Here is the patch that enables it on current alsa-lib-1.0.23
> (hopefully this mailing list is a good place to "post" this):
Thanks for the patch. I looked over this post until now.
The idea is interesting, and I thought of a similar thing before PA
gets popularized, too.
Though, this implementation is a bit too hackish, especially the part
using pipe & co. If we have a more simpler and cleaner way for this,
it's worth including to the standard plugin, I think.
thanks,
Takashi
>
>
> --- a/configure.in 2010-09-23 17:03:00.000000000 +0200
> +++ b/configure.in 2010-09-23 17:09:11.029900400 +0200
> @@ -445,7 +445,7 @@
> pcm_plugins=""
> fi
>
> -PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi
> shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop
> asym iec958 softvol extplug ioplug mmap_emul"
> +PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi
> shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop
> asym iec958 softvol extplug ioplug mmap_emul pavc"
>
> build_pcm_plugin="no"
> for t in $PCM_PLUGIN_LIST; do
> @@ -516,6 +516,7 @@
> AM_CONDITIONAL(BUILD_PCM_PLUGIN_EXTPLUG, test x$build_pcm_extplug = xyes)
> AM_CONDITIONAL(BUILD_PCM_PLUGIN_IOPLUG, test x$build_pcm_ioplug = xyes)
> AM_CONDITIONAL(BUILD_PCM_PLUGIN_MMAP_EMUL, test x$build_pcm_mmap_emul = xyes)
> +AM_CONDITIONAL(BUILD_PCM_PLUGIN_PAVC, test x$build_pcm_pavc = xyes)
>
> dnl Defines for plug plugin
> if test "$build_pcm_rate" = "yes"; then
>
> --- a/src/pcm/Makefile.am 2010-04-16 13:11:05.000000000 +0200
> +++ b/src/pcm/Makefile.am 2010-09-23 17:20:10.601899695 +0200
> @@ -102,6 +102,9 @@
> if BUILD_PCM_PLUGIN_MMAP_EMUL
> libpcm_la_SOURCES += pcm_mmap_emul.c
> endif
> +if BUILD_PCM_PLUGIN_PAVC
> +libpcm_la_SOURCES += pcm_pavc.c
> +endif
>
> EXTRA_DIST = pcm_dmix_i386.c pcm_dmix_x86_64.c pcm_dmix_generic.c
>
>
>
> --- a/src/pcm/pcm.c 2010-04-16 13:11:05.000000000 +0200
> +++ b/src/pcm/pcm.c 2010-09-23 17:10:43.813900406 +0200
> @@ -2047,7 +2047,7 @@
> static const char *const build_in_pcms[] = {
> "adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat",
> "linear", "meter", "mulaw", "multi", "null", "empty", "plug",
> "rate", "route", "share",
> - "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul",
> + "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul","pavc",
> NULL
> };
>
> --- a/include/pcm.h 2010-04-16 13:11:05.000000000 +0200
> +++ b/include/pcm.h 2010-09-23 17:11:58.389899158 +0200
> @@ -376,7 +376,9 @@
> SND_PCM_TYPE_EXTPLUG,
> /** Mmap-emulation plugin */
> SND_PCM_TYPE_MMAP_EMUL,
> - SND_PCM_TYPE_LAST = SND_PCM_TYPE_MMAP_EMUL
> + /** PAVC plugin */
> + SND_PCM_TYPE_PAVC,
> + SND_PCM_TYPE_LAST = SND_PCM_TYPE_PAVC
> };
>
> /** PCM type */
>
> --- a/src/pcm/pcm_pavc.c
> +++ b/src/pcm/pcm_pavc.c
> @@ -0,0 +1,846 @@
> +/*
> + * PCM - PAVC plugin
> + * Based on PCM - Copy conversion plugin by Abramo Bagnara
> <abramo at alsa-project.org>
> + *
> + * Copyright (c) 2010 by Adrian Kobyliński <huk256 at gmail.com>
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + */
> +
> +#include <byteswap.h>
> +#include "pcm_local.h"
> +#include "pcm_plugin.h"
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/wait.h>
> +
> +
> +#ifndef PIC
> +/* entry for static linking */
> +const char *_snd_module_pcm_pavc = "";
> +#endif
> +
> +#ifndef DOC_HIDDEN
> +typedef struct {
> + /* This field need to be the first */
> + snd_pcm_plugin_t plug;
> +} snd_pcm_pavc_t;
> +#endif
> +
> +static int snd_pcm_pavc_hw_refine_cprepare(snd_pcm_t *pcm
> ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
> +{
> + int err;
> + snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
> + err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
> + &access_mask);
> + if (err < 0)
> + return err;
> + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
> + return 0;
> +}
> +
> +static int snd_pcm_pavc_hw_refine_sprepare(snd_pcm_t *pcm
> ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *sparams)
> +{
> + snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
> + _snd_pcm_hw_params_any(sparams);
> + _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
> + &saccess_mask);
> + return 0;
> +}
> +
> +static int snd_pcm_pavc_hw_refine_schange(snd_pcm_t *pcm
> ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
> + snd_pcm_hw_params_t *sparams)
> +{
> + int err;
> + unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
> + err = _snd_pcm_hw_params_refine(sparams, links, params);
> + if (err < 0)
> + return err;
> + return 0;
> +}
> +
> +static int snd_pcm_pavc_hw_refine_cchange(snd_pcm_t *pcm
> ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
> + snd_pcm_hw_params_t *sparams)
> +{
> + int err;
> + unsigned int links = ~SND_PCM_HW_PARBIT_ACCESS;
> + err = _snd_pcm_hw_params_refine(params, links, sparams);
> + if (err < 0)
> + return err;
> + return 0;
> +}
> +
> +static int snd_pcm_pavc_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
> +{
> + return snd_pcm_hw_refine_slave(pcm, params,
> + snd_pcm_pavc_hw_refine_cprepare,
> + snd_pcm_pavc_hw_refine_cchange,
> + snd_pcm_pavc_hw_refine_sprepare,
> + snd_pcm_pavc_hw_refine_schange,
> + snd_pcm_generic_hw_refine);
> +}
> +
> +static int snd_pcm_pavc_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
> +{
> + return snd_pcm_hw_params_slave(pcm, params,
> + snd_pcm_pavc_hw_refine_cchange,
> + snd_pcm_pavc_hw_refine_sprepare,
> + snd_pcm_pavc_hw_refine_schange,
> + snd_pcm_generic_hw_params);
> +}
> +
> +static snd_pcm_uframes_t
> + snd_pcm_pavc_write_areas(snd_pcm_t *pcm,
> + const snd_pcm_channel_area_t *areas,
> + snd_pcm_uframes_t offset,
> + snd_pcm_uframes_t size,
> + const snd_pcm_channel_area_t *slave_areas,
> + snd_pcm_uframes_t slave_offset,
> + snd_pcm_uframes_t *slave_sizep)
> +{
> + if (size > *slave_sizep)
> + size = *slave_sizep;
> + snd_pcm_areas_copy(slave_areas, slave_offset,
> + areas, offset,
> + pcm->channels, size, pcm->format);
> + *slave_sizep = size;
> + return size;
> +}
> +
> +static snd_pcm_uframes_t
> + snd_pcm_pavc_read_areas(snd_pcm_t *pcm,
> + const snd_pcm_channel_area_t *areas,
> + snd_pcm_uframes_t offset,
> + snd_pcm_uframes_t size,
> + const snd_pcm_channel_area_t *slave_areas,
> + snd_pcm_uframes_t slave_offset,
> + snd_pcm_uframes_t *slave_sizep)
> +{
> + if (size > *slave_sizep)
> + size = *slave_sizep;
> + snd_pcm_areas_copy(areas, offset,
> + slave_areas, slave_offset,
> + pcm->channels, size, pcm->format);
> + *slave_sizep = size;
> + return size;
> +}
> +
> +static void snd_pcm_pavc_dump(snd_pcm_t *pcm, snd_output_t *out)
> +{
> + snd_pcm_pavc_t *pavc = pcm->private_data;
> + snd_output_printf(out, "Copy conversion PCM\n");
> + if (pcm->setup) {
> + snd_output_printf(out, "Its setup is:\n");
> + snd_pcm_dump_setup(pcm, out);
> + }
> + snd_output_printf(out, "Slave: ");
> + snd_pcm_dump(pavc->plug.gen.slave, out);
> +}
> +
> +static const snd_pcm_ops_t snd_pcm_pavc_ops = {
> + .close = snd_pcm_generic_close,
> + .info = snd_pcm_generic_info,
> + .hw_refine = snd_pcm_pavc_hw_refine,
> + .hw_params = snd_pcm_pavc_hw_params,
> + .hw_free = snd_pcm_generic_hw_free,
> + .sw_params = snd_pcm_generic_sw_params,
> + .channel_info = snd_pcm_generic_channel_info,
> + .dump = snd_pcm_pavc_dump,
> + .nonblock = snd_pcm_generic_nonblock,
> + .async = snd_pcm_generic_async,
> + .mmap = snd_pcm_generic_mmap,
> + .munmap = snd_pcm_generic_munmap,
> +};
> +
> +/**
> + * \brief Creates a new copy PCM
> + * \param pcmp Returns created PCM handle
> + * \param name Name of PCM
> + * \param slave Slave PCM handle
> + * \param close_slave When set, the slave PCM handle is closed with copy PCM
> + * \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_pavc_open(snd_pcm_t **pcmp, const char *name, snd_pcm_t
> *slave, int close_slave)
> +{
> + snd_pcm_t *pcm;
> + snd_pcm_pavc_t *pavc;
> + int err;
> + assert(pcmp && slave);
> + pavc = calloc(1, sizeof(snd_pcm_pavc_t));
> + if (!pavc) {
> + return -ENOMEM;
> + }
> + snd_pcm_plugin_init(&pavc->plug);
> + pavc->plug.read = snd_pcm_pavc_read_areas;
> + pavc->plug.write = snd_pcm_pavc_write_areas;
> + pavc->plug.undo_read = snd_pcm_plugin_undo_read_generic;
> + pavc->plug.undo_write = snd_pcm_plugin_undo_write_generic;
> + pavc->plug.gen.slave = slave;
> + pavc->plug.gen.close_slave = close_slave;
> +
> + err = snd_pcm_new(&pcm, SND_PCM_TYPE_PAVC, name, slave->stream,
> slave->mode);
> + if (err < 0) {
> + free(pavc);
> + return err;
> + }
> + pcm->ops = &snd_pcm_pavc_ops;
> + pcm->fast_ops = &snd_pcm_plugin_fast_ops;
> + pcm->private_data = pavc;
> + pcm->poll_fd = slave->poll_fd;
> + pcm->poll_events = slave->poll_events;
> + pcm->monotonic = slave->monotonic;
> + snd_pcm_set_hw_ptr(pcm, &pavc->plug.hw_ptr, -1, 0);
> + snd_pcm_set_appl_ptr(pcm, &pavc->plug.appl_ptr, -1, 0);
> + *pcmp = pcm;
> +
> + return 0;
> +}
> +
> +int countString(char * x,char x2,int count)
> +{
> + int i;
> + int z=0;
> + for(i=0;i<count;++i)
> + {
> + if(x[i]==x2)
> + {
> + ++z;
> + }
> + }
> + return z;
> +}
> +
> +char *deleteChar(char *x, char x2, int count)
> +{
> + char *ret;
> + ret=(char*) malloc(count+1);
> + int i;
> + for(i=0;i<count;++i)
> + {
> + ret[i]=0;
> + }
> + for(i=0;i<count;++i)
> + {
> + if(x[i]!=x2)
> + {
> + ret[i]=x[i];
> + }
> + else
> + {
> + ret[i]=' ';
> + }
> + }
> + return ret;
> +}
> +
> +char *getProceses()
> +{
> + FILE *pipe;
> + if ( !(pipe = (FILE*)popen("lsof -F p /dev/snd/pcmC0D0p","r")) )
> + { // If fpipe is NULL
> + perror("Problems with pipe");
> + return NULL;
> + }
> +
> + int i=0,j=0;
> + int c;
> +
> + do {
> + c = fgetc (pipe);
> + ++i;
> + } while (c != EOF);
> + fclose (pipe);
> +
> + char *buf=(char*)malloc(sizeof(char)*i);
> +
> + for(j=0;j<i;++j)
> + {
> + buf[j]=0;
> + }
> +
> + i=0;
> +
> + if ( !(pipe = (FILE*)popen("lsof -F p /dev/snd/pcmC0D0p","r")) )
> + { // If fpipe is NULL
> + perror("Problems with pipe");
> + return NULL;
> + }
> +
> + do {
> + c = fgetc (pipe);
> + if(c!=EOF && c!='p' && c!='\n')
> + {
> + buf[i]=c;
> + }
> + else if(c=='p')
> + {
> + buf[i]=' ';
> + }
> + else if(c=='\n')
> + {
> + buf[i]=',';
> + }
> + ++i;
> + } while (c != EOF);
> + fclose (pipe);
> +
> + return buf;
> +}
> +
> +char *loadPid(char *name)
> +{
> + FILE *file;
> + int c,i=0,j=0;
> +
> + file=fopen(name,"r");
> + if(file!=NULL)
> + {
> + do{
> + c=fgetc(file);
> + ++i;
> + }while(c!=EOF);
> + fclose(file);
> +
> + char *buf=malloc((sizeof(char))*i);
> + for(j=0;j<i;++j)
> + {
> + buf[j]=0;
> + }
> +
> + i=0;
> +
> + file=fopen(name,"r");
> + if(file!=NULL)
> + {
> + do{
> + c=fgetc(file);
> + if(c!=EOF && c!='\n')
> + buf[i]=c;
> + ++i;
> + }while(c!=EOF);
> + fclose(file);
> +
> + return buf;
> + }
> + else
> + {
> + return NULL;
> + }
> +
> + }
> + else
> + {
> + return NULL;
> + }
> +}
> +
> +void savePid(char *pid,char *file)
> +{
> + int i;
> + char command3[256];
> +
> + for(i=0;i<256;++i)
> + {
> + command3[i]=0;
> + }
> +
> + strcat(command3,"echo ");
> + strcat(command3,pid);
> + strcat(command3," > ");
> + strcat(command3,file);
> +
> + for(i=5;i<256;++i)
> + {
> + if(command3[i]!=EOF && command3[i]!='\n' && command3[i]!='*')
> + {
> + command3[i]=command3[i];
> + }
> + else
> + {
> + command3[i]=' ';
> + }
> + }
> +
> + system(command3);
> +}
> +
> +int checkDir(char *name)
> +{
> + char command[128];
> + memset(command,0,128);
> +
> + strcat(command,"ls -aF ");
> + strcat(command,getenv("HOME"));
> + strcat(command," | grep \\./$ | grep -w ");
> + strcat(command,name);
> +
> + FILE *pipe;
> + if ( !(pipe = (FILE*)popen(command,"r")) )
> + {
> + perror("Problems with pipe");
> + return -1;
> + }
> +
> + int i=0;
> + int c;
> +
> + do {
> + c = fgetc (pipe);
> + if(c!=EOF && c!='\n')
> + ++i;
> + } while (c != EOF);
> + fclose (pipe);
> +
> + if(i>0)
> + {
> + return 1;
> + }
> + else
> + {
> + return 0;
> + }
> +}
> +
> +int createDir(char *name)
> +{
> + pid_t PID;
> + int status;
> +
> + PID=fork();
> +
> + if(PID>0)
> + {
> + char command[128];
> + memset(command,0,128);
> +
> + strcat(command,getenv("HOME"));
> + strcat(command,"/");
> + strcat(command,".softvol");
> +
> + execlp("mkdir","mkdir",command,NULL);
> + }
> + else
> + {
> + pid_t wpid = waitpid(PID, &status, WUNTRACED);
> + }
> +
> + return WEXITSTATUS(status);
> +}
> +
> +char *getDir(char *name)
> +{
> + static char command[128];
> + memset(command,0,128);
> +
> + strcat(command,getenv("HOME"));
> + strcat(command,"/");
> + strcat(command,name);
> +
> + return command;
> +}
> +
> +char *getSoftvolNumber(int channelCount,int current)
> +{
> + size_t i;
> + static char ret[4];
> + memset(ret,0,5);
> + char buf1[4],buf2[4];
> + sprintf(buf1,"%i",channelCount);
> + sprintf(buf2,"%i",current);
> +
> + if(strlen(buf2)<strlen(buf1))
> + {
> + for(i=0;i<(strlen(buf1)-1);++i)
> + {
> + ret[i]='0';
> + }
> +
> + strcat(ret,buf2);
> +
> + return ret;
> + }
> + else if(strlen(buf2)==strlen(buf1))
> + {
> + strcat(ret,buf2);
> + return ret;
> + }
> + else
> + {
> + //this should never happen
> + return NULL;
> + }
> +}
> +
> +int checkSoftvolProcess(int channelcount)
> +{
> + int i=0,c=0,ret=channelcount-1,j=0;
> + char nameBuf[1024];
> + char pidBuf[32];
> + char numBuf[4];
> + char command[128];
> + char fileName[64];
> +
> + FILE *fpipe;
> +
> + for(i=0;i<channelcount;++i)
> + {
> + memset(nameBuf,0,1024);
> + memset(numBuf,0,4);
> + memset(command,0,128);
> + memset(fileName,0,64);
> +
> + strcat(command,"ps -A -o pid= | grep -w -f ");
> + strcat(command,getDir(".softvol"));
> + strcat(command,"/SOFTVOL");
> + strcat(command,getSoftvolNumber(channelcount,i));
> + strcat(command,".pid");
> +
> + strcat(fileName,getDir(".softvol"));
> + strcat(fileName,"/SOFTVOL");
> + strcat(fileName,getSoftvolNumber(channelcount,i));
> + strcat(fileName,".pid");
> +
> + //Here we check if current process is already conected to something
> + FILE *temp=fopen(fileName,"r");
> + if(temp!=NULL)
> + {
> + j=0;
> + do{
> + c=fgetc(temp);
> + if(c!=EOF && c!='\n')
> + {
> + nameBuf[j]=c;
> + }
> + else
> + {
> + nameBuf[j]='\0';
> + }
> +
> + ++j;
> + }while(c!=EOF);
> + }
> + else
> + {
> + memset(nameBuf,0,1024);
> + }
> +
> + sprintf(pidBuf,"%d",getpid());
> +
> + if(strcmp(nameBuf,pidBuf)==0)
> + {
> + //Current process wants to re-initialize the sound - so
> we connect it
> + //to the output it is currently using
> + ret=i;
> + break;
> + }
> + else
> + {
> + //Current process is not connected to anything - so we connect it
> + //to the firs unused softvol output
> + memset(fileName,0,64);
> + }
> + }
> +
> + if(fileName[0]!=0)
> + {
> + //Current process is already using some softvol output - so we
> + //reconnect it to this output
> + for(i=0;i<channelcount;++i)
> + {
> + memset(numBuf,0,4);
> + memset(command,0,128);
> + memset(fileName,0,64);
> +
> + strcat(command,"ps -A -o pid= | grep -w -f ");
> + strcat(command,getDir(".softvol"));
> + strcat(command,"/SOFTVOL");
> + strcat(command,getSoftvolNumber(channelcount,i));
> + strcat(command,".pid 2> /dev/null");
> +
> + strcat(fileName,getDir(".softvol"));
> + strcat(fileName,"/SOFTVOL");
> + strcat(fileName,getSoftvolNumber(channelcount,i));
> + strcat(fileName,".pid");
> +
> + //Here we check if process is alive...
> + if ( !(fpipe = (FILE*)popen(command,"r")) )
> + { // If fpipe is NULL
> + perror("Problems with pipe");
> + }
> +
> + j=0;
> +
> + do {
> + c = fgetc (fpipe);
> + if(c!=EOF)
> + ++j;
> + } while (c != EOF);
> + fclose (fpipe);
> +
> + //...and if it is the current process
> + if(j>0)
> + {
> + FILE *temp=fopen(fileName,"r");
> + j=0;
> + do{
> + c=fgetc(temp);
> + if(c!=EOF && c!='\n')
> + {
> + nameBuf[j]=c;
> + }
> + else
> + {
> + nameBuf[j]='\0';
> + }
> +
> + ++j;
> + }while(c!=EOF);
> +
> + char pidBuf[32];
> + sprintf(pidBuf,"%d",getpid());
> +
> + if(strcmp(nameBuf,pidBuf)==0)
> + {
> + //Current process wants to re-initialize the
> sound - so we connect it
> + //to the output it is currently using
> + ret=i;
> + break;
> + }
> + else
> + {
> + //It is some other process - so we connect it
> + //to the first unused softvol output
> + ret=i+1;
> + }
> + }
> + else
> + {
> + ret=i;
> + }
> + }
> + }
> + else
> + {
> + //Process isn't using any softvol output - so we connect it
> + //to the first unused on the list
> +
> + for(i=0;i<channelcount;++i)
> + {
> + memset(numBuf,0,4);
> + memset(command,0,128);
> + memset(fileName,0,64);
> +
> + strcat(command,"ps -A -o pid= | grep -w -f ");
> + strcat(command,getDir(".softvol"));
> + strcat(command,"/SOFTVOL");
> + strcat(command,getSoftvolNumber(channelcount,i));
> + strcat(command,".pid 2> /dev/null");
> +
> + strcat(fileName,getDir(".softvol"));
> + strcat(fileName,"/SOFTVOL");
> + strcat(fileName,getSoftvolNumber(channelcount,i));
> + strcat(fileName,".pid");
> +
> + //Here we check if process is alive...
> + if ( !(fpipe = (FILE*)popen(command,"r")) )
> + { // If fpipe is NULL
> + perror("Problems with pipe");
> + }
> +
> + j=0;
> +
> + do {
> + c = fgetc (fpipe);
> + if(c!=EOF)
> + ++j;
> + } while (c != EOF);
> + fclose (fpipe);
> +
> + //...and if it is the current process
> + if(j>0)
> + {
> +
> + }
> + else
> + {
> + ret=i;
> + break;
> + }
> + }
> + }
> + return ret;
> +}
> +
> +/*! \page pcm_plugins
> +
> +\section pcm_plugins_copy Plugin: copy
> +
> +This plugin copies samples from master copy PCM to given slave PCM.
> +The channel count, format and rate must match for both of them.
> +
> +\code
> +pcm.name {
> + type copy # Copy PCM
> + slave STR # Slave name
> + # or
> + slave { # Slave definition
> + pcm STR # Slave PCM name
> + # or
> + pcm { } # Slave PCM definition
> + }
> +}
> +\endcode
> +
> +\subsection pcm_plugins_copy_funcref Function reference
> +
> +<UL>
> + <LI>snd_pcm_copy_open()
> + <LI>_snd_pcm_copy_open()
> +</UL>
> +
> +*/
> +
> +/**
> + * \brief Creates a new copy PCM
> + * \param pcmp Returns created PCM handle
> + * \param name Name of PCM
> + * \param root Root configuration node
> + * \param conf Configuration node with copy 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_pavc_open(snd_pcm_t **pcmp, const char *name,
> + snd_config_t *root, snd_config_t *conf,
> + snd_pcm_stream_t stream, int mode)
> +{
> + long channelcount=-1;
> + int ret=0;
> + char softvolName[64];
> + char pidBuf[32];
> + char fileName[64];
> + char helpBuf[32];
> +
> + memset(softvolName,0,64);
> +
> + snd_config_iterator_t i, next;
> + int err;
> + snd_pcm_t *spcm;
> + snd_config_t *slave = NULL, *sconf;
> +
> + snd_config_t *tempConfig;
> + err=snd_config_search(conf,"channelcount",&tempConfig);
> + if(err<0)
> + {
> + SNDERR("channelcount field not found");
> + return -ENOENT;
> + }
> + err=snd_config_get_integer(tempConfig,&channelcount);
> + if(err<0)
> + {
> + SNDERR("channelcount vaule must be integer");
> + return -EINVAL;
> + }
> +
> + snd_config_t *softvol[channelcount];
> +
> + int counter=0;
> +
> + sprintf(softvolName,"softvol%i",counter);
> +
> + 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, "slave") == 0)
> + {
> + slave = n;
> + continue;
> + }
> + if (strcmp(id, "channelcount") == 0)
> + {
> + continue;
> + }
> + if((counter<channelcount) && strcmp(id,softvolName)==0)
> + {
> + softvol[counter]=n;
> + ++counter;
> + sprintf(softvolName,"softvol%i",counter);
> + continue;
> + }
> + SNDERR("Unknown field %s", id);
> + return -EINVAL;
> + }
> +
> + if (!slave) {
> + SNDERR("slave is not defined");
> + return -EINVAL;
> + }
> +
> + if(checkDir(".softvol"))
> + {
> + //Directory exists - we do nothing
> + }
> + else
> + {
> + //Directory doesn't exists we need to create it
> + if((err=createDir(".softvol"))!=0)
> + {
> + SNDERR("Unable to create ~/.softvol directory - unknown
> error %i",err);
> + return err;
> + }
> + }
> +
> + ret=checkSoftvolProcess(channelcount);
> +
> + err = snd_pcm_slave_conf(root, softvol[ret], &sconf, 0);
> +
> + if (err < 0)
> + return err;
> + err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
> + snd_config_delete(sconf);
> + if (err < 0)
> + return err;
> + err = snd_pcm_pavc_open(pcmp, name, spcm, 1);
> + if (err < 0)
> + snd_pcm_close(spcm);
> +
> + sprintf(pidBuf,"%d",getpid());
> + memset(fileName,0,64);
> + strcat(fileName,getDir(".softvol"));
> + strcat(fileName,"/SOFTVOL");
> +
> + strcat(fileName,getSoftvolNumber(channelcount,ret));
> + strcat(fileName,".pid");
> +
> + savePid(pidBuf,fileName);
> +
> + return err;
> +}
> +#ifndef DOC_HIDDEN
> +SND_DLSYM_BUILD_VERSION(_snd_pcm_pavc_open, SND_PCM_DLSYM_VERSION);
> +#endif
>
>
>
>
> To test PAVC, we have to rebuild alsa-lib, and create .asoundrc or
> /etc/asound.conf that looks like this:
>
>
> pcm.!default {
> type plug
> slave.pcm "asymed"
> }
>
> pcm.asymed
> {
> type asym
> playback.pcm "pavcp"
> capture.pcm "dsnooped"
> }
>
> pcm.dmixer {
> type dmix
> ipc_key 1025
> slave {
> pcm "hw:0"
> period_time 0
> period_size 256
> #buffer_size 4096
> periods 128
> rate 44100
> }
> }
>
> pcm.dsnooped {
> type dsnoop
> ipc_key 1026
> slave
> {
> pcm "hw:0"
> channels 2
> period_size 256
> #buffer_size 4096
> rate 44100
> periods 0
> period_time 0
> }
> }
>
> pcm.softvol00 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol00"
> card 0
> }
> }
>
> pcm.softvol01 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol01"
> card 0
> }
> }
>
> pcm.softvol02 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol02"
> card 0
> }
> }
>
> pcm.softvol03 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol03"
> card 0
> }
> }
>
> pcm.softvol04 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol04"
> card 0
> }
> }
>
> pcm.softvol05 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol05"
> card 0
> }
> }
>
> pcm.softvol06 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol06"
> card 0
> }
> }
>
> pcm.softvol07 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol07"
> card 0
> }
> }
>
> pcm.softvol08 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol08"
> card 0
> }
> }
>
> pcm.softvol09 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol09"
> card 0
> }
> }
>
> pcm.softvol10 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol10"
> card 0
> }
> }
>
> pcm.softvol11 {
> type softvol
> slave {
> pcm "dmixer"
> }
> control {
> name "Softvol11"
> card 0
> }
> }
>
> ctl.dmixer {
> type hw
> card 0
> }
>
> ctl.dsnooped {
> type hw
> card 0
> }
>
>
> pcm.pavcp
> {
> type pavc
> slave.pcm "dmixer"
> channelcount 12
> softvol0.pcm "softvol00"
> softvol1.pcm "softvol01"
> softvol2.pcm "softvol02"
> softvol3.pcm "softvol03"
> softvol4.pcm "softvol04"
> softvol5.pcm "softvol05"
> softvol6.pcm "softvol06"
> softvol7.pcm "softvol07"
> softvol8.pcm "softvol08"
> softvol9.pcm "softvol09"
> softvol10.pcm "softvol10"
> softvol11.pcm "softvol11"
> }
>
>
> With this, anything connected to "default" will be automatically
> redirected to the first unused softvol channel. PIDs of apps that
> currently use softvol channels are saved to ~/.softvol/SOFTVOLXX.pid,
> so this can be checked at any time. If some application will try to
> restart only its sound subsystem, PAVC plugin will check those files
> to determine if it should be connected to the new softvol channel, or
> the one it currently uses. I tried to mimic the way this works with
> OSS4.
>
> Current limitations:
>
> -Since there is no "mute and slider at the same time" support in
> SOFTVOL plugin - there is no mute switch for software channels (or
> maybe there is a way I don't know about?)
>
> -Software channels must be named SOFTVOL00,01,02 and so on
>
> -SOFTVOL channels have both PLAYBACK and CAPTURE capabilities by
> default - this makes some mixers go haywire, and display double
> sliders - maybe there is a way around this I don't know about (like
> setting PLAYBACK only capability)?
>
> -Code of PAVC, could probably look nicer
>
> Hopefully someone will take a look at this, of course I am open to
> suggestions and critics.
>
> Best regards.
>
> P.S
>
> Sorry for my English ;]
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
More information about the Alsa-devel
mailing list