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@xxxxxxxxxxxxxxxx> > + * > + * Copyright (c) 2010 by Adrian KobyliÅski <huk256@xxxxxxxxx> > + * > + * 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@xxxxxxxxxxxxxxxx > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel