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

audio.c

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

/*
 *      audio.c  --  Audio processing for "virtual transceiver".
 *
 *      Copyright (C) 1999-2000  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

#include <sys/types.h>

#define G_LOG_DOMAIN "eppfm"

#ifdef HAVE_LINUX_SOUNDCARD_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>
#endif      

#ifdef HAVE_DIRECTX
#include <directx.h>
#endif

#include "eppfm.h"

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

struct {
      /* gtk timeout handle */
      gint tmotag;
      /* sampling rate conversion state */
      unsigned phinceppdsp, phincdspepp, pheppdsp, phdspepp;
      /* ptt state */
      unsigned ptt_state;
      /* control register */
      unsigned char ctrlreg;
      /* led blink counter */
      unsigned ledcnt;
      /* level counter */
      unsigned lvlicnt, lvliaccum, lvli, lvlocnt, lvloaccum, lvlo;
      /* frequency generators */
      unsigned phase0, phase1, phase2, phase3;
      unsigned freq0, freq1, freq2, freq3;
      
      /* low level driver specific data */

#ifdef HAVE_LINUX_SOUNDCARD_H
      int fddsp;
#endif      
#ifdef HAVE_DIRECTX
      LPDIRECTSOUND dsplay;
      LPDIRECTSOUNDCAPTURE dsrec;
      LPDIRECTSOUNDBUFFER playbuf;
      LPDIRECTSOUNDCAPTUREBUFFER recbuf;
      HANDLE hinst;
      HWND hwnd;
      DWORD playbufsz, recbufsz, playptr, recptr;
#endif

} state;

#define SNDLATENCY 4000   /* approx 1/2 seconds */
#define BUFSIZE    8192   /* must be significantly bigger than SNDLATENCY! */
#define PHASEFRAC  12

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

void audio_ptt(int ptt)
{
      if (ptt)
            state.ptt_state |= 1;
      else
            state.ptt_state &= ~1;
}

void audio_1750(int on)
{
      if (on) {
            state.freq2 = (1750*65536+8000/2)/8000;
      } else {
            state.freq2 = 0;
            state.phase2 = 0x4000;
      }
}

/*
 *
 * DTMF frequencies
 *
 *      1209 1336 1477 1633
 *  697   1    2    3    A
 *  770   4    5    6    B
 *  852   7    8    9    C
 *  941   *    0    #    D
 * 
 */

void audio_dtmf(int ch)
{
      switch (ch) {
      case 0:
            state.freq0 = (1336*65536+8000/2)/8000;
            state.freq1 = (941*65536+8000/2)/8000;
            break;

      case 1:
            state.freq0 = (1209*65536+8000/2)/8000;
            state.freq1 = (697*65536+8000/2)/8000;
            break;

      case 2:
            state.freq0 = (1336*65536+8000/2)/8000;
            state.freq1 = (697*65536+8000/2)/8000;
            break;

      case 3:
            state.freq0 = (1477*65536+8000/2)/8000;
            state.freq1 = (697*65536+8000/2)/8000;
            break;

      case 4:
            state.freq0 = (1209*65536+8000/2)/8000;
            state.freq1 = (770*65536+8000/2)/8000;
            break;

      case 5:
            state.freq0 = (1336*65536+8000/2)/8000;
            state.freq1 = (770*65536+8000/2)/8000;
            break;

      case 6:
            state.freq0 = (1477*65536+8000/2)/8000;
            state.freq1 = (770*65536+8000/2)/8000;
            break;

      case 7:
            state.freq0 = (1209*65536+8000/2)/8000;
            state.freq1 = (852*65536+8000/2)/8000;
            break;

      case 8:
            state.freq0 = (1336*65536+8000/2)/8000;
            state.freq1 = (852*65536+8000/2)/8000;
            break;

      case 9:
            state.freq0 = (1477*65536+8000/2)/8000;
            state.freq1 = (852*65536+8000/2)/8000;
            break;

      case 10:
            state.freq0 = (1633*65536+8000/2)/8000;
            state.freq1 = (697*65536+8000/2)/8000;
            break;

      case 11:
            state.freq0 = (1633*65536+8000/2)/8000;
            state.freq1 = (770*65536+8000/2)/8000;
            break;

      case 12:
            state.freq0 = (1633*65536+8000/2)/8000;
            state.freq1 = (852*65536+8000/2)/8000;
            break;

      case 13:
            state.freq0 = (1633*65536+8000/2)/8000;
            state.freq1 = (941*65536+8000/2)/8000;
            break;

      case 14:
            state.freq0 = (1209*65536+8000/2)/8000;
            state.freq1 = (941*65536+8000/2)/8000;
            break;

      case 15:
            state.freq0 = (1477*65536+8000/2)/8000;
            state.freq1 = (941*65536+8000/2)/8000;
            break;

      default:
            state.freq0 = state.freq1 = 0;
            state.phase0 = state.phase1 = 0x4000;
            break;
      }
}

