/*********************************************************************
 *      query.c - queries capabilities of an OSS sound driver
 *      Copyright (C) 1999-2000 Rui Sousa 
 ********************************************************************* 
 *     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. 
 ********************************************************************* 
*/

#include <stdio.h>
#include <math.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <stdlib.h>

static struct capability {
	char *name;
	int type;
} cap[] = {

	{
	"Revision", DSP_CAP_REVISION}
	, {
	"Duplex", DSP_CAP_DUPLEX}
	, {
	"Real Time", DSP_CAP_REALTIME}
	, {
	"Batch", DSP_CAP_BATCH}
	, {
	"CoProcessor", DSP_CAP_COPROC}
	, {
	"Trigger", DSP_CAP_TRIGGER}
	, {
	"Mmapped", DSP_CAP_MMAP}
	, {
	"Multi Opens", DSP_CAP_MULTI}
	, {
	"Channel Bind", DSP_CAP_BIND}
	
};

static struct audio_format {
	char *name;
	int type;
} form[] = {

	{
	"AFMT_MU_LAW", AFMT_MU_LAW}
	, {
	"AFMT_A_LAW", AFMT_A_LAW}
	, {
	"AFMT_IMA_ADPCM", AFMT_IMA_ADPCM}
	, {
	"AFMT_U8", AFMT_U8}
	, {
	"AFMT_S16_LE", AFMT_S16_LE}
	, {
	"AFMT_S8", AFMT_S8}
	, {
	"AFMT_S16_BE", AFMT_S16_BE}
	, {
	"AFMT_U16_LE", AFMT_U16_LE}
	, {
	"AFMT_U16_BE", AFMT_U16_BE}
	, {
	"AFMT_MPEG", AFMT_MPEG}
	, {
	"AFMT_AC3", AFMT_AC3}
	
};

static struct channel_number {
	char *name;
	int number;
} channel[] = {

	{
	"mono", 0}
	, {
	"stereo", 1}
};

static struct sampling_rate {
	char *name;
	int freq;
} rate[] = {

	{
	"8000", 8000}
	, {
	"11025", 11025}
	, {
	"16000", 16000}
	, {
	"22050", 22050}
	, {
	"24000", 24000}
	, {
	"32000", 32000}
	, {
	"44100", 44100}
	, {
	"48000", 48000}
	, {
	"96000", 96000}
	
};

