/* hplow_audio.c 
	vi:se ts=3 sw=3:
 */

/* Driver for HP9000 series 710/7x5 with HP-UX 9.01 */
/* Using low-level audio calls */
/* Copyright 1993 Marc Espie   (Marc.Espie@ens.fr)  */
/* Copyright 1993 Markus Gyger (mgyger@itr.ch) */

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/audio.h>
#include "defs.h"
#include "extern.h"

ID("$Id$")
#ifndef DEFAULT_SAMPLE_RATE
#define DEFAULT_SAMPLE_RATE  22050
#endif

#ifndef AUDIO_NAME
#define AUDIO_NAME           "/dev/audio"
#endif
#ifndef AUDIO_CTL_NAME
#define AUDIO_CTL_NAME       "/dev/audioCtl"
#endif

LOCAL int audio;
LOCAL int audio_ctl;

LOCAL struct audio_describe ainfo;
LOCAL char *buffer;
LOCAL short *sbuffer;
LOCAL int index;
LOCAL int sample_rate;
LOCAL int channels;
LOCAL int min_samples;

LOCAL int stereo;
LOCAL int primary, secondary;

void set_mix(percent)
int percent;
    {
    percent *= 256;
    percent /= 100;
    primary = percent;
    secondary = 512 - percent;
    }

LOCAL int available(f)
int f;
    {
    int best = 0;
    int i;

    for (i = 0; i < ainfo.nrates; i++)
	if (abs(ainfo.sample_rate[i] - f) < abs(best - f))
	    best = ainfo.sample_rate[i];
    return best;
    }

int open_audio(f, s)
int f;
int s;
    {
    int type;

    audio_ctl = open(AUDIO_CTL_NAME, O_WRONLY | O_NDELAY);
    if (audio_ctl == -1)
        {
        end_all("No audio control device");
        }

    if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
        {
	end_all("No audio info");
        }

    if (f == 0)
        f = DEFAULT_SAMPLE_RATE;
        /* round frequency to acceptable value */
    sample_rate = available(f);

        /* check whether we have stereo device */
    if (ainfo.nchannels < 2)
        {
            /* a 710 or 425 -> revert to base quality audio */
        stereo = 0;
        channels = 1;
        }
    else
        {
            /* 7x5 set up */
        stereo = s;
        if (stereo)
            {
            channels = 2;
            set_mix(30);
            }
        else
            channels = 1;
        }

        /* write a minimum of samples to avoid underflows on 715 (???) */
    switch (ainfo.audio_id)
        {
        case AUDIO_ID_CS4215:
            /* empirically found values */
            min_samples = 12288;
            if (channels < 2) min_samples = 8192;
            if (sample_rate <= 11025) min_samples = 6144;
            if (sample_rate <= 8000) min_samples = 4096;
            break;

        case AUDIO_ID_PSB2160:
        default:
            min_samples = 0;
            break;
        }

    if (ioctl(audio_ctl, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT) == -1)
        {
		  end_all("Linear format not available");
        }
    if (ioctl(audio_ctl, AUDIO_SET_CHANNELS, channels) == -1)
        {
		  end_all("Could not set # of audio channels");
        }
    if (ioctl(audio_ctl, AUDIO_SET_SAMPLE_RATE, sample_rate) == -1)
        {
		  end_all("Could not set audio sample rate");
        }

    audio = open(AUDIO_NAME, O_WRONLY | O_NDELAY);
    if (audio == -1)
        {
		  end_all("Could not open audio device");
        }

#ifdef SET_OUTPUT
        /* ensure we hear something */
    if (ioctl(audio_ctl, AUDIO_SET_OUTPUT,
        ((SET_OUTPUT & 1) ? AUDIO_OUT_INTERNAL : 0) |   /* speaker */
        ((SET_OUTPUT & 2) ? AUDIO_OUT_EXTERNAL : 0) |   /* phones  */
        ((SET_OUTPUT & 4) ? AUDIO_OUT_LINE : 0) |   /* line    */
        ((SET_OUTPUT & 7) ? 0 : AUDIO_OUT_NONE)) == -1)
		  notice("Warning: could not set audio output");
#endif

    index = 0;
    sbuffer = (short *)malloc(2 * channels * sample_rate);
    buffer = (char *)sbuffer;
    if (!buffer)
        end_all("Could not allocate buffer");
    return sample_rate;
    }

void set_synchro(s)
boolean s;
    {
    }

int update_frequency()
    {
    int oldfreq;

    oldfreq = sample_rate;
    if (ioctl(audio, AUDIO_GET_SAMPLE_RATE, &sample_rate) != -1)
        {
        if (oldfreq != sample_rate)
            {
            sbuffer = (short *)realloc(sbuffer, 2 * channels * sample_rate);
            buffer = (char *)sbuffer;
            return sample_rate;
            }
        }
    return 0;
    }

void output_samples(left, right)
int left, right;
    {
    if (stereo)
        {
        sbuffer[index++] = (left * primary + right * secondary)/256;
        sbuffer[index++] = (right * primary + left * secondary)/256;
        }
    else
        sbuffer[index++] = left + right;
    }

void flush_buffer()
    {
    if (index >= min_samples)
        {
        write(audio, buffer, 2 * index);
        index = 0;
        }
    }

void discard_buffer()
    {
    if (ioctl(audio, AUDIO_RESET, RESET_TX_BUF) == -1)
		notice("Warning: could not discard audio buffer");
    }

void close_audio()
    {
    free(buffer);
    close(audio);
    close(audio_ctl);
    }

