/* Copyright (c) 1991-2002 Doshita Lab. Speech Group, Kyoto University */
/* Copyright (c) 2000-2002 Speech and Acoustics Processing Lab., NAIST */
/*   All rights reserved   */

/* adin_mic_freebsd.c --- adin lib for FreeBSD/snd */

/* $Id: adin_mic_freebsd.c,v 1.2 2002/09/11 22:01:50 ri Exp $ */

/* Thanks to Kentaro Nagatomo for information */
/* All functions are the same as OSS version, except the header filename */

/*
 * These function will not change mixer setting.
 * Use other mixer program to setup mic device.
 * (mute/unmute, volume control, etc.)
 */

#include <sent/stddefs.h>
#include <sent/adin.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>

/* sound header */
/* #include <sys/soundcard.h> */
#include <machine/soundcard.h>

static int audio_fd;		/* audio descriptor */
static boolean need_swap;	/* whether samples need byte swap */
static int fmt;			/* signed 16bit */
struct pollfd fds[1];		/* structure for polling */

#define FREQALLOWRANGE 200	/* acceptable sampling frequency width around 16kHz */
#define POLLINTERVAL 200	/* in miliseconds */


/* check audio port resource and initialize */
/* will be called once at startup time */
boolean
adin_mic_standby(int sfreq, void *arg)
{
  int fmt_can;
  int fmt1, fmt2;                /* sampling format */
  int rfmt;
  int samplerate;
  int stereo;		/* mono */

  /* open device */
  if ((audio_fd = open("/dev/dsp", O_RDONLY)) == -1) {
    perror("adin_mic_standby: open /dev/dsp");
    return(FALSE);
  }

  /* check whether soundcard can record 16bit data */
  /* and set fmt */
  if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt_can) == -1) {
    perror("adin_mic_standby: sndctl_dsp_getfmts");
    return(FALSE);
  }
#ifdef WORDS_BIGENDIAN
  fmt1 = AFMT_S16_BE;
  fmt2 = AFMT_S16_LE;
#else
  fmt1 = AFMT_S16_LE;               /* 16bit signed (little endian) */
  fmt2 = AFMT_S16_BE;               /* (big endian) */
#endif /* WORDS_BIGENDIAN */
  /* fmt2 needs byte swap */
  if (fmt_can & fmt1) {
    fmt = fmt1;
    need_swap = FALSE;
  } else if (fmt_can & fmt2) {
    fmt = fmt2;
    need_swap = TRUE;
  } else {
    fprintf(stderr, "adin_mic_standby: 16bit recording not supported\n");
    return FALSE;
  }
#ifdef DEBUG
  if (need_swap) {
    j_printf("samples need swap\n");
  } else {
    j_printf("samples need not swap\n");
  }
#endif
  
  if (close(audio_fd) != 0) return FALSE;

  /* re-open for recording */
  /* open device */
  if ((audio_fd = open("/dev/dsp", O_RDONLY)) == -1) {
    perror("adin_mic_standby: open /dev/dsp");
    return(FALSE);
  }
  /* set format, samplerate, channels */
  rfmt = fmt;
  if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rfmt) == -1) {
    perror("adin_mic_standby: sndctl_dsp_setfmt");
    return(FALSE);
  }
  if (rfmt != fmt) {
    fprintf(stderr, "adin_mic_standby: 16bit recording not supported\n");
    return FALSE;
  }

  stereo = 0;			/* mono */
  if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
    perror("adin_mic_standby: sndctl_dsp_stereo (mono)");
    return(FALSE);
  }
  if (stereo != 0) {
    fprintf(stderr,"adin_mic_standby: monoral recording not supported\n");
    return FALSE;
  }

  samplerate = sfreq;
  if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &samplerate) == -1) {
    fprintf(stderr, "adin_mic_standby: sndctl_dsp_speed (%dHz)\n", sfreq);
    return(FALSE);
  }
  if (samplerate < sfreq - FREQALLOWRANGE || samplerate > sfreq + FREQALLOWRANGE) {
    fprintf(stderr,"adin_mic_standby: couldn't set sampling rate to near %dHz. (%d)\n", sfreq, samplerate);
    return FALSE;
  }
  if (samplerate != sfreq) {
    fprintf(stderr,"adin_mic_standby: set sampling rate to %dHz\n", samplerate);
  }

  /* set polling status */
  fds[0].fd = audio_fd;
  fds[0].events = POLLIN;
  
  return TRUE;
}
 
/* start recording */
boolean
adin_mic_start()
{
  char buf[2];

  /* Read 1 sample (and ignore it) to tell the audio device start recording.
     (If you knows better way, teach me...) */
  read(audio_fd, buf, 2);
  return(TRUE);
}

/* stop recording */
boolean
adin_mic_stop()
{
  /*
   * Not reset device on each end of speech, just let the buffer overrun...
   * Resetting and restarting of recording device sometimes causes
   * hawling noises at the next recording.
   * I don't now why, so take the easy way... :-(
   */
  return TRUE;
}

/* read samples from audio device */
/* try to read `sampnum' samples and returns actual sample num recorded */
int
adin_mic_read(SP16 *buf, int sampnum)
{
  int size,cnt;
  audio_buf_info info;

  /* wait till at least one sample can be read */
  poll(fds, 1, POLLINTERVAL);
  /* get actual sample num in the device buffer */
  if (ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
    perror("adin_mic_read: sndctl_dsp_getispace");
    return(-2);
  }
  /*printf("%d\n",info.bytes);*/
  
  /* get them as much as possible */
  size = sampnum * sizeof(SP16);
  if (size > info.bytes) size = info.bytes;
  cnt = read(audio_fd, buf, size);
/* 
 *   do {
 *     errno = 0;
 *     cnt = read(audio_fd, buf, sampnum*sizeof(short));
 *   } while ( cnt < 0 && ( errno == EINTR || errno == EAGAIN ) );
 */
  if ( cnt < 0 ) {
    perror("adin_mic_read: read error\n");
    return ( -2 );
  }
  cnt /= sizeof(short);
  if (need_swap) swap_sample_bytes(buf, cnt);
  return(cnt);
}
