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
- Copyright (c) 2010 by Adrian KobyliĆski huk256@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@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel