/*
 * Written by Arthur Peters
 *
 * Copyright (C) 2000  Arthur Peters
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Please goto http://www.gnu.org/copyleft/gpl.html to find a copy of the
 * GNU General Public License.
 *
 * You can contact me (Arthur Peters) by email at amep@softhome.net.
 *
 */

#include <list>
#include <iostream>
#include <string>
#include <map>
#include <function.h>

// Includes needed to open, write and read /dev/dsp 
#include <unistd.h>
#include <fcntl.h>

// Includes needed for OSS ioctls
#include <sys/ioctl.h>
#include <sys/soundcard.h>

// typedef int sampletype;

map<int, string> sampletype2name;
/*{
   AFMT_U8, "Unsigned 8 bit",
   AFMT_MU_LAW, "MU Law",
      //AFMT_A_LAW, AFMT_IMA_ADPCM, AFMT_U8, AFMT_S16_LE,
//  AFMT_S16_BE, AFMT_S8, AFMT_U16_LE, AFMT_U16_BE, AFMT_MPEG};
   AFMT_MPEG, "MPEG Audio",
};
*/
map<int,map<int, list<int> > > ratesdata;

/*
struct channelcountdata
{
      int channels;
      list<sampletype> types;
};

struct ratedata
{
      int rate;
      list<channelcountdata> channels;
};
*/

// Tests :
bool hasCAPS(char *dev);

bool isFullduplex(char *dev);
bool isBuffered(char *dev);
bool canMMap(char *dev);
bool canTrigger(char *dev);
//bool canRecord(char *dev);
//bool canPlay(char *dev);

list<int> findRates(char *dev, int chans, int fmt=AFMT_S16_LE, bool all=true);
list<int> findChannels(char *dev, int rate=44100, int fmt=AFMT_S16_LE);
list<int> findTypes(char *dev, int rate=44100, int chans=1);

int getDSPCAPS(char *dev);

// Main :
int main(int argc, char *argv[])
{
   {
      sampletype2name[AFMT_MU_LAW] = "MU Law";
      sampletype2name[AFMT_A_LAW] = "A Law";
      sampletype2name[AFMT_IMA_ADPCM] = "IMA ADPCM 4:1 compression";
      sampletype2name[AFMT_S8] = "Signed 8 bit";
      sampletype2name[AFMT_U8] = "Unsigned 8 bit";
      sampletype2name[AFMT_S16_LE] = "Signed 16 bit little-endian";
      sampletype2name[AFMT_S16_BE] = "Signed 16 bit big-endian";
      sampletype2name[AFMT_U16_LE] = "Unsigned 16 bit little-endian";
      sampletype2name[AFMT_U16_BE] = "Unsigned 16 bit big-endian";
      sampletype2name[AFMT_MPEG] = "MPEG Audio";
   }

   
   char *devicename = "/dev/dsp1";

   cout << "Supports CAPS ioctl?\t" << hasCAPS(devicename);
   cout << "(CAPS = 0x" << hex << getDSPCAPS(devicename) << ")" << dec << endl;
   
   cout << "Full-Duplex?\t\t" << isFullduplex(devicename) << endl;
   cout << "Has internal buffers?\t" << isBuffered(devicename) << endl;
   cout << "Memory Map?\t\t" << canMMap(devicename) << endl;
   cout << "Trigger?\t\t" << canTrigger(devicename) << endl;

   // fill ratesdata
   //do it!

   cout << "Supports these sampling rates in stereo: " << endl;
   list<int> rates = findRates(devicename, 2);
   copy(rates.begin(), rates.end(), ostream_iterator<int>(cout, ", "));
   cout << endl;

   cout << "Supports these sampling rates in mono: " << endl;
   rates = findRates(devicename, 1);
   copy(rates.begin(), rates.end(), ostream_iterator<int>(cout, ", "));
   cout << endl;

   cout << "Supports these channel counts at " << rates.back() << "Khz: ";
   list<int> channels = findChannels(devicename, rates.back());
   copy(channels.begin(), channels.end(), ostream_iterator<int>(cout, ", "));
   cout << endl;

   return true;
}

