/*
 * $Source: src/unix/common/snd_unix.c $
 *
 * UNIX(tm) sound interface (mainly OSS)
 */

/* $Id: snd_unix.c 1.2 Fri, 15 Jun 2001 19:13:55 -0400 nyef $ */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <alsa/asoundlib.h>

/* #include "ui.h" */
#include "snd.h"
#if 0
#include <common/unixdep.h>

#ifdef SYSTEM_LINUX
#include <sys/soundcard.h>
#define SOUND_DEVICE "/dev/dsp"
#endif

#ifdef SYSTEM_FREEBSD
#include <machine/soundcard.h>
#define SOUND_DEVICE "/dev/dsp"
#endif

#ifdef SYSTEM_NETBSD
#include <sys/audioio.h>
#define SOUND_DEVICE "/dev/sound"
#endif

#ifdef SYSTEM_OPENBSD
#error No sound support on OpenBSD systems yet
#endif
#endif

/* final_wave used as 2048 byte array on mono systems. */
unsigned char final_wave[4096];
int waveptr;
int wavflag;
//int sound_fd;
snd_pcm_t *hplayback;

inline void write_mono_wave_data(void)
{
    int err;

    if (wavflag) {
	err = snd_pcm_writei(hplayback, &final_wave[(wavflag - 1) << 10], 1024);
	/* All the docs say EPIPE, so of course it's ESTRPIPE. */
	if (err == -ESTRPIPE) {
	    fprintf(stderr, "Underrun while writing to audio interface.\n");
	    err = snd_pcm_prepare(hplayback);
	    if (err < 0) {
		fprintf(stderr, "Failed to recover from underrun (%s/%d)\n",
			snd_strerror(err), err);
		snd_pcm_close(hplayback);
		hplayback = NULL;
	    }
	} else if (err < 0) {
	    fprintf(stderr, "Write to audio interface failed (%s/%d)\n",
		    snd_strerror(err), err);
	    snd_pcm_close(hplayback);
	    hplayback = NULL;
	} else if (err != 1024) {
	    fprintf(stderr, "Short write, tried %d wrote %d\n",
		    1024, err);
	}
	wavflag = 0;
    }
}

inline void write_stereo_wave_data(void)
{
    int err;

    if (wavflag) {
	err = snd_pcm_writei(hplayback, &final_wave[(wavflag - 1) << 11], 2048);
	if (err == -ESTRPIPE) {
	    fprintf(stderr, "Underrun while writing to audio interface.\n");
	    err = snd_pcm_prepare(hplayback);
	    if (err < 0) {
		fprintf(stderr, "Failed to recover from underrun (%s/%d)\n",
			snd_strerror(err), err);
		snd_pcm_close(hplayback);
		hplayback = NULL;
	    }
	} else if (err < 0) {
	    fprintf(stderr, "Write to audio interface failed (%s/%d)\n",
		    snd_strerror(err), err);
	    snd_pcm_close(hplayback);
	    hplayback = NULL;
	} else if (err != 2048) {
	    fprintf(stderr, "Short write, tried %d wrote %d\n",
		    2048, err);
	}
	wavflag = 0;
    }
}

void snd_output_4_waves(int samples, u8 *wave1, u8 *wave2, u8 *wave3, u8 *wave4)
{
    int i;
    int tmp;

    if (hplayback) {
	for (i = 0; i < samples; i++) {
            tmp = wave1[i] + wave2[i] + wave3[i] + wave4[i];
	    final_wave[waveptr] = tmp >> 2;
	    waveptr++;
	    if (waveptr == 2048) {
		waveptr = 0;
		wavflag = 2;
	    } else if (waveptr == 1024) {
		wavflag = 1;
	    }
	}

	write_mono_wave_data();
    }
}

void snd_output_8_waves(int samples, u8 *wave1, u8 *wave2, u8 *wave3, u8 *wave4, u8 *wave5, u8 *wave6, u8 *wave7, u8 *wave8)
{
    int i;
    int tmp;

    if (hplayback) {
	for (i = 0; i < samples; i++) {
            tmp = wave1[i] + wave2[i] + wave3[i] + wave4[i] + wave5[i] + wave6[i] + wave7[i] + wave8[i];
	    final_wave[waveptr] = tmp >> 3;
	    waveptr++;
	    if (waveptr == 2048) {
		waveptr = 0;
		wavflag = 2;
	    } else if (waveptr == 1024) {
		wavflag = 1;
	    }
	}
	
	write_mono_wave_data();
    }
}

