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

audiolinux.c

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

/*
 *      audio.c  --  Audio processing for "virtual transceiver", Linux IO.
 *
 *      Copyright (C) 1999  Thomas Sailer (sailer@ife.ee.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

#ifdef HAVE_LINUX_SOUNDCARD_H

#include <sys/types.h>

#include <linux/soundcard.h>
#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 "eppfm.h"

#include <gtk/gtk.h>
#include <glib.h>

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

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

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

struct {
      enum { st_mdmerr, st_off, st_play, st_rec } state;

      /* gtk timeout handle */
      gint tmotag;
      /* sampling rate conversion state */
      int16_t sample[OVERLAP];
      /* control register */
      unsigned char ctrlreg;
      /* led blink counter */
      unsigned ledcnt;
      
      /* low level driver specific data */
      int fddsp;
} state = { st_mdmerr, };

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

static gint periodic_play(gpointer data)
{
      union {
            unsigned char u[BUFSIZE];
            signed char s[BUFSIZE];
      } buf;
      int16_t sbuf[BUFSIZE];
      int apar, i, icnt, cnt;

      /* epp->dsp direction */
      /* get FIFO count */
      buf.u[0] = state.ctrlreg | 1;
      if (parport_epp_write_addr(buf.u, 1) != 1)
            goto err;
      if (parport_epp_read_addr(buf.u, 2) != 2)
            goto err;
      icnt = buf.u[0] | (((unsigned int)buf.u[1]) << 8);
      buf.u[0] = state.ctrlreg;
      if (parport_epp_write_addr(buf.u, 1) != 1)
            goto err;
      icnt &= 0x7fff;
      if (icnt > 0) {
            if (icnt > BUFSIZE/2)
                  icnt = BUFSIZE/2;
            memcpy(buf.s, state.sample, OVERLAP);
            if (parport_epp_read_data(buf.s+OVERLAP, icnt) != icnt)
                  goto err;
            memcpy(state.sample, buf.s+icnt, OVERLAP);
            cnt = audio_convertoutput(icnt, buf.s+OVERLAP, sbuf);
            if ((i = write(state.fddsp, sbuf, 2*cnt)) != 2*cnt) {
                  lprintf(0, "write(%d) failed %i (%s)\n", 2*cnt, i, strerror(errno));
                  goto err;
            }
            if (ioctl(state.fddsp, SNDCTL_DSP_GETODELAY, &apar) == -1) {
                  lprintf(0, "ioctl SNDCTL_DSP_GETODELAY failed (%s)\n", strerror(errno));
                  goto err;
            }
            apar >>= 1;
            /* adjust speed */
            if (audio_adjustoutput(cnt, icnt, apar))
                  goto err;
            /* play games with the LEDS */
            state.ledcnt += icnt;
            if (state.ledcnt >= 4000) {
                  state.ledcnt %= 4000;
                  state.ctrlreg += 0x40;
            }
            /* DCD led */
            if (audio_getsquelch())
                  state.ctrlreg |= 0x10;
            else
                  state.ctrlreg &= ~0x10;
      }
      return TRUE;

  err:
      reset_modem();
      lprintf(0, "EPP timeout\n");
      state.state = st_mdmerr;
      close(state.fddsp);
      return FALSE;
}

static gint periodic_rec(gpointer data)
{
      union {
            unsigned char u[BUFSIZE];
            signed char s[BUFSIZE];
      } buf;
      int16_t sbuf[BUFSIZE];
      int ocnt, cnt, ocnts, omax;

      /* dsp->epp direction */
      /* read sound */
      memcpy(sbuf, state.sample, OVERLAP*2);
      ocnts = read(state.fddsp, sbuf+OVERLAP, 2*BUFSIZE/2);
      if (ocnts == -1 && errno == EAGAIN)
            ocnts = 0;
      if (ocnts < 0) {
            lprintf(0, "read(%d) failed %i (%s)\n", 2*BUFSIZE/2, ocnts, strerror(errno));
            goto err;
      }
      ocnts >>= 1;
      memcpy(state.sample, sbuf+ocnts, OVERLAP*2);
      /* get FIFO count */
      buf.u[0] = state.ctrlreg | 2;
      if (parport_epp_write_addr(buf.u, 1) != 1)
            goto err;
      if (parport_epp_read_addr(buf.u, 2) != 2)
            goto err;
      ocnt = buf.u[0] | (((unsigned int)buf.u[1]) << 8);
      buf.u[0] = state.ctrlreg;
      if (parport_epp_write_addr(buf.u, 1) != 1)
            goto err;
      ocnt &= 0x7fff;
      omax = 16384 - ocnt;
      cnt = 0;
      if (ocnts > 0) {
            cnt = audio_convertinput(ocnts, sbuf+OVERLAP, buf.s);
            if (cnt > omax) {
                  lprintf(0, "epp adapter output overrun (%d, %d)\n", cnt, omax);
                  goto err;
            }
            if (parport_epp_write_data(buf.s, cnt) != cnt)
                  goto err;
            /* reget the FIFO count */
            buf.u[0] = state.ctrlreg | 2;
            if (parport_epp_write_addr(buf.u, 1) != 1)
                  goto err;
            if (parport_epp_read_addr(buf.u, 2) != 2)
                  goto err;
            ocnt = buf.u[0] | (((unsigned int)buf.u[1]) << 8);
            buf.u[0] = state.ctrlreg;
            if (parport_epp_write_addr(buf.u, 1) != 1)
                  goto err;
            ocnt &= 0x7fff;
      }
      /* adjust speed */
      if (audio_adjustinput(cnt, ocnts, ocnt))
            goto err;
      return TRUE;

  err:
      reset_modem();
      lprintf(0, "EPP timeout\n");
      state.state = st_mdmerr;
      close(state.fddsp);
      return FALSE;
}

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