unsigned audio_getilevel(void)
{
      return state.lvli;
}

unsigned audio_getolevel(void)
{
      return state.lvlo;
}

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

#define LEVELACCUM 2048

static void process_input(int16_t *buf, unsigned cnt)
{
      unsigned i;

      for (i = 0; i < cnt; i++) {
            state.lvliaccum += ((int)buf[i] * (int)buf[i]) >> 16;
            if ((++state.lvlicnt) >= LEVELACCUM) {
                  state.lvli = state.lvliaccum / LEVELACCUM;
                  state.lvlicnt = state.lvliaccum = 0;
            }
      }
}

static void process_output(int16_t *buf, unsigned cnt)
{
      unsigned i;
      int acc;

      for (i = 0; i < cnt; i++) {
            acc = buf[i];
            state.lvloaccum += (acc * acc) >> 16;
            if ((++state.lvlocnt) >= LEVELACCUM) {
                  state.lvlo = state.lvloaccum / LEVELACCUM;
                  state.lvlocnt = state.lvloaccum = 0;
            }
            acc += (SIN(state.phase0) + SIN(state.phase1) + SIN(state.phase2) + SIN(state.phase3)) >> 1;
            if (acc > 32767)
                  acc = 32767;
            else if (acc < -32768)
                  acc = -32768;
            buf[i] = acc;
            state.phase0 += state.freq0;
            state.phase1 += state.freq1;
            state.phase2 += state.freq2;
            state.phase3 += state.freq3;
      }
}

static void initstate(void)
{
      state.pheppdsp = 0;
      state.phdspepp = 0;
      state.ptt_state = 0;
      state.ctrlreg = 0;
      state.ledcnt = 0;
      state.lvlicnt = state.lvliaccum = state.lvli = 0;
      state.lvlocnt = state.lvloaccum = state.lvlo = 0;
      state.phase0 = state.phase1 = state.phase2 = state.phase3 = 0x4000;
      state.freq0 = state.freq1 = state.freq2 = state.freq3 = 0;
}

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

#ifdef HAVE_LINUX_SOUNDCARD_H