bool hasCAPS(char *dev)
{
   int fd = -1;

   fd = open(dev, O_RDWR);

   int caps = 0x00, ret;
   
   ret = ioctl(fd, SNDCTL_DSP_GETCAPS, &caps);

   close(fd);

   if( ret == -1 )
      return false;
   
   return true;
}

bool isFullduplex(char *dev)
{
   if( (getDSPCAPS(dev) & DSP_CAP_DUPLEX) == DSP_CAP_DUPLEX )
   {
      return true;
   }
   return false;
}

bool isBuffered(char *dev)
{
   if( (getDSPCAPS(dev) & DSP_CAP_BATCH) == DSP_CAP_BATCH )
   {
      return true;
   }
   return false;
}

bool canMMap(char *dev)
{
   if( (getDSPCAPS(dev) & DSP_CAP_MMAP) == DSP_CAP_MMAP )
      return true;
   return false;
}

bool canTrigger(char *dev)
{
   if( (getDSPCAPS(dev) & DSP_CAP_TRIGGER) == DSP_CAP_TRIGGER )
      return true;
   return false;
}

int getDSPCAPS(char *dev)
{
   int fd = -1;

   fd = open(dev, O_WRONLY);

   int caps = 0x00;
   
   ioctl(fd, SNDCTL_DSP_GETCAPS, &caps);

   close(fd);
   
   return caps;
}

const int standardrates[] =
{ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 96000 };
const int n_standardrates = sizeof(standardrates) / sizeof(standardrates[0]);


list<int> findRates(char *dev, int chans, int fmt, bool all)
{
   list<int> ret;

   if( all == false )
   {
      for(int i=0; i<n_standardrates; i++)
      {
	 int fd = -1, rate = standardrates[i];
	 fd = open(dev, O_WRONLY);
	 
	 ioctl(fd, SNDCTL_DSP_CHANNELS, &chans);
	 ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
	 
	 if(ioctl(fd, SNDCTL_DSP_SPEED, &rate) != -1)
	    ret.push_back(rate);
	 
	 close(fd);
      }
   }
   else
   {
      for(int i=1; i<100000; i+=1000)
      {
	 int fd = -1, rate = i;
	 fd = open(dev, O_WRONLY);
	 
	 ioctl(fd, SNDCTL_DSP_CHANNELS, &chans);
	 ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
	 
	 if(ioctl(fd, SNDCTL_DSP_SPEED, &rate) != -1)
	    ret.push_back(rate);
	 
	 close(fd);
      }
   }

   ret.sort();
   ret.unique(equal_to<int>());
      
   return ret;
}

list<int> findChannels(char *dev, int rate, int fmt)
{
   list<int> ret;
   
   for(int i=1; i<9; i++)
   {
      int fd = -1, chans = i;
      fd = open(dev, O_WRONLY);
      
      ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
      ioctl(fd, SNDCTL_DSP_SPEED, &rate);
      
      if(ioctl(fd, SNDCTL_DSP_CHANNELS, &chans) != -1)
	 ret.push_back(chans);
      
      close(fd);
   }

   ret.sort();
   ret.unique(equal_to<int>());
      
   return ret;
}

const int sampletypes[] =
{ AFMT_MU_LAW, AFMT_A_LAW, AFMT_IMA_ADPCM, AFMT_U8, AFMT_S16_LE,
  AFMT_S16_BE, AFMT_S8, AFMT_U16_LE, AFMT_U16_BE, AFMT_MPEG};
const int n_sampletypes = sizeof(sampletypes) / sizeof(sampletypes[0]);


list<int> findTypes(char *dev, int rate, int chans)
{
   list<int> ret;

   for(int i=0; i<n_sampletypes; i++)
   {
      int fd = -1, fmt = sampletypes[i];
      fd = open(dev, O_WRONLY);
      
      ioctl(fd, SNDCTL_DSP_CHANNELS, &chans);
      ioctl(fd, SNDCTL_DSP_SPEED, &rate);
	 
      if(ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) != -1)
	 ret.push_back(fmt);
      
      close(fd);
   }

   ret.sort();
   ret.unique(equal_to<int>());
      
   return ret;
}