static void playstop(void)
{
      unsigned char buf;

      if (state.state != st_play)
            return;
      gtk_timeout_remove(state.tmotag);
      close(state.fddsp);
      /* reset the EPP adapter */
      buf = 7;
      if (parport_epp_write_addr(&buf, 1) != 1) {
            state.state = st_mdmerr;
            reset_modem();
      } else
            state.state = st_off;
}

static void recstop(void)
{
      unsigned char buf;

      if (state.state != st_rec)
            return;
      gtk_timeout_remove(state.tmotag);
      close(state.fddsp);     
      /* reset the EPP adapter */
      buf = 7;
      if (parport_epp_write_addr(&buf, 1) != 1) {
            state.state = st_mdmerr;
            reset_modem();
      } else
            state.state = st_off;
}

static void playstart(void)
{
      unsigned char buf[2];
      int16_t sbuf[SNDLATENCY];
      audio_buf_info abinfo;
      int apar;
      unsigned srate;
      int i;

      if (state.state == st_play)
            playstop();
      if (state.state == st_rec)
            recstop();
      state.ctrlreg = 0;
      state.ledcnt = 0;
      memset(state.sample, 0, sizeof(state.sample));
      /* start modem if mdmerr */
      if (state.state == st_mdmerr) {
            cfg.bitrate = SAMPLINGRATE;
            if ((i = adapter_start_eppsamp(&cfg))) {
                  lprintf(0, "Cannot initialize the modem\n");
                  return;
            }
      }
      state.state = st_off;
      if ((state.fddsp = open("/dev/dsp", O_WRONLY | O_NONBLOCK)) == -1) {
            lprintf(0, "cannot open /dev/dsp (%s)\n", strerror(errno));
            return;
      }
      fcntl(state.fddsp, F_SETFL, fcntl(state.fddsp, F_GETFL, 0) | O_NONBLOCK);
      if (ioctl(state.fddsp, SNDCTL_DSP_NONBLOCK, 0) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_NONBLOCK failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (ioctl(state.fddsp, 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;
      }
      apar = AFMT_S16_LE;
      if (ioctl(state.fddsp, SNDCTL_DSP_SETFMT, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SETFMT failed (%s), S16_LE not supported??\n", strerror(errno));
            goto errsnd;
      }
      apar = 0;
      if (ioctl(state.fddsp, SNDCTL_DSP_STEREO, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_STEREO failed (%s), mono not supported??\n", strerror(errno));
            goto errsnd;
      }
      srate = cfg.bitrate;
      if (ioctl(state.fddsp, SNDCTL_DSP_SPEED, &srate) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SPEED failed (%s), samplingrate %u not supported??\n", 
                  strerror(errno), cfg.bitrate);
            goto errsnd;
      }
      lprintf(2, "epp adapter sampling rate %u, soundcard sampling rate %u\n", cfg.bitrate, srate);
      if (abs(cfg.bitrate - srate) > cfg.bitrate / 2) {
            lprintf(0, "sampling rates (%u,%u) too different\n", cfg.bitrate, srate);
            goto errsnd;
      }
      if (ioctl(state.fddsp, 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;
      }
      audio_initoutput(cfg.bitrate, srate);
      lprintf(0, "Audio Output to Linux Soundcard\n");
      /* reset the EPP adapter */
      buf[0] = 7;
      buf[1] = state.ctrlreg;
      if (parport_epp_write_addr(buf, 2) != 2)
            goto errret;
      /* prefill to nominal queue size and start soundcard */
      memset(sbuf, 0, sizeof(sbuf));
      if ((i = write(state.fddsp, sbuf, 2*SNDLATENCY)) != 2*SNDLATENCY) {
            lprintf(0, "write(%d) failed %i (%s)\n", 2*SNDLATENCY, i, strerror(errno));
            goto errsnd;
      }
      apar = PCM_ENABLE_OUTPUT;
      if (ioctl(state.fddsp, SNDCTL_DSP_SETTRIGGER, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SETTRIGGER failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      state.tmotag = gtk_timeout_add(100, periodic_play, NULL);
      state.state = st_play;
      return;

  errsnd:
      buf[0] = 7;
      if (parport_epp_write_addr(buf, 1) != 1)
            goto errret;
      state.state = st_off;
      goto errclose;

  errret:
      lprintf(0, "EPP timeout\n");
      state.state = st_mdmerr;
      reset_modem();
  errclose:
      close(state.fddsp);
}

static void recstart(void)
{
      unsigned char buf[SNDLATENCY];
      audio_buf_info abinfo;
      int apar;
      unsigned srate;
      int i;

      if (state.state == st_play)
            playstop();
      if (state.state == st_rec)
            recstop();
      state.ctrlreg = 0x20;
      state.ledcnt = 0;
      memset(state.sample, 0, sizeof(state.sample));
      /* start modem if mdmerr */
      if (state.state == st_mdmerr) {
            cfg.bitrate = SAMPLINGRATE;
            if ((i = adapter_start_eppsamp(&cfg))) {
                  lprintf(0, "Cannot initialize the modem\n");
                  return;
            }
      }
      state.state = st_off;
      if ((state.fddsp = open("/dev/dsp", O_RDONLY | O_NONBLOCK)) == -1) {
            lprintf(0, "cannot open /dev/dsp (%s)\n", strerror(errno));
            return;
      }
      fcntl(state.fddsp, F_SETFL, fcntl(state.fddsp, F_GETFL, 0) | O_NONBLOCK);
      if (ioctl(state.fddsp, SNDCTL_DSP_NONBLOCK, 0) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_NONBLOCK failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (ioctl(state.fddsp, 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;
      }
      apar = AFMT_S16_LE;
      if (ioctl(state.fddsp, SNDCTL_DSP_SETFMT, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SETFMT failed (%s), S16_LE not supported??\n", strerror(errno));
            goto errsnd;
      }
      apar = 0;
      if (ioctl(state.fddsp, SNDCTL_DSP_STEREO, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_STEREO failed (%s), mono not supported??\n", strerror(errno));
            goto errsnd;
      }
      srate = cfg.bitrate;
      if (ioctl(state.fddsp, SNDCTL_DSP_SPEED, &srate) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SPEED failed (%s), samplingrate %u not supported??\n", 
                  strerror(errno), cfg.bitrate);
            goto errsnd;
      }
      lprintf(2, "epp adapter sampling rate %u, soundcard sampling rate %u\n", cfg.bitrate, srate);
      if (abs(cfg.bitrate - srate) > cfg.bitrate / 2) {
            lprintf(0, "sampling rates (%u,%u) too different\n", cfg.bitrate, srate);
            goto errsnd;
      }
      if (ioctl(state.fddsp, 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;
      }
      audio_initinput(cfg.bitrate, srate);
      lprintf(0, "Audio Input from Linux Soundcard\n");
      /* reset the EPP adapter */
      buf[0] = 7;
      buf[1] = state.ctrlreg;
      if (parport_epp_write_addr(buf, 2) != 2)
            goto errret;
      /* prefill EPP adapter start soundcard */
      memset(buf, 0, sizeof(buf));
      if (parport_epp_write_data(buf, SNDLATENCY) != SNDLATENCY)
            goto errret;
      apar = PCM_ENABLE_INPUT;
      if (ioctl(state.fddsp, SNDCTL_DSP_SETTRIGGER, &apar) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SETTRIGGER failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      state.tmotag = gtk_timeout_add(100, periodic_rec, NULL);
      state.state = st_rec;
      return;

  errsnd:
      buf[0] = 7;
      if (parport_epp_write_addr(buf, 1) != 1)
            goto errret;
      state.state = st_off;
      goto errclose;
      
  errret:
      lprintf(0, "EPP timeout\n");
      state.state = st_mdmerr;
      reset_modem();
  errclose:
      close(state.fddsp);
}

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

void audio_ptt(int ptt)
{
      if (ptt && state.state != st_rec)
            recstart();
      else if (!ptt && state.state != st_play)
            playstart();
}

int audio_drvinit(const char *config)
{
      return 0;
}

/* ---------------------------------------------------------------------- */
#endif /* HAVE_LINUX_SOUNDCARD_H */


Generated by  Doxygen 1.6.0   Back to index