/*
 * sound_oss.c - Open Sound System driver
 *
 * Copyright (C) 1995-1998 David Firth
 * Copyright (C) 1998-2010 Atari800 development team (see DOC/CREDITS)
 *
 * This file is part of the Atari800 emulator project which emulates
 * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
 *
 * Atari800 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.
 *
 * Atari800 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 Atari800; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "config.h"

#ifdef SOUND

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
/* XXX: #include <machine/soundcard.h> */

#include "atari.h"
#include "log.h"
#include "pokeysnd.h"
#include "sound.h"
#include "util.h"

static const char * const dspname = "/dev/dsp";
static int dsprate = 22050;

static int sound_enabled = TRUE;
static int dsp_fd;
static int output_channels;
static int pokey_chips;

int Sound_Initialise(int *argc, char *argv[])
{
	int i, j;
	int help_only = FALSE;

	for (i = j = 1; i < *argc; i++) {
		int i_a = (i + 1 < *argc);		/* is argument available? */
		int a_m = FALSE;			/* error, argument missing! */

		if (strcmp(argv[i], "-sound") == 0)
			sound_enabled = TRUE;
		else if (strcmp(argv[i], "-nosound") == 0)
			sound_enabled = FALSE;
		else if (strcmp(argv[i], "-dsprate") == 0) {
			if (i_a)
				dsprate = Util_sscandec(argv[++i]);
			else a_m = TRUE;
		}
		else {
			if (strcmp(argv[i], "-help") == 0) {
				help_only = TRUE;
				Log_print("\t-sound           Enable sound\n"
				       "\t-nosound         Disable sound\n"
				       "\t-dsprate <rate>  Set sample rate in Hz"
				      );
			}
			argv[j++] = argv[i];
		}

		if (a_m) {
			Log_print("Missing argument for '%s'", argv[i]);
			sound_enabled = FALSE;
			return FALSE;
		}
	}
	*argc = j;

	if (help_only || !sound_enabled) {
		sound_enabled = FALSE;
		return TRUE;
	}

	dsp_fd = open(dspname, O_WRONLY);
	if (dsp_fd == -1) {
		perror(dspname);
		sound_enabled = FALSE;
		return TRUE;
	}
	i = AFMT_U8;
	if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &i)) {
		Log_print("%s: cannot set 8-bit unsigned samples", dspname);
		close(dsp_fd);
		sound_enabled = FALSE;
		return TRUE;
	}

	/* try to set the OSS device into appropriate number of channels (1 for
	 * single pokey, 2 for dual Pokey).
	 * If the sound card does not support the mode we need it should not fail
	 * but just set the output_channels to value it's able to support.
	 */
#ifdef STEREO_SOUND
	output_channels = 2;
#else
	output_channels = 1;
#endif
	if (ioctl(dsp_fd, SNDCTL_DSP_CHANNELS, &output_channels)) {
		Log_print("%s: SNDCTL_DSP_CHANNELS(%1) failed", dspname, output_channels);
		close(dsp_fd);
		sound_enabled = FALSE;
		return TRUE;
	}

	if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &dsprate)) {
		Log_print("%s: cannot set %d sample rate", dspname, dsprate);
		close(dsp_fd);
		sound_enabled = FALSE;
		return TRUE;
	}
	if (dsprate < 1000 || dsprate > 65535) {
		Log_print("%s: %d sample rate is not supported", dspname, dsprate);
		close(dsp_fd);
		sound_enabled = FALSE;
		return TRUE;
	}

#ifdef STEREO_SOUND
	pokey_chips = 2; /* stereo sound generated by two Pokey chips */
#else
	pokey_chips = 1;
#endif
	POKEYSND_Init(POKEYSND_FREQ_17_EXACT, dsprate, pokey_chips, 0);

	return TRUE;
}

void Sound_Pause(void)
{
	if (sound_enabled) {
		/* flush buffers */
		ioctl(dsp_fd, SNDCTL_DSP_POST, NULL);
	}
}

void Sound_Continue(void)
{
	/* do nothing */
}

void Sound_Exit(void)
{
	if (sound_enabled) {
		close(dsp_fd);
		sound_enabled = FALSE;
	}
}

void Sound_Update(void)
{
	/* 4096 bytes are enough for stereo 102400Hz
	   - pokeysnd currently supports only up to 65535Hz */
	static unsigned char buffer[4096];
	unsigned int len;
	if (!sound_enabled || Atari800_turbo)
		return;
	/* compute number of samples for one Atari frame
	   (assuming 60Hz for NTSC and 50Hz for PAL) */
	len = dsprate / (Atari800_tv_mode == Atari800_TV_NTSC ? 60 : 50) * pokey_chips;

#if 0
	/* this code is not needed because buffer[] is big enough */
	while (len > sizeof(buffer)) {
		POKEYSND_Process(buffer, sizeof(buffer));
		write(dsp_fd, buffer, sizeof(buffer));
		len -= sizeof(buffer);
	}
#endif

	POKEYSND_Process(buffer, len);

#if 0
	/* For some unknown reason, this is needed
	   on Piotr's Red Hat 9, VT82C686 AC97 Audio Controller.
	   Not that the sound is just a bit silent without this,
	   it is totally broken. */
	{
		unsigned int i;
		for (i = 0; i < len; i++)
			buffer[i] <<= 1;
	}
#endif

	/* the following block of code deals with situation when Pokey output
	 * does not match OSS output.
	 * It would better be handled in the Pokey emulation itself
	 */
	if (output_channels == 2) {
		/* OSS device set to two channel mode */
		if (pokey_chips == 1) {
			/* upmix mono to stereo - for sound in both channels */
			int i;
			for (i = len-1; i >= 0; i--) {
				int pos = i * 2;
				buffer[pos] = buffer[pos+1] = buffer[i];
			}
		}
		else if (! POKEYSND_stereo_enabled) {
			/* only single Pokey plays - copy left channel to right channel */
			int i;
			for (i = 0; i < len; i+=2) {
				buffer[i+1] = buffer[i];
			}
		}
	}
	else {
		/* OSS device set to single channel output */
		if (pokey_chips == 2) {
			/* downmix both channels into single output */
			int i;
			for (i = 0; i < len/2; i++) {
				int pos = i * 2;
				buffer[i] = (POKEYSND_stereo_enabled ? buffer[pos]/2 + buffer[pos+1]/2 :
							 buffer[pos]);
			}
		}
	}

	{
		int wlen = write(dsp_fd, buffer, len);
		if (wlen < len) {
			/* handle problem */
		}
	}
}

#endif	/* SOUND */

/*
vim:ts=4:sw=4:
*/