void snd_output_stereo_samples(u8 left, u8 right)
{
    if (hplayback) {
	final_wave[waveptr++] = left;
	final_wave[waveptr++] = right;

	if (waveptr == /* 4096 */ 2048) {
	    waveptr = 0;
	    wavflag = 2;
	} else if (waveptr == /* 2048 */ 1024) {
	    wavflag = 1;
	}

/* 	write_stereo_wave_data(); */
	write_mono_wave_data();
    }
}

void snd_output_stereo_samples_16(u16 left, u16 right)
{
    if (hplayback) {
	final_wave[waveptr++] = right;
	final_wave[waveptr++] = right >> 8;
	final_wave[waveptr++] = left;
	final_wave[waveptr++] = left >> 8;

	if (waveptr == 4096) {
	    waveptr = 0;
	    wavflag = 2;
	} else if (waveptr == 2048) {
	    wavflag = 1;
	}

	write_stereo_wave_data();
    }
}

void snd_init(void)
{
    hplayback = NULL;
}

int snd_open_common(int sample_rate, int stereo, int format)
{
    int result;
    unsigned int sound_rate;
    //snd_pcm_uframes_t frames;
    //int dir;
    snd_pcm_hw_params_t *hw_params;

    waveptr = 0;
    wavflag = 0;

    sound_rate = sample_rate;
    
    result = snd_pcm_open(&hplayback, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (result < 0) {
	fprintf(stderr, "Failed to open audio device (%s)\n",
		snd_strerror(result));
	hplayback = NULL;
	return 0;
    }

    result = snd_pcm_hw_params_malloc(&hw_params);
    if (result < 0) {
	fprintf(stderr, "Failed to obtain HW parameter structure (%s)\n",
		snd_strerror(result));
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    result = snd_pcm_hw_params_any(hplayback, hw_params);
    if (result < 0) {
	fprintf(stderr, "Failed to initialize HW parameter structure (%s)\n",
		snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    result = snd_pcm_hw_params_set_access(hplayback, hw_params,
					  SND_PCM_ACCESS_RW_INTERLEAVED);
    if (result < 0) {
	fprintf(stderr, "Failed to set access type (%s)\n",
		snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    result = snd_pcm_hw_params_set_format(hplayback, hw_params, format);
    if (result < 0) {
	fprintf(stderr, "Failed to set sample format (%s)\n",
		snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    result = snd_pcm_hw_params_set_rate_near(hplayback, hw_params, &sound_rate, 0);
    if (result < 0) {
	fprintf(stderr, "Failed to set sample rate (%s)\n",
		snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    printf("Sample rate set to %d\n", sound_rate);

    result = snd_pcm_hw_params_set_channels(hplayback, hw_params, stereo + 1);
    if (result < 0) {
	fprintf(stderr, "Failed to set channel count (%s)\n",
		snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

#if 0 /* This is all about interrupts and latency, which we don't do yet. */
    frames = 2;
    result = snd_pcm_hw_params_set_period_size_near(hplayback, hw_params, &frames, &dir);
    if (result < 0) {
	fprintf(stderr, "Failed to set period (%s)\n",
		snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    printf("Period set to %ld frames\n", frames);

    /* FIXME: Set buffer size. */
#endif

    result = snd_pcm_hw_params(hplayback, hw_params);
    if (result < 0) {
	fprintf(stderr, "Failed to set hardware parameters (%s)\n",
	       snd_strerror(result));
	snd_pcm_hw_params_free(hw_params);
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    snd_pcm_hw_params_free(hw_params);

    result = snd_pcm_prepare(hplayback);
    if (result < 0) {
	fprintf(stderr, "Failed to prapare for playback (%s)\n",
		snd_strerror(result));
	snd_pcm_close(hplayback);
	hplayback = NULL;
	return 0;
    }

    return 1;
}

int snd_open(int sample_rate)
{
    return snd_open_common(sample_rate, 0, SND_PCM_FORMAT_U8);
}

int snd_open_stereo(int sample_rate)
{
    return snd_open_common(sample_rate, 1, SND_PCM_FORMAT_U8);
}

int snd_open_stereo_16(int sample_rate)
{
    return snd_open_common(sample_rate, 1, SND_PCM_FORMAT_S16_LE);
}

void snd_close(void)
{
    if (hplayback) {
	snd_pcm_close(hplayback);
	hplayback = NULL;
    }
}

/* EOF */
