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

audiolinux.c

/*****************************************************************************/

/*
 *      audiolinux.c  --  Audio processing for "virtual transceiver", Linux IO.
 *
 *      Copyright (C) 1999, 2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* AIX requires this to be the first thing in the file.  */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
#pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#endif

#include <sys/types.h>

#if defined(HAVE_SYS_SOUNDCARD_H)
#include <sys/soundcard.h>
#elif defined(HAVE_LINUX_SOUNDCARD_H)
#include <linux/soundcard.h>
#endif
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "trxapi.h"
#include "trx.h"

/* ---------------------------------------------------------------------- */
#if (defined(HAVE_LINUX_SOUNDCARD_H) || defined(HAVE_SYS_SOUNDCARD_H))

#define BUFSIZE    8192   /* must be significantly bigger than SNDLATENCY! */

/* ---------------------------------------------------------------------- */

static void stop(struct trx_thread_state *state)
{
      if (state->du.a.io.fdout != -1 && state->du.a.io.fdout != state->du.a.io.fdin)
            close(state->du.a.io.fdout);
      if (state->du.a.io.fdin != -1)
            close(state->du.a.io.fdin);
      state->du.a.io.fdin = state->du.a.io.fdout = -1;
}

static void start(struct trx_thread_state *state)
{
      int16_t buf[SNDLATENCY];
      audio_buf_info abinfo;
      int apar;
      unsigned srate;

      stop(state);
      if (strncmp(state->cfg.adapt.audiodevin, state->cfg.adapt.audiodevout, sizeof(state->cfg.adapt.audiodevin))) {
            if ((state->du.a.io.fdin = open(state->cfg.adapt.audiodevin, O_RDONLY | O_NONBLOCK)) == -1) {
                  lprintf(0, "cannot open \"%s\" (%s)\n", state->cfg.adapt.audiodevin, strerror(errno));
                  goto errsnd;
            }
            if ((state->du.a.io.fdout = open(state->cfg.adapt.audiodevout, O_WRONLY | O_NONBLOCK)) == -1) {
                  lprintf(0, "cannot open \"%s\" (%s)\n", state->cfg.adapt.audiodevout, strerror(errno));
                  goto errsnd;
            }
      } else {
            if ((state->du.a.io.fdin = state->du.a.io.fdout = open(state->cfg.adapt.audiodevin, O_RDWR | O_NONBLOCK)) == -1) {
                  lprintf(0, "cannot open \"%s\" (%s)\n", state->cfg.adapt.audiodevin, strerror(errno));
                  goto errsnd;
            }
      }
      fcntl(state->du.a.io.fdin, F_SETFL, fcntl(state->du.a.io.fdin, F_GETFL, 0) | O_NONBLOCK);
      if (state->du.a.io.fdin != state->du.a.io.fdout)
            fcntl(state->du.a.io.fdout, F_SETFL, fcntl(state->du.a.io.fdout, F_GETFL, 0) | O_NONBLOCK);
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_NONBLOCK, 0) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_NONBLOCK failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (state->du.a.io.fdin != state->du.a.io.fdout && ioctl(state->du.a.io.fdout, SNDCTL_DSP_NONBLOCK, 0) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_NONBLOCK failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_GETCAPS, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETCAPS failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (!(apar & DSP_CAP_TRIGGER)) {
            lprintf(0, "soundcard does not support trigger\n");
            goto errsnd;
      }
      if (state->du.a.io.fdin != state->du.a.io.fdout) {
            if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_GETCAPS, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_GETCAPS failed (%s)\n", strerror(errno));
                  goto errsnd;
            }
            if (!(apar & DSP_CAP_TRIGGER)) {
                  lprintf(0, "soundcard does not support trigger\n");
                  goto errsnd;
            }
      } else {
            if (!(apar & DSP_CAP_DUPLEX)) {
                  lprintf(0, "soundcard does not support full duplex\n");
                  goto errsnd;
            }
      }
      apar = AFMT_S16_LE;
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_SETFMT, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SETFMT failed (%s), S16_LE not supported??\n", strerror(errno));
            goto errsnd;
      }
#if 0
      apar = 0;
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_STEREO, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_STEREO failed (%s), mono not supported??\n", strerror(errno));
            goto errsnd;
      }
#else
      apar = 1;
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_CHANNELS, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_CHANNELS failed (%s), mono not supported??\n", strerror(errno));
            goto errsnd;
      }
