/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "osscard.h" #include "msossread.h" #include "msosswrite.h" #ifdef HAVE_SYS_SOUNDCARD_H #include #include #include #include #if 0 void * oss_thread(OssCard *obj) { gint i; gint err; g_message("oss_thread: starting **********"); while(1){ for(i=0;ilock); if (obj->ref==0){ g_cond_signal(obj->cond); g_mutex_unlock(obj->lock); g_thread_exit(NULL); } g_mutex_unlock(obj->lock); obj->readindex=i; err=read(obj->fd,obj->readbuf[i],SND_CARD(obj)->bsize); if (err<0) g_warning("oss_thread: read() error:%s.",strerror(errno)); obj->writeindex=i; write(obj->fd,obj->writebuf[i],SND_CARD(obj)->bsize); memset(obj->writebuf[i],0,SND_CARD(obj)->bsize); } } } #endif int oss_open(OssCard *obj, int bits,int stereo, int rate) { int fd; int p=0,cond=0; int i=0; int min_size=0,blocksize=512; int err; //g_message("opening sound device"); fd=open(obj->dev_name,O_RDWR|O_NONBLOCK); if (fd<0) return -EWOULDBLOCK; /* unset nonblocking mode */ /* We wanted non blocking open but now put it back to normal ; thanks Xine !*/ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)&~O_NONBLOCK); /* reset is maybe not needed but takes time*/ /*ioctl(fd, SNDCTL_DSP_RESET, 0); */ #ifdef WORDS_BIGENDIAN p=AFMT_U16_BE; #else p=AFMT_U16_LE; #endif err=ioctl(fd,SNDCTL_DSP_SETFMT,&p); if (err<0){ g_warning("oss_open: can't set sample format:%s.",strerror(errno)); } p = bits; /* 16 bits */ err=ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p); if (err<0){ g_warning("oss_open: can't set sample size to %i:%s.",bits,strerror(errno)); } p = rate; /* rate in khz*/ err=ioctl(fd, SNDCTL_DSP_SPEED, &p); if (err<0){ g_warning("oss_open: can't set sample rate to %i:%s.",rate,strerror(errno)); } p = stereo; /* stereo or not */ err=ioctl(fd, SNDCTL_DSP_STEREO, &p); if (err<0){ g_warning("oss_open: can't set mono/stereo mode:%s.",strerror(errno)); } if (rate==16000) blocksize=4096; /* oss emulation is not very good at 16khz */ else blocksize=blocksize*(rate/8000); ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); /* try to subdivide BLKSIZE to reach blocksize if necessary */ if (min_size>blocksize) { cond=1; p=min_size/blocksize; while(cond) { i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p); //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno); if ((i==0) || (p==1)) cond=0; else p=p/2; } } ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); if (min_size>blocksize) { g_warning("dsp block size set to %i.",min_size); }else{ /* no need to access the card with less latency than needed*/ min_size=blocksize; } g_message("dsp blocksize is %i.",min_size); /* start recording !!! Alex */ { int fl,res; fl=PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT; res=ioctl(fd, SNDCTL_DSP_SETTRIGGER, &fl); if (res<0) g_warning("OSS_TRIGGER: %s",strerror(errno)); } obj->fd=fd; obj->readpos=0; obj->writepos=0; SND_CARD(obj)->bits=bits; SND_CARD(obj)->stereo=stereo; SND_CARD(obj)->rate=rate; SND_CARD(obj)->bsize=min_size; return fd; } int oss_card_probe(OssCard *obj,int bits,int stereo,int rate) { int fd; int p=0,cond=0; int i=0; int min_size=0,blocksize=512; if (obj->fd>0) return SND_CARD(obj)->bsize; fd=open(obj->dev_name,O_RDWR|O_NONBLOCK); if (fd<0) { g_warning("oss_card_probe: can't open %s: %s.",obj->dev_name,strerror(errno)); return -1; } ioctl(fd, SNDCTL_DSP_RESET, 0); p = bits; /* 16 bits */ ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p); p = stereo; /* number of channels */ ioctl(fd, SNDCTL_DSP_CHANNELS, &p); p = rate; /* rate in khz*/ ioctl(fd, SNDCTL_DSP_SPEED, &p); ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); /* try to subdivide BLKSIZE to reach blocksize if necessary */ if (min_size>blocksize) { cond=1; p=min_size/blocksize; while(cond) { i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p); //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno); if ((i==0) || (p==1)) cond=0; else p=p/2; } } ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size); if (min_size>blocksize) { g_warning("dsp block size set to %i.",min_size); }else{ /* no need to access the card with less latency than needed*/ min_size=blocksize; } close(fd); return min_size; } int oss_card_open(OssCard *obj,int bits,int stereo,int rate) { int fd; obj->ref++; if (obj->fd==0){ fd=oss_open(obj,bits,stereo,rate); if (fd<0) { obj->fd=0; obj->ref--; return -1; } } obj->readbuf=g_malloc0(SND_CARD(obj)->bsize); obj->writebuf=g_malloc0(SND_CARD(obj)->bsize); SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED; return 0; } void oss_card_close(OssCard *obj) { int i; obj->ref--; if (obj->ref==0) { close(obj->fd); obj->fd=0; SND_CARD(obj)->flags&=~SND_CARD_FLAGS_OPENED; g_free(obj->readbuf); obj->readbuf=NULL; g_free(obj->writebuf); obj->writebuf=NULL; } } void oss_card_destroy(OssCard *obj) { snd_card_uninit(SND_CARD(obj)); g_free(obj->dev_name); g_free(obj->mixdev_name); if (obj->readbuf!=NULL) g_free(obj->readbuf); if (obj->writebuf!=NULL) g_free(obj->writebuf); } gboolean oss_card_can_read(OssCard *obj) { struct timeval tout={0,0}; int err; fd_set fdset; if (obj->readpos!=0) return TRUE; FD_ZERO(&fdset); FD_SET(obj->fd,&fdset); err=select(obj->fd+1,&fdset,NULL,NULL,&tout); if (err>0) return TRUE; else return FALSE; } int oss_card_read(OssCard *obj,char *buf,int size) { int err; gint bsize=SND_CARD(obj)->bsize; if (sizereadpos,size); if (obj->readpos==0){ err=read(obj->fd,obj->readbuf,bsize); if (err<0) { g_warning("oss_card_read: read() failed:%s.",strerror(errno)); return -1; } } memcpy(buf,&obj->readbuf[obj->readpos],canread); obj->readpos+=canread; if (obj->readpos>=bsize) obj->readpos=0; return canread; }else{ err=read(obj->fd,buf,size); if (err<0) { g_warning("oss_card_read: read-2() failed:%s.",strerror(errno)); } return err; } } int oss_card_write(OssCard *obj,char *buf,int size) { int err; gint bsize=SND_CARD(obj)->bsize; if (sizewritepos,size); memcpy(&obj->writebuf[obj->writepos],buf,canwrite); obj->writepos+=canwrite; if (obj->writepos>=bsize){ err=write(obj->fd,obj->writebuf,bsize); obj->writepos=0; } return canwrite; }else{ return write(obj->fd,buf,bsize); } } void oss_card_set_level(OssCard *obj,gint way,gint a) { int p,mix_fd; int osscmd; g_return_if_fail(obj->mixdev_name!=NULL); #ifdef HAVE_SYS_SOUNDCARD_H switch(way){ case SND_CARD_LEVEL_GENERAL: osscmd=SOUND_MIXER_VOLUME; break; case SND_CARD_LEVEL_INPUT: osscmd=SOUND_MIXER_IGAIN; break; case SND_CARD_LEVEL_OUTPUT: osscmd=SOUND_MIXER_PCM; break; default: g_warning("oss_card_set_level: unsupported command."); return; } p=(((int)a)<<8 | (int)a); mix_fd = open(obj->mixdev_name, O_WRONLY); ioctl(mix_fd,MIXER_WRITE(osscmd), &p); close(mix_fd); #endif } gint oss_card_get_level(OssCard *obj,gint way) { int p=0,mix_fd; int osscmd; g_return_if_fail(obj->mixdev_name!=NULL); #ifdef HAVE_SYS_SOUNDCARD_H switch(way){ case SND_CARD_LEVEL_GENERAL: osscmd=SOUND_MIXER_VOLUME; break; case SND_CARD_LEVEL_INPUT: osscmd=SOUND_MIXER_IGAIN; break; case SND_CARD_LEVEL_OUTPUT: osscmd=SOUND_MIXER_PCM; break; default: g_warning("oss_card_get_level: unsupported command."); return -1; } mix_fd = open(obj->mixdev_name, O_RDONLY); ioctl(mix_fd,MIXER_READ(SOUND_MIXER_VOLUME), &p); close(mix_fd); #endif return p>>8; } void oss_card_set_source(OssCard *obj,int source) { gint p=0; gint mix_fd; g_return_if_fail(obj->mixdev_name!=NULL); #ifdef HAVE_SYS_SOUNDCARD_H if (source == 'c') p = 1 << SOUND_MIXER_CD; if (source == 'l') p = 1 << SOUND_MIXER_LINE; if (source == 'm') p = 1 << SOUND_MIXER_MIC; mix_fd = open(obj->mixdev_name, O_WRONLY); ioctl(mix_fd, SOUND_MIXER_WRITE_RECSRC, &p); close(mix_fd); #endif } MSFilter *oss_card_create_read_filter(OssCard *card) { MSFilter *f=ms_oss_read_new(); ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index); return f; } MSFilter *oss_card_create_write_filter(OssCard *card) { MSFilter *f=ms_oss_write_new(); ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index); return f; } SndCard * oss_card_new(char *devname, char *mixdev_name) { OssCard * obj= g_new0(OssCard,1); SndCard *base= SND_CARD(obj); snd_card_init(base); obj->dev_name=g_strdup(devname); obj->mixdev_name=g_strdup( mixdev_name); #ifdef HAVE_GLIB base->card_name=g_strdup_printf("%s (Open Sound System)",devname); #else base->card_name=malloc(100); snprintf(base->card_name, 100, "%s (Open Sound System)",devname); #endif base->_probe=(SndCardOpenFunc)oss_card_probe; base->_open_r=(SndCardOpenFunc)oss_card_open; base->_open_w=(SndCardOpenFunc)oss_card_open; base->_can_read=(SndCardPollFunc)oss_card_can_read; base->_read=(SndCardIOFunc)oss_card_read; base->_write=(SndCardIOFunc)oss_card_write; base->_close_r=(SndCardCloseFunc)oss_card_close; base->_close_w=(SndCardCloseFunc)oss_card_close; base->_set_rec_source=(SndCardMixerSetRecSourceFunc)oss_card_set_source; base->_set_level=(SndCardMixerSetLevelFunc)oss_card_set_level; base->_get_level=(SndCardMixerGetLevelFunc)oss_card_get_level; base->_destroy=(SndCardDestroyFunc)oss_card_destroy; base->_create_read_filter=(SndCardCreateFilterFunc)oss_card_create_read_filter; base->_create_write_filter=(SndCardCreateFilterFunc)oss_card_create_write_filter; return base; } #define DSP_NAME "/dev/dsp" #define MIXER_NAME "/dev/mixer" gint oss_card_manager_init(SndCardManager *manager, gint tabindex) { gchar *devname; gchar *mixername; gint devindex=0; gint found=0; /* search for /dev/dsp and /dev/mixer */ #ifdef HAVE_GLIB if (g_file_test(DSP_NAME,G_FILE_TEST_EXISTS)){ tabindex++; devindex++; manager->cards[0]=oss_card_new(DSP_NAME,MIXER_NAME); manager->cards[0]->index=0; found++; g_message("Found /dev/dsp."); } for (;tabindexcards[tabindex]=oss_card_new(devname,mixername); manager->cards[tabindex]->index=tabindex; tabindex++; found++; } g_free(devname); g_free(mixername); } #else if (access(DSP_NAME,F_OK)==0){ tabindex++; devindex++; manager->cards[0]=oss_card_new(DSP_NAME,MIXER_NAME); manager->cards[0]->index=0; found++; g_message("Found /dev/dsp."); } for (;tabindexcards[tabindex]=oss_card_new(devname,mixername); manager->cards[tabindex]->index=tabindex; tabindex++; found++; } g_free(devname); g_free(mixername); } #endif if (tabindex==0) g_warning("No sound cards found !"); return found; } #endif