/*****************************************************************
query audio device capabilities

$Id: auquery.c,v 1.6 2003/01/19 23:17:09 purnhage Exp $

Source file: auquery.c

(c) Heiko Purnhagen 2001-2003
e-mail: purnhage@tnt.uni-hannover.de
WWW:    http://www.tnt.uni-hannover.de/~purnhage/

This program is available under the terms of the GNU General Public
License. See file COPYING for further details. All usual disclaimers
apply!

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>

Changes:
21-Dec-2001 HP      first version, based on plax.c
30-Dec-2001 HP      things start to work ...
06-Feb-2002 HP      added MIXER_INFO and DSP_GETCAPS for Linux/OSS
2003-01-07  HP      using RDWR, added SNDCTL_DSP_SETDUPLEX before GETCAPS
**********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>

#include "common.h"		/* common module */
#include "cmdline.h"		/* command line module */
#include "playrec.h"		/* audio play & record */

#ifdef Linux
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#endif

/* ---------- declarations ---------- */

#define PROGVER "auquery V1.2 07-Jan-2003"
#define CVSID "$Id: auquery.c,v 1.6 2003/01/19 23:17:09 purnhage Exp $"
#ifndef DISTVER		/* set in Makefile */
#define DISTVER "<unknown>"
#endif

#ifndef MSVC
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif


/* ---------- variables ---------- */

/* command line module */

static char *progName = "<no program name>";
static int specNumChan;
static int specNumChanUsed;
static int lowerNumChan;
static int upperNumChan;
static float specFSample;
static int specFSampleUsed;
static float lowerFSample;
static float upperFSample;
static int queryMode;
static int debugLevel;
static int *varArgIdx;

static CmdLinePara paraList[] = {
  {&varArgIdx,NULL,"<audio devices (dflt: default device)>"},
  {NULL,NULL,NULL}
};

static CmdLineSwitch switchList[] = {
  {"h",NULL,NULL,NULL,NULL,"print help"},
  {"c",&specNumChan,"%d",NULL,&specNumChanUsed,
   "specific number of channels (dflt: range cl .. cu)"},
  {"cl",&lowerNumChan,"%d","0",NULL,
   "lower limit for number of channels\n"
   "(0 = try default / audio panel settings)"},
  {"cu",&upperNumChan,"%d","2",NULL,
   "upper limit for number of channels"},
  {"s",&specFSample,"%f",NULL,&specFSampleUsed,
   "specific sampling frequency [Hz] (dflt: range sl .. su)"},
  {"sl",&lowerFSample,"%f","4000",NULL,
   "lower limit for sampling frequency [Hz]"},
  {"su",&upperFSample,"%f","200000",NULL,
   "upper limit for sampling frequency [Hz]"},
  {"m",&queryMode,"%i","0",NULL,
   "query mode (0=rec&play 1=rec 2=play) (duplex not supported)\n\b"},
  {"d",&debugLevel,"%i","0",NULL,
   "debug level (on stderr) (0=off 1=basic .. 5=full)\n\b"},
  {NULL,NULL,NULL,NULL,NULL,NULL}
};

static int stop = 0;

static float testFSample[] = {-1.,4000.,5000.,6000.,7350.,
			      8000.,10000.,11250.,12000.,15000.,
			      16000.,20000.,22050.,24000.,30000.,
			      32000.,40000.,44056.,44100.,48000.,60000.,
			      64000.,88200.,96000.,100000.,
			      128000.,172400.,192000.,
			      200000.,-2.,-3.};


/* ---------- local functions ---------- */

#ifndef MSVC
void stopproc (int sig)
{
  stop = 1;
  if (sig==SIGPIPE) {
    stop = 2;
    signal(SIGPIPE,stopproc);	/* catch broken output pipe -- again */
  }
  /* 2nd SIGINT will abort immediately */
}
#endif


/* ---------- main ---------- */