#endif
      srate = state->du.a.p.srateusb;
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_SPEED, &srate) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SPEED failed (%s), samplingrate %u not supported??\n", 
                  strerror(errno), state->du.a.p.srateusb);
            goto errsnd;
      }
      state->du.a.p.sratedspout = state->du.a.p.sratedspin = srate;
      if (state->du.a.io.fdin != state->du.a.io.fdout) {
            apar = AFMT_S16_LE;
            if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_SETFMT, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_SETFMT failed (%s), S16_LE not supported??\n", strerror(errno));
                  goto errsnd;
            }
#if 0
            apar = 0;
            if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_STEREO, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_STEREO failed (%s), mono not supported??\n", strerror(errno));
                  goto errsnd;
            }
#else
            apar = 1;
            if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_CHANNELS, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_CHANNELS failed (%s), mono not supported??\n", strerror(errno));
                  goto errsnd;
            }
#endif
            srate = state->du.a.p.srateusb;
            if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_SPEED, &srate) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_SPEED failed (%s), samplingrate %u not supported??\n", 
                        strerror(errno), state->du.a.p.srateusb);
                  goto errsnd;
            }
            state->du.a.p.sratedspout = srate;
}
      lprintf(2, "usb adapter sampling rate %u, soundcard sampling rate in %u out %u\n", 
            state->du.a.p.srateusb, state->du.a.p.sratedspin, state->du.a.p.sratedspout);
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_GETISPACE, &abinfo) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETISPACE failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (abinfo.fragstotal * abinfo.fragsize < 2*SNDLATENCY*2) {
            lprintf(0, "soundcard buffers too small (%u)\n", 
                  abinfo.fragstotal * abinfo.fragsize);
            goto errsnd;
      }
      if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_GETOSPACE, &abinfo) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETOSPACE failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (abinfo.fragstotal * abinfo.fragsize < 2*SNDLATENCY*2) {
            lprintf(0, "soundcard buffers too small (%u)\n", 
                  abinfo.fragstotal * abinfo.fragsize);
            goto errsnd;
      }
      if (state->du.a.io.fdin != state->du.a.io.fdout) {
            apar = PCM_ENABLE_INPUT;
            if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_SETTRIGGER, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_SETTRIGGER failed (%s)\n", strerror(errno));
                  goto errsnd;
            }
            apar = PCM_ENABLE_OUTPUT;
            if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_SETTRIGGER, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_SETTRIGGER failed (%s)\n", strerror(errno));
                  goto errsnd;
            }
      } else {
            apar = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
            if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_SETTRIGGER, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_SETTRIGGER failed (%s)\n", strerror(errno));
                  goto errsnd;
            }
      }
      state->du.a.io.inzerosamples = SNDLATENCY;
      memset(buf, 0, sizeof(buf));
      write(state->du.a.io.fdout, buf, sizeof(buf));
      return;

  errsnd:
      stop(state);
      state->du.a.p.sratedspin = state->du.a.p.sratedspout = state->du.a.p.srateusb;
}

/* ---------------------------------------------------------------------- */

void audio_globalinit(void)
{
}

struct trxapi_baycomusb_adapter_audio_devs *audio_get_device_list(void)
{
      struct trxapi_baycomusb_adapter_audio_devs *ad;

      /* retrieve audio devs */
      if (!(ad = calloc(1, sizeof(struct trxapi_baycomusb_adapter_audio_devs) + 
                    4 * sizeof(trxapi_audiodevice_t) + 
                    4 * sizeof(trxapi_audiodevice_t))))
            return NULL;
      ad->audiodevsin = (void *)(ad + 1);
      ad->nraudiodevsin = 4;
      ad->audiodevsout = &ad->audiodevsin[4];
      ad->nraudiodevsout = 4;
      strncpy(ad->audiodevsin[0], "/dev/dsp", sizeof(ad->audiodevsin[0]));
      strncpy(ad->audiodevsin[1], "/dev/dsp1", sizeof(ad->audiodevsin[1]));
      strncpy(ad->audiodevsin[2], "/dev/dsp2", sizeof(ad->audiodevsin[2]));
      strncpy(ad->audiodevsin[3], "/dev/dsp3", sizeof(ad->audiodevsin[3]));
      strncpy(ad->audiodevsout[0], "/dev/dsp", sizeof(ad->audiodevsout[0]));
      strncpy(ad->audiodevsout[1], "/dev/dsp1", sizeof(ad->audiodevsout[1]));
      strncpy(ad->audiodevsout[2], "/dev/dsp2", sizeof(ad->audiodevsout[2]));
      strncpy(ad->audiodevsout[3], "/dev/dsp3", sizeof(ad->audiodevsout[3]));
      return ad;
}

