/*
 * FILE:    auddev_sgi.c
 * PROGRAM: RAT
 * AUTHORS:  Isidor Kouvelas + Colin Perkins + Orion Hodson
 *
 * Copyright (c) 1995-2001 University College London
 * All rights reserved.
 */
 
#ifndef HIDE_SOURCE_STRINGS
static const char cvsid[] = 
	"$Id: auddev_sgi.c,v 1.30 2001/01/08 20:29:49 ucaccsp Exp $";
#endif /* HIDE_SOURCE_STRINGS */

#include <audio.h>

#include "config_unix.h"
#include "config_win32.h"
#include "audio_types.h"
#include "audio_fmt.h"
#include "auddev_sgi.h"
#include "debug.h"

#define QSIZE		16000		/* Two seconds for now... */
#define AVG_SIZE	20000
#define SAMSIG		7500.0
#define BASEOFF         0x0a7f

#define SGI_OPORT_SPEAKER    0x0101
#define SGI_IPORT_MICROPHONE 0x0203
#define SGI_IPORT_LINE_IN    0x0202
#define SGI_IPORT_APANEL     0x0201

#define RAT_TO_SGI_DEVICE(x)	((x) * 255 / MAX_AMP)
#define SGI_DEVICE_TO_RAT(x)	((x) * MAX_AMP / 255)

static int      audio_fd = -1;
static ALport	rp = NULL, wp = NULL;	/* Read and write ports */
static int      bytes_per_block;
static audio_port_t iport = SGI_IPORT_APANEL;

int
sgi_audio_device_count()
{
        return 1;
}

char*
sgi_audio_device_name(audio_desc_t ad)
{
        UNUSED(ad);
        return "SGI Audio Device";
}

/*
 * Try to open the audio device.
 * Return TRUE if successfull FALSE otherwise.
 */

int
sgi_audio_open(audio_desc_t ad, audio_format* ifmt, audio_format *ofmt)
{
	ALconfig	c;
	long		cmd[8];

        if (audio_fd != -1) {
                sgi_audio_close(ad);
        }

        if (ifmt->encoding != DEV_S16) return FALSE;

	if ((c = ALnewconfig()) == NULL) {
		fprintf(stderr, "ALnewconfig error\n");
		exit(1);
	}

        switch(ifmt->channels) {
        case 1:
                ALsetchannels(c, AL_MONO); break;
        case 2:
                ALsetchannels(c, AL_STEREO); break;
        default:
                sgi_audio_close(ad);
        }

	ALsetwidth(c, AL_SAMPLE_16);
	ALsetqueuesize(c, QSIZE);
	ALsetsampfmt(c, AL_SAMPFMT_TWOSCOMP);

	if ((wp = ALopenport("RAT write", "w", c)) == NULL) {
		fprintf(stderr, "ALopenport (write) error\n");
                sgi_audio_close(ad);
                return FALSE;
        }

	if ((rp = ALopenport("RAT read", "r", c)) == NULL) {
		fprintf(stderr, "ALopenport (read) error\n");
                sgi_audio_close(ad);
                return FALSE;
        }

	cmd[0] = AL_OUTPUT_RATE;
	cmd[1] = ofmt->sample_rate;
	cmd[2] = AL_INPUT_RATE;
	cmd[3] = ifmt->sample_rate;
	cmd[4] = AL_MONITOR_CTL;
	cmd[5] = AL_MONITOR_OFF;
	/*cmd[6] = AL_INPUT_SOURCE;*/
	/*cmd[7] = AL_INPUT_MIC;*/

	if (ALsetparams(AL_DEFAULT_DEVICE, cmd, 6L/*was 8L*/) == -1) {
		fprintf(stderr, "audio_open/ALsetparams error\n");
                sgi_audio_close(ad);
        }

	/* Get the file descriptor to use in select */
	audio_fd = ALgetfd(rp);

	if (ALsetfillpoint(rp, ifmt->bytes_per_block) < 0) {
                debug_msg("ALsetfillpoint failed (%d samples)\n", ifmt->bytes_per_block);
        }
        bytes_per_block = ifmt->bytes_per_block;
        
	/* We probably should free the config here... */
        
	return TRUE;
}

