/*********************************************************************
 *      common.c - common code
 * 
 *      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 <stdlib.h>
#include <stdio.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <math.h>
#include <fcntl.h>
#include "common.h"

int setup(char *device, mode_t mode)
{
	int audio_fd;
	audio_buf_info ospace_info, ispace_info;
	int i;
	int val, ret;

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

	printf("\nSELECTED FORMAT\n");

	val = FORMAT;
	if ((ret = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &val)) == -1)
		perror("SNDCTL_DSP_SETFMT");

	if (val != FORMAT) {
		fprintf(stderr, "format %d not supported\n", FORMAT);
		exit(EXIT_FAILURE);
	}

	printf("(SNDCTL_DSP_SETFMT)\nformat: %d ret: %d\n\n", val, ret);

	val = CHANNELS;
	if ((ret = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &val)) == -1)
		perror("SNDCTL_DSP_CHANNELS");

	if (val != CHANNELS) {
		fprintf(stderr, "%d channels mode not supported\n", CHANNELS);
		exit(EXIT_FAILURE);
	}

	printf("(SNDCTL_DSP_CHANNELS)\nchannels: %d ret: %d\n\n", val, ret);

	val = SPEED;
	if ((ret = ioctl(audio_fd, SNDCTL_DSP_SPEED, &val)) == -1)
		perror("SNDCTL_DSP_SPEED");

	if ((double) val / SPEED - 1.0 > 0.05) {
		fprintf(stderr, "speed %d not supported\n", SPEED);
		exit(EXIT_FAILURE);
	}

	printf("(SNDCTL_DSP_SPEED)\nspeed: %d ret: %d\n\n", val, ret);

	printf("\nSELECTED BUFFER\n");

	val = (FRAGSHIFT & 0xffff) | ((FRAGS & 0xffff) << 16);
	if ((ret = ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &val)))
		perror("SNDCTL_DSP_SETFRAGMENT");

	printf("(SNDCTL_DSP_SETFRAGMENT)\nsize: %d number: %d ret: %d\n\n", 1 << FRAGSHIFT, FRAGS, ret);

	printf("\nACTUAL BUFFER\n");

	if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &val)) == -1)
		perror("SNDCTL_DSP_GETBLKSIZE");

	printf("(SNDCTL_DSP_GETBLKSIZE)\nfragsize: %d ret: %d\n\n", val, ret);

	if ((mode & O_WRONLY) || (mode & O_RDWR)) {

		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &ospace_info)) == -1)
			perror("SNDCTL_DSP_GETOSPACE");

		printf
		    ("(SNDCTL_DSP_GETOSPACE)\nfragments: %d fragstotal: %d fragsize: %d bytes: %d ret: %d\n\n",
		     ospace_info.fragments, ospace_info.fragstotal, ospace_info.fragsize, ospace_info.bytes, ret);

		printf("\nPLAYING\n");

		for (i = 0; i < CHANNELS; i++) {
			printf("Channel %d\n", i);
			printf("Frequency     %7d Hz\nVolume            %3d %%\n\n", freq[i], vol[i]);
		}
	}

	if (!(mode & O_WRONLY) || (mode & O_RDWR)) {
		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &ispace_info)) == -1)
			perror("SNDCTL_DSP_GETISPACE");

		printf
		    ("(SNDCTL_DSP_GETISPACE)\nfragments: %d fragstotal: %d fragsize: %d bytes: %d ret: %d\n\n",
		     ispace_info.fragments, ispace_info.fragstotal, ispace_info.fragsize, ispace_info.bytes, ret);

	}

	fflush(stdout);

	getchar();

	return audio_fd;
}

static void generate_wave(struct wave *wave, double vol, int size)
{
	int i;

	if (vol > 100.0)
		vol = 100.0;
	else if (vol < 0.0)
		vol = 0.0;

	for (i = 0; i < size; i++)
		wave->buf[i] = vol / 100.0 * sin(wave->w * (i + wave->t0));

	wave->t0 += size;

	return;
}

void output_devbuf(__u8 * devbuf)
{
	int i, j;
	__s8 t8;
	__s16 t16;
	FILE *fp;

	if ((fp = fopen("output.dat", "a")) == NULL) {
		perror("output.dat");
		exit(EXIT_FAILURE);
	}

	switch (FORMAT) {

	case AFMT_S8:
		for (i = 0; i < BUF_SIZE / CHANNELS; i++) {
			for (j = 0; j < CHANNELS; j++)
				fprintf(fp, "%d ", (__s8) devbuf[CHANNELS * i + j]);

			fprintf(fp, "\n");
		}

		break;

	case AFMT_U8:
		for (i = 0; i < BUF_SIZE / CHANNELS; i++) {
                        for (j = 0; j < CHANNELS; j++) {
				t8 = devbuf[CHANNELS * i + j] ^ 0x80;
                                fprintf(fp, "%d ", (__s8) t8);
			}

                        fprintf(fp, "\n");
                }

		break;

	case AFMT_S16_LE:
		for (i = 0; i < BUF_SIZE / (2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
				t16 = (devbuf[2 * CHANNELS * i + j + 1] << 8) | devbuf[2 * CHANNELS * i + j];
                                fprintf(fp, "%d ", t16);
                        }

                        fprintf(fp, "\n");
                }

	case AFMT_S16_BE:
		for (i = 0; i < BUF_SIZE / (2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
                                t16 = (devbuf[2 * CHANNELS * i + j] << 8) | devbuf[2 * CHANNELS * i + j + 1];
                                fprintf(fp, "%d ", t16);
                        }

                        fprintf(fp, "\n");
                }

		break;

	case AFMT_U16_LE:
		for (i = 0; i < BUF_SIZE / (2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
                                t16 = ((devbuf[2 * CHANNELS * i + j + 1] << 8) | devbuf[2 * CHANNELS * i + j]) ^ 0x8000;
                                fprintf(fp, "%d ", t16);
                        }

                        fprintf(fp, "\n");
                }

		break;
	case AFMT_U16_BE:
                for (i = 0; i < BUF_SIZE / (2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
                                t16 = ((devbuf[2 * CHANNELS * i + j] << 8) | devbuf[2 * CHANNELS * i + j + 1]) ^ 0x8000;
                                fprintf(fp, "%d ", t16);
                        }

                        fprintf(fp, "\n");
                }

		break;

	default:
		break;
	}

	fclose(fp);

	return;
}



void fill_devbuf(struct wave *wave, __u8 * devbuf)
{
	int i, j;
	__u8 t8;
	__u16 t16;
	FILE *fp;

	if (WRITE_FILE)
		if ((fp = fopen("input.dat", "a")) == NULL) {
			perror("input.dat");
			exit(EXIT_FAILURE);
		}

	switch (FORMAT) {

	case AFMT_S8:
		for (i = 0; i < CHANNELS; i++)
			generate_wave(&wave[i], vol[i], BUF_SIZE / CHANNELS);


		for (i = 0; i < BUF_SIZE / CHANNELS; i++) {
			for (j = 0; j < CHANNELS; j++) {
				devbuf[CHANNELS * i + j] = 127.0 * wave[j].buf[i];
				if (WRITE_FILE)
					fprintf(fp, "%d ", (__s8) devbuf[CHANNELS * i + j]);
			}
			if (WRITE_FILE)
				fprintf(fp, "\n");
		}
		break;

	case AFMT_U8:
		for (i = 0; i < CHANNELS; i++)
                        generate_wave(&wave[i], vol[i], BUF_SIZE / CHANNELS);


                for (i = 0; i < BUF_SIZE / CHANNELS; i++) {
                        for (j = 0; j < CHANNELS; j++) {
				t8 = 127.0 * wave[j].buf[i];
                                devbuf[CHANNELS * i + j] = t8 ^ 0x80;
                                if (WRITE_FILE)
                                        fprintf(fp, "%d ", (__s8) t8);
                        }
                        if (WRITE_FILE)
                                fprintf(fp, "\n");
                }
		break;

	case AFMT_S16_LE:
		for (i = 0; i < CHANNELS; i++)
                        generate_wave(&wave[i], vol[i], BUF_SIZE / (2 * CHANNELS));


                for (i = 0; i < BUF_SIZE / ( 2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
				t16 = (__u16) (32767.0 * wave[j].buf[i]);
                                devbuf[(2 * CHANNELS * i) + 2 * j] = t16 & 0x00ff;
				devbuf[(2 * CHANNELS * i) + 2 * j + 1] = (t16 >> 8) & 0x00ff;
                                if (WRITE_FILE)
                                        fprintf(fp, "%d ", (__s16) t16);
                        }
                        if (WRITE_FILE)
                                fprintf(fp, "\n");
                }

		break;

	case AFMT_S16_BE:
		for (i = 0; i < CHANNELS; i++)
                        generate_wave(&wave[i], vol[i], BUF_SIZE / (2 * CHANNELS));


                for (i = 0; i < BUF_SIZE / ( 2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
                                t16 = (__u16) (32767.0 * wave[j].buf[i]);
                                devbuf[(2 * CHANNELS * i) + 2 * j] = (t16 >> 8) & 0x00ff;
                                devbuf[(2 * CHANNELS * i) + 2 * j + 1] = t16 & 0x00ff;
                                if (WRITE_FILE)
                                        fprintf(fp, "%d ", (__s16) t16);
                        }
                        if (WRITE_FILE)
                                fprintf(fp, "\n");
                }
		break;

	case AFMT_U16_LE:
		for (i = 0; i < CHANNELS; i++)
                        generate_wave(&wave[i], vol[i], BUF_SIZE / (2 * CHANNELS));


                for (i = 0; i < BUF_SIZE / ( 2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
                                t16 = (__u16) (32767.0 * wave[j].buf[i]) ^ 0x8000;
                                devbuf[(2 * CHANNELS * i) + 2 * j] = t16 & 0x00ff;
                                devbuf[(2 * CHANNELS * i) + 2 * j + 1] = (t16 >> 8) & 0x00ff;
                                if (WRITE_FILE)
                                        fprintf(fp, "%d ", (__s16) (t16 ^ 0x8000));
                        }
                        if (WRITE_FILE)
                                fprintf(fp, "\n");
                }

		break;

	case AFMT_U16_BE:
                for (i = 0; i < CHANNELS; i++)
                        generate_wave(&wave[i], vol[i], BUF_SIZE / (2 * CHANNELS));


                for (i = 0; i < BUF_SIZE / ( 2 * CHANNELS); i++) {
                        for (j = 0; j < CHANNELS; j++) {
                                t16 = (__u16) (32767.0 * wave[j].buf[i]) ^ 0x8000;
                                devbuf[(2 * CHANNELS * i) + 2 * j] = (t16 >> 8) & 0x00ff;
                                devbuf[(2 * CHANNELS * i) + 2 * j + 1] = t16 & 0x00ff;
                                if (WRITE_FILE)
                                        fprintf(fp, "%d ", (__s16) (t16 ^ 0x8000));
                        }
                        if (WRITE_FILE)
                                fprintf(fp, "\n");
                }

		break;

	default:
		break;
	}

	if (WRITE_FILE)
		fclose(fp);

	return;
}

void print_info(int audio_fd, mode_t mode)
{
	audio_buf_info ospace_info, ispace_info;
	count_info optr_info, iptr_info;
	int ret, val;

	if ((mode & O_WRONLY) || (mode & O_RDWR)) {

		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &optr_info)) == -1) {
			perror("SNDCTL_DSP_GETOPTR");
		}

		printf("(SNDCTL_DSP_GETOPTR)\nbytes: %d blocks: %d ptr: %d ret: %d\n\n", optr_info.bytes, optr_info.blocks, optr_info.ptr, ret);

		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &ospace_info)) == -1) {
			perror("SNDCTL_DSP_GETOSPACE");
		}

		printf
		    ("(SNDCTL_DSP_GETOSPACE)\nfragments: %d fragstotal: %d fragsize: %d bytes: %d ret: %d\n\n",
		     ospace_info.fragments, ospace_info.fragstotal, ospace_info.fragsize, ospace_info.bytes, ret);

		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &val)) == -1) {
			perror("SNDCTL_DSP_GETODELAY");
		}

		printf("(SNDCTL_DSP_GETODELAY)\npending: %d ret: %d\n\n", val, ret);
	}

	if (!(mode & O_WRONLY) || (mode & O_RDWR)) {
		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETIPTR, &iptr_info)) == -1) {
			perror("SNDCTL_DSP_GETIPTR");
		}

		printf("(SNDCTL_DSP_GETIPTR)\nbytes: %d blocks: %d ptr: %d ret: %d\n\n", iptr_info.bytes, iptr_info.blocks, iptr_info.ptr, ret);

		if ((ret = ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &ispace_info)) == -1) {
			perror("SNDCTL_DSP_GETISPACE");
		}

		printf
		    ("(SNDCTL_DSP_GETISPACE)\nfragments: %d fragstotal: %d fragsize: %d bytes: %d ret: %d\n\n",
		     ispace_info.fragments, ispace_info.fragstotal, ispace_info.fragsize, ispace_info.bytes, ret);

	}

	return;
}