int main (int argc, char *argv[])
{
  int result;
  int listIdx;
  int useDfltDev;
  PRStream *qpr;
  char *qDevice;
  int qMode,qf;
  int qNumChan;
  float qFSample;
  int devNumChan;
  float devFSample;
  float lastFSample;

#ifdef Linux
  const char *dn;
  int fd;
  mixer_info minfo;
  int caps;
#endif

  /* evaluate command line  */
  CmdLineInit(0);
  result = CmdLineEval(argc,argv,paraList,switchList,1,&progName);
  if (result) {
    if (result==1) {
      fprintf(stderr,"%s: %s\n"
	      "%s\n"
	      "only 16 bit format supported\n"
	      ,progName,PROGVER "   " DISTVER,CVSID);
      CmdLineHelp(progName,paraList,switchList,stderr);
      exit (1);
    }
    else
      CommonExit(1,"command line error (\"-h\" for help)");
  }
  PRInit(max(-1,debugLevel-2));

  if (debugLevel)
    fprintf(stdout,"%s: %s\n",progName,PROGVER "   " DISTVER);
  if (debugLevel>2) {
    fprintf(stderr,"%s\n",CVSID);
    fprintf(stderr,
	    "only 16 bit format supported, "
	    "debugLevel=%d\n",debugLevel);
  }

#ifndef WIN32
  signal(SIGINT,stopproc);	/* catch ^C */
  signal(SIGPIPE,stopproc);	/* catch broken output pipe */
#endif

  if (specNumChanUsed)
    lowerNumChan = upperNumChan = specNumChan;
  if (specFSampleUsed)
    lowerFSample = upperFSample = specFSample;

  /* process all devices on command line or default device */
  listIdx = 0;
  useDfltDev = varArgIdx[listIdx] < 0;
  while (!stop && (useDfltDev || varArgIdx[listIdx] >= 0)) {

    qDevice = (varArgIdx[listIdx]>=0)?argv[varArgIdx[listIdx]]:(char*)NULL;

    if (debugLevel)
      fprintf(stderr,
	      "%s: querying %s\n",progName,
	      (qDevice)?qDevice:"default audio device");

#ifdef Linux
    dn = (qDevice)?qDevice:"/dev/dsp";
    printf("device %s\n",dn);
    if ((fd=open(dn,O_RDWR,0)) == -1)
      printf("  ... can not open\n");
    else {
      if (ioctl(fd,SOUND_MIXER_INFO,&minfo) != -1)
	printf("  mixer: id=\"%s\" name=\"%s\"\n",minfo.id,minfo.name);
      else
	printf("  mixer: id/name unknown\n");
      ioctl(fd,SNDCTL_DSP_SETDUPLEX,0);
      if (ioctl(fd,SNDCTL_DSP_GETCAPS,&caps) != -1)
	printf("  capabilities: (0x%x) REV=%d "
	       "DUPLEX=%1d REALTIME=%1d BATCH=%1d "
	       "COPROC=%1d TRIGGER=%1d MMAP=%1d\n",
	       caps,
	       caps&DSP_CAP_REVISION,
	       (caps&DSP_CAP_DUPLEX)?1:0,
	       (caps&DSP_CAP_REALTIME)?1:0,
	       (caps&DSP_CAP_BATCH)?1:0,
	       (caps&DSP_CAP_COPROC)?1:0,
	       (caps&DSP_CAP_TRIGGER)?1:0,
	       (caps&DSP_CAP_MMAP)?1:0);
      else
	printf("capabilities unknown\n");
      close(fd);
    }
#endif

    for (qMode=0; qMode<2; qMode++) {
      if (qMode==0 && (queryMode==0 || queryMode==1))
	printf("querying %s (record)\n",
	       (qDevice)?qDevice:"default audio device");
      if (qMode==1 && (queryMode==0 || queryMode==2))
	printf("querying %s (play)\n",
	       (qDevice)?qDevice:"default audio device");
      for (qNumChan=lowerNumChan; qNumChan<=upperNumChan; qNumChan++) {
	lastFSample = -1;
	for (qf=0; qf>=0 && testFSample[qf]!=-3; qf++) {
	  if (qNumChan==0) {
	    qFSample = 0.;
	    qf = -2;
	  }
	  else {
	    qFSample = testFSample[qf];
	    if (qFSample==-1)
	      qFSample = lowerFSample;
	    else if (qFSample==-2) {
	      if (upperFSample<=lowerFSample)
		continue;
	      qFSample = upperFSample;
	    }
	    else if (qFSample<=lowerFSample || qFSample>=upperFSample)
	      continue;
	  }
	  if (qMode==0 && (queryMode==0 || queryMode==1)) {
	    /* open rec */
	    devNumChan = qNumChan;
	    devFSample = qFSample;
	    if (debugLevel) {
	      fprintf(stderr,"rec  %2d ch %8.1f Hz ... ",qNumChan,qFSample);
	      fflush(stderr);
	    }
	    if ((qpr=PROpenRead(qDevice,&devNumChan,&devFSample)))
	      PRClose(qpr);
	    if (debugLevel) {
	      if (qpr) {
		if (devNumChan==qNumChan && devFSample==qFSample)
		  fprintf(stderr,"OK\n");
		else
		  fprintf(stderr,"got %2d ch %8.1f Hz\n",
			  devNumChan,devFSample);
	      }
	      else
		fprintf(stderr,"failed\n");
	      fflush(stderr);
	    }
	  }
	  if (qMode==1 && (queryMode==0 || queryMode==2)) {
	    /* open play */
	    devNumChan = qNumChan;
	    devFSample = qFSample;
	    if (debugLevel) {
	      fprintf(stderr,"play %2d ch %8.1f Hz ... ",qNumChan,qFSample);
	      fflush(stderr);
	    }
	    if ((qpr=PROpenWrite(qDevice,&devNumChan,&devFSample)))
	      PRClose(qpr);
	    if (debugLevel) {
	      if (qpr) {
		if (devNumChan==qNumChan && devFSample==qFSample)
		  fprintf(stderr,"OK\n");
		else
		  fprintf(stderr,"got %2d ch %8.1f Hz\n",
			  devNumChan,devFSample);
	      }
	      else
		fprintf(stderr,"failed\n");
	      fflush(stderr);
	    }
	  }
	  if (qpr) {
	    if (qNumChan==0) {
	      printf("default setting: %2d ch %8.1f Hz\n",
		     devNumChan,devFSample);
	    }
	    else if (devNumChan==qNumChan) {
	      if (lastFSample<0)
		printf(" %2d ch:",devNumChan);
	      if (lastFSample<devFSample) {
		printf(" %8.1f Hz",devFSample);
		lastFSample = devFSample;
	      }
	    }
	  }
	}
	if (lastFSample>=0)
	  printf(" \n");
      }
    }

    if (useDfltDev)
      useDfltDev = 0;
    if (varArgIdx[listIdx]>=0)
      listIdx++;
  }

  CmdLineEvalFree(paraList);

  if (debugLevel)
    fprintf(stderr,"%s: %s%s\n",progName,
	    stop?"aborted":"finished",(stop==2)?" (broken pipe)":"");

  exit (0);
}

