diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c | |
download | tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c')
-rw-r--r-- | kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c new file mode 100644 index 00000000..c240aa72 --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c @@ -0,0 +1,640 @@ +/* + The mediastreamer library aims at providing modular media processing and I/O + for linphone, but also for any telephony application. + Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org + + 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 library 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 "alsacard.h" + +#ifdef HAVE_ALSA_ASOUNDLIB_H + +static gchar *over_pcmdev=NULL; + +#include "msossread.h" +#include "msosswrite.h" + +#include <signal.h> + +int __alsa_card_write(AlsaCard *obj,char *buf,int size); + +int alsa_set_params(AlsaCard *obj, int rw, int bits, int stereo, int rate) +{ + snd_pcm_hw_params_t *hwparams=NULL; + snd_pcm_sw_params_t *swparams=NULL; + snd_pcm_t *pcm_handle; + gint dir,exact_value; + gint channels; + gint fsize=0; + gint periods=8; + gint periodsize=256; + gint err; + int format; + + if (rw) { + pcm_handle=obj->write_handle; + } + else pcm_handle=obj->read_handle; + + /* Allocate the snd_pcm_hw_params_t structure on the stack. */ + snd_pcm_hw_params_alloca(&hwparams); + + /* Init hwparams with full configuration space */ + if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { + g_warning("alsa_set_params: Cannot configure this PCM device.\n"); + return(-1); + } + + if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { + g_warning("alsa_set_params: Error setting access.\n"); + return(-1); + } + /* Set sample format */ +#ifdef WORDS_BIGENDIAN + format=SND_PCM_FORMAT_S16_BE; +#else + format=SND_PCM_FORMAT_S16_LE; +#endif + if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) { + g_warning("alsa_set_params: Error setting format.\n"); + return(-1); + } + /* Set number of channels */ + if (stereo) channels=2; + else channels=1; + if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) { + g_warning("alsa_set_params: Error setting channels.\n"); + return(-1); + } + /* Set sample rate. If the exact rate is not supported */ + /* by the hardware, use nearest possible rate. */ + exact_value=rate; + dir=0; + if ((err=snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_value, &dir))<0){ + g_warning("alsa_set_params: Error setting rate to %i:%s",rate,snd_strerror(err)); + return -1; + } + if (dir != 0) { + g_warning("alsa_set_params: The rate %d Hz is not supported by your hardware.\n " + "==> Using %d Hz instead.\n", rate, exact_value); + } + /* choose greater period size when rate is high */ + periodsize=periodsize*(rate/8000); + + /* Set buffer size (in frames). The resulting latency is given by */ + /* latency = periodsize * periods / (rate * bytes_per_frame) */ + /* + fsize=periodsize * periods; + exact_value=fsize; + if ((err=snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams,&exact_value)) < 0) { + g_warning("alsa_set_params: Error setting buffer size:%s",snd_strerror(err)); + return(-1); + } + if (fsize!= exact_value) { + g_warning("alsa_set_params: The buffer size %d is not supported by your hardware.\n " + "==> Using %d instead.\n", fsize, exact_value); + } + */ + /* set period size */ + exact_value=periodsize; + dir=0; + if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &exact_value, &dir) < 0) { + g_warning("alsa_set_params: Error setting period size.\n"); + return(-1); + } + if (dir != 0) { + g_warning("alsa_set_params: The period size %d is not supported by your hardware.\n " + "==> Using %d instead.\n", periodsize, exact_value); + } + periodsize=exact_value; + /* Set number of periods. Periods used to be called fragments. */ + exact_value=periods; + dir=0; + if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &exact_value, &dir) < 0) { + g_warning("alsa_set_params: Error setting periods.\n"); + return(-1); + } + if (dir != 0) { + g_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n " + "==> Using %d instead.\n", periods, exact_value); + } + /* Apply HW parameter settings to */ + /* PCM device and prepare device */ + if ((err=snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + g_warning("alsa_set_params: Error setting HW params:%s",snd_strerror(err)); + return(-1); + } + /*prepare sw params */ + if (rw){ + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(pcm_handle, swparams); + if ((err=snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,periodsize*2 ))<0){ + g_warning("alsa_set_params: Error setting start threshold:%s",snd_strerror(err)); + return -1; + } + if ((err=snd_pcm_sw_params(pcm_handle, swparams))<0){ + g_warning("alsa_set_params: Error setting SW params:%s",snd_strerror(err)); + return(-1); + } + } + obj->frame_size=channels*(bits/8); + SND_CARD(obj)->bsize=periodsize*obj->frame_size; + /* //SND_CARD(obj)->bsize=4096; */ + obj->frames=periodsize; + g_message("alsa_set_params: blocksize=%i.",SND_CARD(obj)->bsize); + return SND_CARD(obj)->bsize; +} + +int alsa_card_open_r(AlsaCard *obj,int bits,int stereo,int rate) +{ + int bsize; + int err; + snd_pcm_t *pcm_handle; + gchar *pcmdev; + if (over_pcmdev!=NULL) pcmdev=over_pcmdev; + else pcmdev=obj->pcmdev; + + if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK) < 0) { + g_warning("alsa_card_open_r: Error opening PCM device %s\n",obj->pcmdev ); + return -1; + } + g_return_val_if_fail(pcm_handle!=NULL,-1); + obj->read_handle=pcm_handle; + if ((bsize=alsa_set_params(obj,0,bits,stereo,rate))<0){ + snd_pcm_close(pcm_handle); + obj->read_handle=NULL; + return -1; + } + obj->readbuf=g_malloc0(bsize); + + err=snd_pcm_start(obj->read_handle); + if (err<0){ + g_warning("Cannot start read pcm: %s", snd_strerror(err)); + } + obj->readpos=0; + SND_CARD(obj)->bsize=bsize; + SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; + return 0; +} + +int alsa_card_open_w(AlsaCard *obj,int bits,int stereo,int rate) +{ + int err,bsize; + snd_pcm_t *pcm_handle; + gchar *pcmdev; + if (over_pcmdev!=NULL) pcmdev=over_pcmdev; + else pcmdev=obj->pcmdev; + + if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) { + g_warning("alsa_card_open_w: Error opening PCM device %s\n", obj->pcmdev); + return -1; + } + obj->write_handle=pcm_handle; + if ((bsize=alsa_set_params(obj,1,bits,stereo,rate))<0){ + snd_pcm_close(pcm_handle); + obj->write_handle=NULL; + return -1; + } + obj->writebuf=g_malloc0(bsize); + + obj->writepos=0; + SND_CARD(obj)->bsize=bsize; + SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; + return 0; +} + + +void alsa_card_set_blocking_mode(AlsaCard *obj, gboolean yesno){ + if (obj->read_handle!=NULL) snd_pcm_nonblock(obj->read_handle,!yesno); + if (obj->write_handle!=NULL) snd_pcm_nonblock(obj->write_handle,!yesno); +} + +void alsa_card_close_r(AlsaCard *obj) +{ + if (obj->read_handle!=NULL){ + snd_pcm_close(obj->read_handle); + obj->read_handle=NULL; + g_free(obj->readbuf); + obj->readbuf=NULL; + } +} + +void alsa_card_close_w(AlsaCard *obj) +{ + if (obj->write_handle!=NULL){ + snd_pcm_close(obj->write_handle); + obj->write_handle=NULL; + g_free(obj->writebuf); + obj->writebuf=NULL; + } +} + +int alsa_card_probe(AlsaCard *obj,int bits,int stereo,int rate) +{ + int ret; + ret=alsa_card_open_w(obj,bits,stereo,rate); + if (ret<0) return -1; + ret=SND_CARD(obj)->bsize; + alsa_card_close_w(obj); + return ret; +} + + +void alsa_card_destroy(AlsaCard *obj) +{ + snd_card_uninit(SND_CARD(obj)); + g_free(obj->pcmdev); + if (obj->readbuf!=0) g_free(obj->readbuf); + if (obj->writebuf!=0) g_free(obj->writebuf); +} + +gboolean alsa_card_can_read(AlsaCard *obj) +{ + int frames; + g_return_val_if_fail(obj->read_handle!=NULL,0); + if (obj->readpos!=0) return TRUE; + if ( frames=snd_pcm_avail_update(obj->read_handle)>=obj->frames) return 1; + /* //g_message("frames=%i",frames); */ + return 0; +} + + + +int __alsa_card_read(AlsaCard *obj,char *buf,int bsize) +{ + int err; + sigset_t set; + sigemptyset(&set); + sigaddset(&set,SIGALRM); + sigprocmask(SIG_BLOCK,&set,NULL); + err=snd_pcm_readi(obj->read_handle,buf,bsize/obj->frame_size); + if (err<0) { + if (err!=-EPIPE){ + g_warning("alsa_card_read: snd_pcm_readi() failed:%s.",snd_strerror(err)); + } + snd_pcm_prepare(obj->read_handle); + err=snd_pcm_readi(obj->read_handle,buf,bsize/obj->frame_size); + if (err<0) g_warning("alsa_card_read: snd_pcm_readi() failed:%s.",snd_strerror(err)); + } + sigprocmask(SIG_UNBLOCK,&set,NULL); + return err*obj->frame_size; +} + +int alsa_card_read(AlsaCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + g_return_val_if_fail(obj->read_handle!=NULL,-1); + if (size<bsize){ + gint canread=MIN(bsize-obj->readpos,size); + + if (obj->readpos==0){ + err=__alsa_card_read(obj,obj->readbuf,bsize); + } + + memcpy(buf,&obj->readbuf[obj->readpos],canread); + obj->readpos+=canread; + if (obj->readpos>=bsize) obj->readpos=0; + return canread; + }else{ + err=__alsa_card_read(obj,buf,size); + return err; + } + +} + +int __alsa_card_write(AlsaCard *obj,char *buf,int size) +{ + int err; + sigset_t set; + sigemptyset(&set); + sigaddset(&set,SIGALRM); + sigprocmask(SIG_BLOCK,&set,NULL); + if ((err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size))<0){ + if (err!=-EPIPE){ + g_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err)); + } + snd_pcm_prepare(obj->write_handle); + err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size); + if (err<0) g_warning("alsa_card_write: Error writing sound buffer (size=%i):%s",size,snd_strerror(err)); + + } + sigprocmask(SIG_UNBLOCK,&set,NULL); + return err; +} + +int alsa_card_write(AlsaCard *obj,char *buf,int size) +{ + int err; + gint bsize=SND_CARD(obj)->bsize; + g_return_val_if_fail(obj->write_handle!=NULL,-1); + if (size<bsize){ + gint canwrite; + + canwrite=MIN(bsize-obj->writepos,size); + memcpy(&obj->writebuf[obj->writepos],buf,canwrite); + obj->writepos+=canwrite; + if (obj->writepos>=bsize){ + err=__alsa_card_write(obj,obj->writebuf,bsize); + obj->writepos=0; + } + return canwrite; + }else{ + return __alsa_card_write(obj,buf,bsize); + } +} + +snd_mixer_t *alsa_mixer_open(AlsaCard *obj){ + snd_mixer_t *mixer=NULL; + int err; + err=snd_mixer_open(&mixer,0); + if (err<0){ + g_warning("Could not open alsa mixer: %s",snd_strerror(err)); + return NULL; + } + if ((err = snd_mixer_attach (mixer, obj->mixdev)) < 0){ + g_warning("Could not attach mixer to card: %s",snd_strerror(err)); + snd_mixer_close(mixer); + return NULL; + } + if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){ + g_warning("snd_mixer_selem_register: %s",snd_strerror(err)); + snd_mixer_close(mixer); + return NULL; + } + if ((err = snd_mixer_load (mixer)) < 0){ + g_warning("snd_mixer_load: %s",snd_strerror(err)); + snd_mixer_close(mixer); + return NULL; + } + obj->mixer=mixer; + return mixer; +} + +void alsa_mixer_close(AlsaCard *obj){ + snd_mixer_close(obj->mixer); + obj->mixer=NULL; +} + +typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction; + +static gint get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){ + long value=0; + const char *elemname; + snd_mixer_elem_t *elem; + int err; + long sndMixerPMin; + long sndMixerPMax; + long newvol; + elem=snd_mixer_first_elem(mixer); + while (elem!=NULL){ + elemname=snd_mixer_selem_get_name(elem); + /* //g_message("Found alsa mixer element %s.",elemname); */ + if (strcmp(elemname,name)==0){ + switch (action){ + case CAPTURE: + if (snd_mixer_selem_has_capture_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol); + newvol-=sndMixerPMin; + value=(100*newvol)/(sndMixerPMax-sndMixerPMin); + if (err<0) g_warning("Could not get capture volume for %s:%s",name,snd_strerror(err)); + /* //else g_message("Succesfully get capture level for %s.",elemname); */ + break; + } + break; + case PLAYBACK: + if (snd_mixer_selem_has_playback_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol); + newvol-=sndMixerPMin; + value=(100*newvol)/(sndMixerPMax-sndMixerPMin); + if (err<0) g_warning("Could not get playback volume for %s:%s",name,snd_strerror(err)); + /* //else g_message("Succesfully get playback level for %s.",elemname); */ + break; + } + break; + case CAPTURE_SWITCH: + + break; + } + } + elem=snd_mixer_elem_next(elem); + } + + return value; +} + + +static void set_mixer_element(snd_mixer_t *mixer,const char *name, gint level,MixerAction action){ + const char *elemname; + snd_mixer_elem_t *elem; + int tmp; + long sndMixerPMin; + long sndMixerPMax; + long newvol; + + elem=snd_mixer_first_elem(mixer); + + while (elem!=NULL){ + elemname=snd_mixer_selem_get_name(elem); + /* //g_message("Found alsa mixer element %s.",elemname); */ + if (strcmp(elemname,name)==0){ + switch(action){ + case CAPTURE: + if (snd_mixer_selem_has_capture_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin; + snd_mixer_selem_set_capture_volume_all(elem,newvol); + /* //g_message("Succesfully set capture level for %s.",elemname); */ + return; + } + break; + case PLAYBACK: + if (snd_mixer_selem_has_playback_volume(elem)){ + snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); + newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin; + snd_mixer_selem_set_playback_volume_all(elem,newvol); + /* //g_message("Succesfully set playback level for %s.",elemname); */ + return; + } + break; + case CAPTURE_SWITCH: + if (snd_mixer_selem_has_capture_switch(elem)){ + snd_mixer_selem_set_capture_switch_all(elem,level); + /* //g_message("Succesfully set capture switch for %s.",elemname); */ + } + break; + case PLAYBACK_SWITCH: + if (snd_mixer_selem_has_playback_switch(elem)){ + snd_mixer_selem_set_playback_switch_all(elem,level); + /* //g_message("Succesfully set capture switch for %s.",elemname); */ + } + break; + + } + } + elem=snd_mixer_elem_next(elem); + } + + return ; +} + + +void alsa_card_set_level(AlsaCard *obj,gint way,gint a) +{ + snd_mixer_t *mixer; + mixer=alsa_mixer_open(obj); + if (mixer==NULL) return ; + switch(way){ + case SND_CARD_LEVEL_GENERAL: + set_mixer_element(mixer,"Master",a,PLAYBACK); + break; + case SND_CARD_LEVEL_INPUT: + set_mixer_element(mixer,"Capture",a,CAPTURE); + break; + case SND_CARD_LEVEL_OUTPUT: + set_mixer_element(mixer,"PCM",a,PLAYBACK); + break; + default: + g_warning("oss_card_set_level: unsupported command."); + } + alsa_mixer_close(obj); +} + +gint alsa_card_get_level(AlsaCard *obj,gint way) +{ + snd_mixer_t *mixer; + gint value; + mixer=alsa_mixer_open(obj); + if (mixer==NULL) return 0; + switch(way){ + case SND_CARD_LEVEL_GENERAL: + value=get_mixer_element(mixer,"Master",PLAYBACK); + break; + case SND_CARD_LEVEL_INPUT: + value=get_mixer_element(mixer,"Capture",CAPTURE); + break; + case SND_CARD_LEVEL_OUTPUT: + value=get_mixer_element(mixer,"PCM",PLAYBACK); + break; + default: + g_warning("oss_card_set_level: unsupported command."); + } + alsa_mixer_close(obj); + return value; +} + +void alsa_card_set_source(AlsaCard *obj,int source) +{ + snd_mixer_t *mixer; + mixer=alsa_mixer_open(obj); + if (mixer==NULL) return; + switch (source){ + case 'm': + set_mixer_element(mixer,"Mic",1,CAPTURE_SWITCH); + set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH); + break; + case 'l': + set_mixer_element(mixer,"Line",1,CAPTURE_SWITCH); + set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH); + break; + } +} + +MSFilter *alsa_card_create_read_filter(AlsaCard *card) +{ + MSFilter *f=ms_oss_read_new(); + ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); + return f; +} + +MSFilter *alsa_card_create_write_filter(AlsaCard *card) +{ + MSFilter *f=ms_oss_write_new(); + ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); + return f; +} + + +SndCard * alsa_card_new(gint devid) +{ + AlsaCard * obj; + SndCard *base; + int err; + gchar *name=NULL; + + /* carefull: this is an alsalib call despite its name! */ + err=snd_card_get_name(devid,&name); + if (err<0) { + return NULL; + } + obj= g_new0(AlsaCard,1); + base= SND_CARD(obj); + snd_card_init(base); + + base->card_name=g_strdup_printf("%s (Advanced Linux Sound Architecture)",name); + base->_probe=(SndCardOpenFunc)alsa_card_probe; + base->_open_r=(SndCardOpenFunc)alsa_card_open_r; + base->_open_w=(SndCardOpenFunc)alsa_card_open_w; + base->_can_read=(SndCardPollFunc)alsa_card_can_read; + base->_set_blocking_mode=(SndCardSetBlockingModeFunc)alsa_card_set_blocking_mode; + base->_read=(SndCardIOFunc)alsa_card_read; + base->_write=(SndCardIOFunc)alsa_card_write; + base->_close_r=(SndCardCloseFunc)alsa_card_close_r; + base->_close_w=(SndCardCloseFunc)alsa_card_close_w; + base->_set_rec_source=(SndCardMixerSetRecSourceFunc)alsa_card_set_source; + base->_set_level=(SndCardMixerSetLevelFunc)alsa_card_set_level; + base->_get_level=(SndCardMixerGetLevelFunc)alsa_card_get_level; + base->_destroy=(SndCardDestroyFunc)alsa_card_destroy; + base->_create_read_filter=(SndCardCreateFilterFunc)alsa_card_create_read_filter; + base->_create_write_filter=(SndCardCreateFilterFunc)alsa_card_create_write_filter; + + + obj->pcmdev=g_strdup_printf("plughw:%i,0",devid); + obj->mixdev=g_strdup_printf("hw:%i",devid); + obj->readbuf=NULL; + obj->writebuf=NULL; + return base; +} + + +gint alsa_card_manager_init(SndCardManager *m, gint index) +{ + gint devindex; + gint i; + gint found=0; + gchar *name=NULL; + for(devindex=0;index<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){ + if (snd_card_get_name(devindex,&name)==0){ + g_message("Found ALSA device: %s",name); + m->cards[index]=alsa_card_new(devindex); + m->cards[index]->index=index; + found++; + index++; + } + } + return found; +} + +void alsa_card_manager_set_default_pcm_device(const gchar *pcmdev){ + if (over_pcmdev!=NULL){ + g_free(over_pcmdev); + } + over_pcmdev=g_strdup(pcmdev); +} + +#endif |