void audio_stop(void)
{
      if (state.fddsp != -1) {
            gtk_timeout_remove(state.tmotag);
            close(state.fddsp);
            state.fddsp = -1;
            reset_modem();
      }
}

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

      /* first do the 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;
            if (parport_epp_read_data(buf.s, icnt) != icnt)
                  goto err;
            state.pheppdsp &= 0xff;
            cnt = 0;
            while (state.pheppdsp < (icnt << PHASEFRAC)) {
                  sbuf[cnt] = buf.s[state.pheppdsp >> PHASEFRAC] << 8;
                  state.pheppdsp += state.phinceppdsp;
                  cnt++;
            }
            process_input(sbuf, cnt);
            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 */
            lprintf(4, "icnt %d cnt %d odel %d\n", icnt, cnt, apar);
            if (apar > SNDLATENCY)
                  state.phinceppdsp++;
            else if (apar < SNDLATENCY)
                  state.phinceppdsp--;
            if (state.phinceppdsp < (0x9 << (PHASEFRAC-4)) || state.phinceppdsp > (0x1f << (PHASEFRAC-4))) {
                  lprintf(0, "phinceppdsp (0x%05x) out of range\n", state.phinceppdsp);
                  goto err;
            }
            /* play games with the LEDS */
            state.ledcnt += icnt;
            if (state.ledcnt >= 4000) {
                  state.ledcnt %= 4000;
                  state.ctrlreg += 0x40;
            }
      }
      /* next do the dsp->epp direction */
      /* read sound */
      ocnts = read(state.fddsp, sbuf, 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;
      process_output(sbuf, ocnts);
      if (state.ptt_state == 2) {
            state.ptt_state = 0;
            state.ctrlreg &= ~0x20;
            buf.u[0] = state.ctrlreg;
            if (parport_epp_write_addr(buf.u, 1) != 1)
                  goto err;
      }
      if (!state.ptt_state)
            goto endper;
      /* get FIFO count */
      state.ctrlreg |= 0x20;
      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;
      if (state.ptt_state == 1) {
            if (ocnt < SNDLATENCY) {
                  memset(buf.s, 0, SNDLATENCY-ocnt);
                  if (parport_epp_write_data(buf.s, SNDLATENCY-ocnt) != SNDLATENCY-ocnt)
                        goto err;
            }
            state.ptt_state = 3;
            goto endper;
      }
      if (ocnts > 0) {
            state.phdspepp &= 0xff;
            cnt = 0;
            while (state.phdspepp < (ocnts << PHASEFRAC)) {
                  buf.s[cnt] = sbuf[state.phdspepp >> PHASEFRAC] >> 8;
                  state.phdspepp += state.phincdspepp;
                  cnt++;
            }
            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 */
      lprintf(4, "ocnts %d cnt %d ocnt %d\n", ocnts, cnt, ocnt);
      if (ocnt > SNDLATENCY)
            state.phincdspepp++;
      else if (ocnt < SNDLATENCY)
            state.phincdspepp--;
      if (state.phincdspepp < (0x9 << (PHASEFRAC-4)) || state.phincdspepp > (0x1f << (PHASEFRAC-4))) {
            lprintf(0, "phincdspepp (0x%05x) out of range\n", state.phincdspepp);
            goto err;
      }
endper:
      lprintf(3, "phase increments: 0x%05x 0x%05x\n", state.phinceppdsp, state.phincdspepp);
      return TRUE;

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

int audio_start(struct adapter_config *cfg)
{
      unsigned char buf[2];
      int16_t sbuf[SNDLATENCY];
      audio_buf_info aboinfo, abiinfo;
      int apar;
      unsigned srate;
      int i;

      initstate();
      cfg->bitrate = 8000;
      if ((i = adapter_start_eppsamp(cfg))) {
            lprintf(0, "Cannot initialize the modem\n");
            return i;
      }
      if ((state.fddsp = open("/dev/dsp", O_RDWR | O_NONBLOCK)) == -1) {
            lprintf(0, "cannot open /dev/dsp (%s)\n", strerror(errno));
            return -1;
      }
      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_DUPLEX)) {
            lprintf(0, "full duplex soundcard required\n");
            goto errsnd;
      }
      if (!(apar & DSP_CAP_TRIGGER)) {
            lprintf(0, "soundcard does not support trigger\n");
            goto errsnd;
      }
      if (ioctl(state.fddsp, SNDCTL_DSP_SETDUPLEX, 0) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_SETDUPLEX failed (%s)\n", strerror(errno));
            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;
      }
      state.phinceppdsp = ((1 << PHASEFRAC) * cfg->bitrate + srate / 2) / srate;
      state.phincdspepp = ((1 << PHASEFRAC) * srate + cfg->bitrate / 2) / cfg->bitrate;
      lprintf(2, "epp->dsp phase inc: 0x%05x  dsp->epp phase inc: 0x%05x\n", state.phinceppdsp, state.phincdspepp);
      if (ioctl(state.fddsp, SNDCTL_DSP_GETOSPACE, &aboinfo) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETOSPACE failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (ioctl(state.fddsp, SNDCTL_DSP_GETISPACE, &abiinfo) == -1) {
            lprintf(0, "ioctl SNDCTL_DSP_GETISPACE failed (%s)\n", strerror(errno));
            goto errsnd;
      }
      if (aboinfo.fragstotal * aboinfo.fragsize < 2*SNDLATENCY*2 ||
          abiinfo.fragstotal * abiinfo.fragsize < 2*SNDLATENCY*2) {
            lprintf(0, "soundcard buffers too small (%u,%u)\n", 
                  aboinfo.fragstotal * aboinfo.fragsize, 
                  abiinfo.fragstotal * abiinfo.fragsize);
            goto errsnd;
      }
      lprintf(0, "Audio IO to Linux Soundcard\n");
      /* reset the EPP adapter */
      buf[0] = 7;
      buf[1] = 0;
      if (parport_epp_write_addr(buf, 2) != 2)
            goto errret;
      /* prefill to nominal queue size and stard 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_INPUT | 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, NULL);
      return 0;

errret:
      lprintf(0, "EPP timeout\n");
errsnd:
      close(state.fddsp);
      return -1;
}

#endif /* HAVE_LINUX_SOUNDCARD_H */

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

#ifdef HAVE_DIRECTX

static BOOL CALLBACK DSEnumProc(LPGUID guid, LPCSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpcontext)
{
        lprintf(1, "has %sGUID, desc %s drvname %s\n", guid ? "" : "no ", lpszDesc, lpszDrvName);
        return TRUE;
}

#if 0
static LRESULT CALLBACK MyWndProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
      switch(umsg) {
            /* Add cases such as WM_CREATE, WM_COMMAND, WM_PAINT if you don't 
               want to pass these messages along for default processing. */
      case WM_CLOSE:
            DestroyWindow(hwnd);
            return 0;

      case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
      }
      return DefWindowProc(hwnd, umsg, wParam, lParam);
}
#endif

void audio_stop(void)
{
      if (state.hwnd != NULL) {
            gtk_timeout_remove(state.tmotag);
            IDirectSoundBuffer_Stop(state.playbuf);
            IDirectSoundCaptureBuffer_Stop(state.recbuf);
            IDirectSoundBuffer_Release(state.playbuf);
            IDirectSoundCaptureBuffer_Release(state.recbuf);
            IDirectSoundCapture_Release(state.dsrec);
            IDirectSound_Release(state.dsplay);
#if 0
            DestroyWindow(state.hwnd);
            UnregisterClass("MyClass", state.hinst);
#endif
            state.hwnd = NULL;
            reset_modem();
      }
}

static gint periodic(gpointer data)
{
      union {
            unsigned char u[BUFSIZE];
            signed char s[BUFSIZE];
      } buf;
      int16_t sbuf[BUFSIZE];
      int16_t *sptr1, *sptr2;
      HRESULT res;
      DWORD lockbytes1, lockbytes2, delay;
      int icnt, ocnt, cnt, ocnts, omax;

      /* first do the 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;
            if (parport_epp_read_data(buf.s, icnt) != icnt)
                  goto err;
            state.pheppdsp &= 0xff;
            cnt = 0;
            while (state.pheppdsp < (icnt << PHASEFRAC)) {
                  sbuf[cnt] = buf.s[state.pheppdsp >> PHASEFRAC] << 8;
                  state.pheppdsp += state.phinceppdsp;
                  cnt++;
            }
            process_input(sbuf, cnt);
            if (FAILED(res = IDirectSoundBuffer_Lock(state.playbuf, state.playptr, cnt*2, 
                                           (LPVOID)&sptr1, &lockbytes1,
                                           (LPVOID)&sptr2, &lockbytes2, 0))) {
                  lprintf(0, "IDirectSoundBuffer_Lock error %lu\n", res);
                  goto err;
            }
            memcpy(sptr1, sbuf, lockbytes1);
            if (lockbytes1 < 2*cnt)
                  memcpy(sptr2, sbuf + lockbytes1/2, 2*cnt - lockbytes1);
            if (FAILED(res = IDirectSoundBuffer_Unlock(state.playbuf, sptr1, lockbytes1, sptr2, lockbytes2))) {
                  lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
                  goto err;
            }
            state.playptr = (state.playptr + 2*cnt) % state.playbufsz;
            /* get output delay */
            if (FAILED(res = IDirectSoundBuffer_GetCurrentPosition(state.playbuf, &delay, NULL))) {
                  lprintf(0, "IDirectSoundBuffer_GetCurrentPosition error %lu\n", res);
                  goto err;
            }
            delay = (state.playbufsz + state.playptr - delay) % state.playbufsz;
            delay >>= 1;
            /* adjust speed */
            lprintf(4, "icnt %d cnt %d odel %u\n", icnt, cnt, delay);
            if (delay > SNDLATENCY)
                  state.phinceppdsp++;
            else if (delay < SNDLATENCY)
                  state.phinceppdsp--;
            if (state.phinceppdsp < (0x9 << (PHASEFRAC-4)) || state.phinceppdsp > (0x1f << (PHASEFRAC-4))) {
                  lprintf(0, "phinceppdsp (0x%05x) out of range\n", state.phinceppdsp);
                  goto err;
            }
            /* play games with the LEDS */
            state.ledcnt += icnt;
            if (state.ledcnt >= 4000) {
                  state.ledcnt %= 4000;
                  state.ctrlreg += 0x40;
            }
      }
      /* next do the dsp->epp direction */
      /* read sound */
      if (FAILED(res = IDirectSoundCaptureBuffer_GetCurrentPosition(state.recbuf, &delay, NULL))) {
            lprintf(0, "IDirectSoundCaptureBuffer_GetCurrentPosition error %lu\n", res);
            goto err;
      }
      ocnts = (state.recbufsz + delay - state.recptr) % state.recbufsz;
      if (ocnts > 0) {
            if (FAILED(res = IDirectSoundCaptureBuffer_Lock(state.recbuf, state.recptr, ocnts, 
                                                (LPVOID)&sptr1, &lockbytes1,
                                                (LPVOID)&sptr2, &lockbytes2, 0))) {
                  lprintf(0, "IDirectSoundCaptureBuffer_Lock error %lu\n", res);
                  goto err;
            }
            memcpy(sbuf, sptr1, lockbytes1);
            if (lockbytes1 < ocnts)
                  memcpy(sbuf + lockbytes1/2, sptr2, ocnts - lockbytes1);
            state.recptr = (state.recptr + ocnts) % state.recbufsz;
            if (FAILED(res = IDirectSoundCaptureBuffer_Unlock(state.recbuf, sptr1, lockbytes1, sptr2, lockbytes2))) {
                  lprintf(0, "IDirectSoundCaptureBuffer_Unlock error %lu\n", res);
                  goto err;
            }
      }
      ocnts >>= 1;
      process_output(sbuf, ocnts);
      if (state.ptt_state == 2) {
            state.ptt_state = 0;
            state.ctrlreg &= ~0x20;
            buf.u[0] = state.ctrlreg;
            if (parport_epp_write_addr(buf.u, 1) != 1)
                  goto err;
      }
      if (!state.ptt_state)
            goto endper;
      /* get FIFO count */
      state.ctrlreg |= 0x20;
      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;
      if (state.ptt_state == 1) {
            if (ocnt < SNDLATENCY) {
                  memset(buf.s, 0, SNDLATENCY-ocnt);
                  if (parport_epp_write_data(buf.s, SNDLATENCY-ocnt) != SNDLATENCY-ocnt)
                        goto err;
            }
            state.ptt_state = 3;
            goto endper;
      }
      if (ocnts > 0) {
            state.phdspepp &= 0xff;
            cnt = 0;
            while (state.phdspepp < (ocnts << PHASEFRAC)) {
                  buf.s[cnt] = sbuf[state.phdspepp >> PHASEFRAC] >> 8;
                  state.phdspepp += state.phincdspepp;
                  cnt++;
            }
            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 */
      lprintf(4, "ocnts %d cnt %d ocnt %d\n", ocnts, cnt, ocnt);
      if (ocnt > SNDLATENCY)
            state.phincdspepp++;
      else if (ocnt < SNDLATENCY)
            state.phincdspepp--;
      if (state.phincdspepp < (0x9 << (PHASEFRAC-4)) || state.phincdspepp > (0x1f << (PHASEFRAC-4))) {
            lprintf(0, "phincdspepp (0x%05x) out of range\n", state.phincdspepp);
            goto err;
      }
endper:
      lprintf(3, "phase increments: 0x%05x 0x%05x\n", state.phinceppdsp, state.phincdspepp);
      return TRUE;

err:
      lprintf(0, "EPP timeout\n");
      IDirectSoundBuffer_Stop(state.playbuf);
      IDirectSoundCaptureBuffer_Stop(state.recbuf);
      IDirectSoundBuffer_Release(state.playbuf);
      IDirectSoundCaptureBuffer_Release(state.recbuf);
      IDirectSoundCapture_Release(state.dsrec);
      IDirectSound_Release(state.dsplay);
#if 0
      DestroyWindow(state.hwnd);
      UnregisterClass("MyClass", state.hinst);
#endif
      state.hwnd = NULL;
      reset_modem();
      return FALSE;
}

int audio_start(struct adapter_config *cfg)
{
      WNDCLASS wndclass;
      HRESULT res;
        WAVEFORMATEX waveformat;
        DSBUFFERDESC bdesc;
        DSCBUFFERDESC cbdesc;
      DWORD lockbytes;
      int16_t *sptr;
      int retval = -1;
      unsigned char buf[2];
      int i;

      state.dsplay = NULL;
      state.dsrec = NULL;
      state.playbuf = NULL;
      state.recbuf = NULL;
      state.hwnd = NULL;
      state.hinst = GetModuleHandleA(0);
      state.playptr = 0;
      state.recptr = 0;
      initstate();
        lprintf(1, "DirectSound drivers\n");
        DirectSoundEnumerateA(DSEnumProc, NULL);
        lprintf(1, "DirectSoundCapture drivers\n");
        DirectSoundCaptureEnumerateA(DSEnumProc, NULL);
#if 0
      wndclass.style = CS_HREDRAW | CS_VREDRAW;
      wndclass.lpfnWndProc = (WNDPROC)MyWndProc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hIcon = NULL;
      wndclass.hInstance = state.hinst;
      wndclass.hCursor = NULL;
      wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      wndclass.lpszMenuName = NULL;
      wndclass.lpszClassName = "MyClass";
      if (!RegisterClass(&wndclass)) {
            lprintf(0, "RegisterClass error, %d\n", GetLastError());
            return -1;
      }
      state.hwnd = CreateWindowEx(WS_EX_APPWINDOW, "MyClass", "eppfpga", WS_DISABLED | WS_POPUP,
                            0, 0, 0, 0, NULL, NULL, state.hinst, NULL);
      if (!state.hwnd) {
            lprintf(0, "CreateWindowEx error, %d\n", GetLastError());
            goto errcreatewin;
      }
#else
      state.hwnd = GetDesktopWindow();
#endif
        if (FAILED(res = DirectSoundCreate(NULL, &state.dsplay, NULL))) {
                lprintf(0, "DirectSoundCreate error %lu\n", res);
                goto errdscreate;
        }
        if (FAILED(res = IDirectSound_SetCooperativeLevel(state.dsplay, state.hwnd, DSSCL_NORMAL))) {
                lprintf(0, "SetCooperativeLevel error %lu\n", res);
                goto errdscreate1;
        }
        if (FAILED(res = DirectSoundCaptureCreate(NULL, &state.dsrec, NULL))) {
                lprintf(0, "DirectSoundCaptureCreate error %lu\n", res);
                goto errdscreate1;
        }
        memset(&waveformat, 0, sizeof(waveformat));
        waveformat.wFormatTag = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 8;
        waveformat.nChannels = 1;
        waveformat.nSamplesPerSec = 8000;
        waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
        waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
        memset(&cbdesc, 0, sizeof(cbdesc));
        cbdesc.dwSize = sizeof(cbdesc);
        cbdesc.dwFlags = /* DSCBCAPS_WAVEMAPPED */ 0;
        state.recbufsz = cbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec;
        cbdesc.lpwfxFormat = &waveformat;
        if (FAILED(res = IDirectSoundCapture_CreateCaptureBuffer(state.dsrec, &cbdesc, &state.recbuf, NULL))) {
                lprintf(0, "CreateSoundCaptureBuffer error %lu\n", res);
                goto errdscreate2;
        }
        memset(&waveformat, 0, sizeof(waveformat));
        waveformat.wFormatTag = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 8;
        waveformat.nChannels = 1;
        waveformat.nSamplesPerSec = 8000;
        waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
        waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
        memset(&bdesc, 0, sizeof(bdesc));
        bdesc.dwSize = sizeof(bdesc);
        bdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN;
        state.playbufsz = bdesc.dwBufferBytes = waveformat.nAvgBytesPerSec;
        bdesc.lpwfxFormat = &waveformat;
        if (FAILED(res = IDirectSound_CreateSoundBuffer(state.dsplay, &bdesc, &state.playbuf, NULL))) {
                lprintf(0, "CreateSoundBuffer error %lu\n", res);
                goto errdscreate3;
        }
      cfg->bitrate = 8000;
      if ((i = adapter_start_eppsamp(cfg))) {
            retval = i;
            lprintf(0, "Cannot initialize the modem\n");
            goto errdscreate4;
      }
      state.phinceppdsp = ((1 << PHASEFRAC) * cfg->bitrate + 8000 / 2) / 8000;
      state.phincdspepp = ((1 << PHASEFRAC) * 8000 + cfg->bitrate / 2) / cfg->bitrate;
      lprintf(2, "epp->dsp phase inc: 0x%05x  dsp->epp phase inc: 0x%05x\n", state.phinceppdsp, state.phincdspepp);
      lprintf(0, "Audio IO to DirectX\n");
      /* reset the EPP adapter */
      buf[0] = 7;
      buf[1] = 0;
      if (parport_epp_write_addr(buf, 2) != 2)
            goto errret;
      /* prefill to nominal queue size and stard soundcard */
      if (FAILED(res = IDirectSoundBuffer_Lock(state.playbuf, 0, 2*SNDLATENCY, (LPVOID)&sptr, &lockbytes, NULL, NULL, 0))) {
                lprintf(0, "IDirectSoundBuffer_Lock error %lu\n", res);
                goto errresmodem;
        }
      memset(sptr, 0, 2*SNDLATENCY);
      if (FAILED(res = IDirectSoundBuffer_Unlock(state.playbuf, sptr, lockbytes, NULL, 0))) {
                lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
                goto errresmodem;
        }
      state.playptr = SNDLATENCY;
      if (FAILED(res = IDirectSoundBuffer_Play(state.playbuf, 0, 0, DSBPLAY_LOOPING))) {
                lprintf(0, "IDirectSoundBuffer_Play error %lu\n", res);
                goto errresmodem;
        }
      if (FAILED(res = IDirectSoundCaptureBuffer_Start(state.recbuf, DSCBSTART_LOOPING))) {
                lprintf(0, "IDirectSoundCaptureBuffer_Start error %lu\n", res);
                goto errresmodem;
        }
      state.tmotag = gtk_timeout_add(100, periodic, NULL);
      return 0;

errret:
      lprintf(0, "EPP timeout\n");
errresmodem:
      IDirectSoundBuffer_Stop(state.playbuf);
      IDirectSoundCaptureBuffer_Stop(state.recbuf);
      reset_modem();
errdscreate4:
      IDirectSoundBuffer_Release(state.playbuf);
errdscreate3:
      IDirectSoundCaptureBuffer_Release(state.recbuf);
errdscreate2:
      IDirectSoundCapture_Release(state.dsrec);
errdscreate1:
      IDirectSound_Release(state.dsplay);
errdscreate:
#if 0
      DestroyWindow(state.hwnd);
errcreatewin:
      UnregisterClass("MyClass", state.hinst);
#endif
      return -1;
}

#endif /* HAVE_DIRECTX */

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

Generated by  Doxygen 1.6.0   Back to index