/* end of auquery.c */




#if 0
      inNumChan = (outNumChanUsed)?outNumChan:1;
      inFSample = (outFSampleUsed)?outFSample:8000.0f;
      devNumChan = inNumChan;
      devFSample = (float)inFSample;
      if (!(inpr=PROpenRead(audioDevice,&devNumChan,&devFSample)))
	CommonExit(1,"can not open input audio device");
      if (devNumChan<=0 && outNumChanUsed)
	devNumChan = inNumChan = outNumChan;
      if (devFSample<=0 && outFSampleUsed) {
	inFSample = (float)outFSample;
	devFSample = (float)inFSample;
      }
      if (devNumChan<=0 || devFSample<=0)
	CommonExit(1,"input audio device configuration troubles");
      /* check whether sampling frequencies are similar enough */
      if (fabs(devFSample-inFSample) < maxFsDev*inFSample)
	devFSample = (float)inFSample;
      if (devNumChan != inNumChan || devFSample != inFSample) {
	CommonWarning("can not configure output audio device as requested");
	CommonWarning("=> using %dch/%.0fHz instead of %dch/%.0fHz",
		      devNumChan,devFSample,inNumChan,inFSample);
      }
      inNumChan = devNumChan;
      inFSample = devFSample;
    }
    if (debugLevel>1)
      fprintf(stderr,
	      "input=%s inNumChan=%d inFSample=%.1f fileNumSamp=%ld\n",
	      (intype==AUDEV)?"audio device":inFileName,
	      inNumChan,inFSample,fileNumSamp);





  if (!audioDeviceUsed)
    audioDevice = NULL;


    CommonWarning("nothing to play -> just resetting audio device ...");
    devNumChan = outNumChan = (outNumChanUsed)?outNumChan:1;
    outFSample = (outFSampleUsed)?(float)outFSample:8000.0f;
    devFSample = (float)outFSample;
    if (!(outpr=PROpenWrite(audioDevice,&devNumChan,&devFSample)))
      CommonExit(1,"can not open output audio device");
    /* check whether sampling frequencies are similar enough */
    if (fabs(devFSample-outFSample) < maxFsDev*outFSample)
      devFSample = (float)outFSample;
    if (devNumChan != outNumChan || devFSample != outFSample)
      CommonWarning("can not configure output audio device as requested");
    PRClose(outpr);
    exit (0);

  



#endif
    