void audio_open(struct trx_thread_state *state)
{
      state->du.a.p.srateusb = state->du.a.p.sratedspin = state->du.a.p.sratedspout = AUDIOSAMPLINGRATE;
      start(state);
      audioproc_init(state);
}

void audio_close(struct trx_thread_state *state)
{
      stop(state);
}

void audio_input(struct trx_thread_state *state, const signed char *samples, unsigned int nrsamples)
{
      int16_t sbuf[BUFSIZE];
      unsigned int apar, cnt;
      int r;

      /* usb->dsp direction */
      cnt = audioproc_convertoutput(state, nrsamples, samples, sbuf);
      if (state->du.a.io.fdout == -1)
            return;
      if ((r = write(state->du.a.io.fdout, sbuf, 2*cnt)) != 2*cnt) {
            lprintf(0, "write(%d) failed %i (%s)\n", 2*cnt, r, strerror(errno));
            goto err;
      }
      if (ioctl(state->du.a.io.fdout, SNDCTL_DSP_GETODELAY, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETODELAY failed (%s)\n", strerror(errno));
            goto err;
      }
      apar >>= 1;
      /* adjust speed */
      if (audioproc_adjustoutput(state, apar))
            goto err;
      return;

  err:
      stop(state);
}

void audio_output(struct trx_thread_state *state, signed char *samples, unsigned int nrsamples)
{
      int16_t *sbuf;
      unsigned int isamples;
      audio_buf_info abinfo;
      int r;

      /* dsp->usb direction */
      if (!nrsamples)
            return;
      if (state->du.a.io.fdin == -1) {
            isamples = audioproc_convertinput_isamples(state, nrsamples);
            sbuf = alloca(isamples * sizeof(sbuf[0]));
            memset(sbuf, 0, isamples * sizeof(sbuf[0]));
            audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
            return;
      }
      isamples = audioproc_convertinput_isamples(state, nrsamples);
      sbuf = alloca(isamples * sizeof(sbuf[0]));
      memset(sbuf, 0, isamples * sizeof(sbuf[0]));
      if (state->du.a.io.inzerosamples >= isamples) {
            state->du.a.io.inzerosamples -= isamples;
      } else {
            state->du.a.io.inzerosamples = 0;
            r = read(state->du.a.io.fdin, sbuf+state->du.a.io.inzerosamples, (isamples-state->du.a.io.inzerosamples) * sizeof(sbuf[0]));
            if (r == -1 && errno != EAGAIN) {
                  audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
                  lprintf(0, "read failed %i (%s)\n", r, strerror(errno));
                  goto err;
            }
      }
      audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
      if (ioctl(state->du.a.io.fdin, SNDCTL_DSP_GETISPACE, &abinfo) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETISPACE failed (%s)\n", strerror(errno));
            goto err;
      }
      /* adjust speed */
      if (audioproc_adjustinput(state, abinfo.bytes / sizeof(sbuf[0]) + state->du.a.io.inzerosamples))
            goto err;
      return;

  err:
      stop(state);
}

/* ---------------------------------------------------------------------- */
#else /* !HAVE_LINUX_SOUNDCARD_H && !HAVE_SYS_SOUNDCARD_H */

void audio_globalinit(void)
{
}

struct trxapi_baycomusb_adapter_audio_devs *audio_get_device_list(void)
{
      return NULL;
}

void audio_open(struct trx_thread_state *state)
{
      state->du.a.p.srateusb = state->du.a.p.sratedspin = state->du.a.p.sratedspout = AUDIOSAMPLINGRATE;
      audioproc_init(state);
}

void audio_close(struct trx_thread_state *state)
{
}

void audio_input(struct trx_thread_state *state, const signed char *samples, unsigned int nrsamples)
{
      audioproc_convertoutput(state, nrsamples, samples, sbuf);
}

void audio_output(struct trx_thread_state *state, signed char *samples, unsigned int nrsamples)
{
      int16_t *sbuf;
      unsigned int isamples;

      if (!nrsamples)
            return;
      isamples = audioproc_convertinput_isamples(state, nrsamples);
      sbuf = alloca(isamples * sizeof(sbuf[0]));
      memset(sbuf, 0, isamples * sizeof(sbuf[0]));
      audioproc_convertinput(state, isamples, nrsamples, sbuf, samples);
}

/* ---------------------------------------------------------------------- */
#endif /* HAVE_LINUX_SOUNDCARD_H || HAVE_SYS_SOUNDCARD_H */

Generated by  Doxygen 1.6.0   Back to index