void query(char *device)
{
	int speed;
	int stereo;
	int format;
	int audio_fd;
	int caps;
	int i, j;

	if ((audio_fd = open(device, O_WRONLY, 0)) == -1) {
		perror(device);
		exit(EXIT_FAILURE);
	}

	if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
		perror("SNDCTL_DSP_GETCAPS");
		exit(EXIT_FAILURE);
	}

	printf("\nCAPABILITIES\n");

	printf("%-14s %3d\n", cap[0].name, caps & cap[0].type);

	for (i = 1; i < 9; i++) {
		printf("%-14s ", cap[i].name);
		if (caps & cap[i].type)
			printf("yes\n");
		else
			printf(" no\n");
	}

	if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &caps) == -1) {
		perror("SNDCTL_DSP_GETFMTS");
		exit(EXIT_FAILURE);
	}

	printf("\nSUPPORTED AUDIO FORMATS (hardware)\n");

	for (i = 0; i < 11; i++) {
		printf("%-14s ", form[i].name);
		if (caps & form[i].type)
			printf("yes\n");
		else
			printf(" no\n");

	}

	printf("\nADDITIONAL SUPPORTED AUDIO FORMATS (software)\n");

	for (i = 0; i < 11; i++) {

		if (!(caps & form[i].type)) {

			printf("%-14s ", form[i].name);

			format = form[i].type;
			if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format) == -1) {
				perror("SNDCTL_DSP_SETFMT");
				exit(EXIT_FAILURE);
			}
			if (format == form[i].type)
				printf("yes\n");
			else
				printf(" no\n");
		}
	}
	close(audio_fd);

	if ((audio_fd = open(device, O_WRONLY, 0)) == -1) {
		perror(device);
		exit(EXIT_FAILURE);
	}

	printf("\nSUPPORTED NUMBER OF CHANNELS (playback)\n");

	for (j = 0; j < 2; j++) {

		stereo = channel[j].number;
		if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
			perror("SNDCTL_DSP_STEREO");
			exit(EXIT_FAILURE);
		}

		printf("%1d %-12s ", channel[j].number, channel[j].name);
		if (stereo == channel[j].number)
			printf("yes\n");
		else
			printf(" no\n");
	}
	close(audio_fd);

	if ((audio_fd = open(device, O_WRONLY, 0)) == -1) {
		perror(device);
		exit(EXIT_FAILURE);
	}


	format =  AFMT_S16_LE;
	if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format) == -1) {
		perror("SNDCTL_DSP_SETFMT");
		exit(EXIT_FAILURE);
	}
	printf("\nMULTICHANNEL (playback, AFMT_S16_LE)\n");
	for(i=1;i<9;i++){
		stereo=i;
		if (ioctl(audio_fd,  SNDCTL_DSP_CHANNELS, &stereo) == -1) {
			perror("SNDCTL_DSP_CHANNELS");
			exit(EXIT_FAILURE);
		}
		printf("%d channels:    ",i);
		printf("%s\n",stereo==i?"yes":"no");
	}

	close(audio_fd);


	if ((audio_fd = open(device, O_RDONLY, 0)) == -1) {
		perror(device);
		exit(EXIT_FAILURE);
	}

	printf("\nSUPPORTED NUMBER OF CHANNELS (recording)\n");

	for (j = 0; j < 2; j++) {

		stereo = channel[j].number;
		if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
			perror("SNDCTL_DSP_STEREO");
			exit(EXIT_FAILURE);
		}

		printf("%1d %-12s ", channel[j].number, channel[j].name);
		if (stereo == channel[j].number)
			printf("yes\n");
		else
			printf(" no\n");
	}

	close(audio_fd);

	if ((audio_fd = open(device, O_WRONLY, 0)) == -1) {
		perror(device);
		exit(EXIT_FAILURE);
	}

	for (j = 0; j < 2; j++) {

		printf("\nSUPPORTED SAMPLING RATES (Hz) (playback, %s)\n", channel[j].name);

		stereo = channel[j].number;
		if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
			perror("SNDCTL_DSP_STEREO");
			exit(EXIT_FAILURE);
		}

		for (i = 0; i < 9; i++) {
			speed = rate[i].freq;
			if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed) == -1) {
				perror("SNDCTL_DSP_SPEED");
				exit(EXIT_FAILURE);
			}
			printf("%-14s ", rate[i].name);
			if (abs(speed / rate[i].freq - 1.0) < 0.05)
				printf("yes\n");
			else
				printf(" no\n");
		}
	}
	close(audio_fd);

	if ((audio_fd = open(device, O_RDONLY, 0)) == -1) {
		perror(device);
		exit(EXIT_FAILURE);
	}

	for (j = 0; j < 2; j++) {

		printf("\nSUPPORTED SAMPLING RATES (Hz) (recording, %s)\n", channel[j].name);

		stereo = channel[j].number;
		if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
			perror("SNDCTL_DSP_STEREO");
			exit(EXIT_FAILURE);
		}

		for (i = 0; i < 9; i++) {
			speed = rate[i].freq;
			if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed) == -1) {
				perror("SNDCTL_DSP_SPEED");
				exit(EXIT_FAILURE);
			}
			printf("%-14s ", rate[i].name);
			if (abs(speed / rate[i].freq - 1.0) < 0.05)
				printf("yes\n");
			else
				printf(" no\n");
		}
	}

	close(audio_fd);

	fflush(stdout);

	return;
}

int main()
{
	query("/dev/dsp");

	return 0;
}