/* Close the audio device */
void
sgi_audio_close(audio_desc_t ad)
{
        UNUSED(ad);
	ALcloseport(rp);
	ALcloseport(wp);
        audio_fd = -1;
}

/* Flush input buffer */
void
sgi_audio_drain(audio_desc_t ad)
{
	u_char buf[QSIZE];

        UNUSED(ad); assert(audio_fd > 0);

	while(sgi_audio_read(audio_fd, buf, QSIZE) == QSIZE);
}

/* Gain and volume values are in the range 0 - MAX_AMP */

void
sgi_audio_set_igain(audio_desc_t ad, int gain)
{
	long	cmd[4];

        UNUSED(ad); assert(audio_fd > 0);

	cmd[0] = AL_LEFT_INPUT_ATTEN;
	cmd[1] = 255 - RAT_TO_SGI_DEVICE(gain);
	cmd[2] = AL_RIGHT_INPUT_ATTEN;
	cmd[3] = cmd[1];
	ALsetparams(AL_DEFAULT_DEVICE, cmd, 4L);
}

int
sgi_audio_get_igain(audio_desc_t ad)
{
	long	cmd[2];

        UNUSED(ad); assert(audio_fd > 0);

	cmd[0] = AL_LEFT_INPUT_ATTEN;
	ALgetparams(AL_DEFAULT_DEVICE, cmd, 2L);
	return (MAX_AMP - SGI_DEVICE_TO_RAT(cmd[1]));
}

void
sgi_audio_set_ogain(audio_desc_t ad, int vol)
{
	long	cmd[4];

        UNUSED(ad); assert(audio_fd > 0);

	cmd[0] = AL_LEFT_SPEAKER_GAIN;
	cmd[1] = RAT_TO_SGI_DEVICE(vol);
	cmd[2] = AL_RIGHT_SPEAKER_GAIN;
	cmd[3] = cmd[1];
	ALsetparams(AL_DEFAULT_DEVICE, cmd, 4L);
}

int
sgi_audio_get_ogain(audio_desc_t ad)
{
	long	cmd[2];

        UNUSED(ad); assert(audio_fd > 0);

	cmd[0] = AL_LEFT_SPEAKER_GAIN;
	ALgetparams(AL_DEFAULT_DEVICE, cmd, 2L);
	return (SGI_DEVICE_TO_RAT(cmd[1]));
}

static int non_block = 1;	/* Initialise to non blocking */

int
sgi_audio_read(audio_desc_t ad, u_char *buf, int buf_bytes)
{
	long   		len;

        UNUSED(ad); assert(audio_fd > 0);
        
	if (non_block) {
		if ((len = ALgetfilled(rp)) < bytes_per_block )
			return (0);
                len = min(len, buf_bytes);
	} else {
		len = (long)buf_bytes;
        }

        len /= BYTES_PER_SAMPLE; /* We only open device in 16 bit mode */

	if (len > QSIZE) {
		fprintf(stderr, "audio_read: too big!\n");
		len = QSIZE;
	}

	ALreadsamps(rp, buf, len);

	return ((int)len * BYTES_PER_SAMPLE);
}

int
sgi_audio_write(audio_desc_t ad, u_char *buf, int buf_bytes)
{
        long samples;
        UNUSED(ad); assert(audio_fd > 0);

        samples = buf_bytes / BYTES_PER_SAMPLE;
	if (samples > QSIZE) {
		fprintf(stderr, "audio_write: too big!\n");
		samples = QSIZE;
	}

	/* Will block */

	ALwritesamps(wp, buf, samples);
	return buf_bytes;
}

/* Set ops on audio device to be non-blocking */
void
sgi_audio_non_block(audio_desc_t ad)
{
        UNUSED(ad); assert(audio_fd > 0);

	non_block = 1;
}

/* Set ops on audio device to block */
void
sgi_audio_block(audio_desc_t ad)
{
        UNUSED(ad); assert(audio_fd > 0);

	non_block = 0;
}

static audio_port_details_t out_ports[] = {
        { SGI_OPORT_SPEAKER, AUDIO_PORT_SPEAKER}
};

#define SGI_NUM_OPORTS (sizeof(out_ports)/sizeof(out_ports[0]))

