Logo Search packages:      
Sourcecode: linphone version File versions  Download package

osscard.c

/*
  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 "osscard.h"

#include "msossread.h"
#include "msosswrite.h"

#ifdef HAVE_SYS_SOUNDCARD_H
#include <sys/soundcard.h>

#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>

#if 0
void * oss_thread(OssCard *obj)
{
      gint i;
      gint err;
      g_message("oss_thread: starting **********");
      while(1){
            for(i=0;i<OSS_CARD_BUFFERS;i++){
                  g_mutex_lock(obj->lock);
                  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 (size<bsize){
            gint canread=MIN(bsize-obj->readpos,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 (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=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 (;tabindex<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){
            devname=g_strdup_printf("%s%i",DSP_NAME,devindex);
            mixername=g_strdup_printf("%s%i",MIXER_NAME,devindex);
            if (g_file_test(devname,G_FILE_TEST_EXISTS)){
                  manager->cards[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 (;tabindex<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){
            devname=malloc(100);
            snprintf(devname, 100, "%s%i",DSP_NAME,devindex);
            mixername=malloc(100);
            snprintf(mixername, 100, "%s%i",MIXER_NAME,devindex);

            if (access(devname,F_OK)==0){
                  manager->cards[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

Generated by  Doxygen 1.6.0   Back to index