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):
--- 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@alsa-project.org + * + * 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 ;]