static audio_port_details_t in_ports[] = {
	{ SGI_IPORT_APANEL,	"APanel" },
        { SGI_IPORT_MICROPHONE, AUDIO_PORT_MICROPHONE },
        { SGI_IPORT_LINE_IN,    AUDIO_PORT_LINE_IN }
};

#define SGI_NUM_IPORTS (sizeof(in_ports)/sizeof(in_ports[0]))

void
sgi_audio_oport_set(audio_desc_t ad, audio_port_t port)
{
        UNUSED(ad); 
        assert(audio_fd > 0);
        assert(port == SGI_OPORT_SPEAKER);
        UNUSED(port);
}

audio_port_t
sgi_audio_oport_get(audio_desc_t ad)
{
        UNUSED(ad); 
        assert(audio_fd > 0);

	return (SGI_OPORT_SPEAKER);
}

int 
sgi_audio_oport_count(audio_desc_t ad) 
{
        UNUSED(ad);
        return (int)SGI_NUM_OPORTS;
}

const audio_port_details_t*
sgi_audio_oport_details(audio_desc_t ad, int idx)
{
        UNUSED(ad);
        assert((unsigned)idx < SGI_NUM_OPORTS && idx >= 0);
        return &out_ports[idx];
}

void
sgi_audio_iport_set(audio_desc_t ad, audio_port_t port)
{
	long pvbuf[2];

        UNUSED(ad); assert(audio_fd > 0);

        switch(port) {
        case SGI_IPORT_LINE_IN: 
                pvbuf[0] = AL_INPUT_SOURCE;
                pvbuf[1] = AL_INPUT_LINE;
                ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2);
                break;
        case SGI_IPORT_MICROPHONE: 
        default:
                pvbuf[0] = AL_INPUT_SOURCE;
                pvbuf[1] = AL_INPUT_MIC;
                ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2);
                break;
	case SGI_IPORT_APANEL:
		break;
        }
        iport = port;
}

audio_port_t
sgi_audio_iport_get(audio_desc_t ad)
{
        UNUSED(ad); assert(audio_fd > 0);
	return iport;
}

int
sgi_audio_iport_count(audio_desc_t ad)
{
        UNUSED(ad); assert(audio_fd > 0);
        return (int)SGI_NUM_IPORTS;
}

const audio_port_details_t*
sgi_audio_iport_details(audio_desc_t ad, int idx)
{
        UNUSED(ad);
        assert(idx < (int)SGI_NUM_IPORTS && idx >= 0);
        return &in_ports[idx];
}

void 
sgi_audio_loopback(audio_desc_t ad, int gain)
{
        long pvbuf[4];
        int  pvcnt;

        UNUSED(ad); assert(audio_fd > 0);

        pvcnt = 2;
        pvbuf[0] = AL_MONITOR_CTL;
        pvbuf[1] = AL_MONITOR_OFF;

        if (gain) {
                pvcnt = 6;
                pvbuf[1] = AL_MONITOR_ON;
                pvbuf[2] = AL_LEFT_MONITOR_ATTEN;
                pvbuf[3] = 255 - RAT_TO_SGI_DEVICE(gain);
                pvbuf[4] = AL_RIGHT_MONITOR_ATTEN;
                pvbuf[5] = pvbuf[3];
        }
        
        if (ALsetparams(AL_DEFAULT_DEVICE, pvbuf, pvcnt) != 0) {
                debug_msg("loopback failed\n");
        }
}

int
sgi_audio_duplex(audio_desc_t ad)
{
        UNUSED(ad); assert(audio_fd > 0);
        return 1;
}

int  
sgi_audio_is_ready(audio_desc_t ad)
{
        UNUSED(ad); assert(audio_fd > 0);
        
        if (ALgetfilled(rp) >= ALgetfillpoint(rp)) {
                return TRUE;
        } else {
                return FALSE;
        }
}

void 
sgi_audio_wait_for(audio_desc_t ad, int delay_ms)
{
        struct timeval tv;
        fd_set rfds;
        UNUSED(ad); assert(audio_fd > 0);

        tv.tv_sec  = 0;
        tv.tv_usec = delay_ms * 1000;

        FD_ZERO(&rfds);
        FD_SET(audio_fd, &rfds);
        
        select(audio_fd + 1, &rfds, NULL, NULL, &tv);
}

