/*	$OpenBSD: audio_sun.c,v 1.3 2004/01/25 22:07:28 fgsch Exp $	*/

/*
 * Copyright (c) 2002 CubeSoft Communications, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistribution of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Neither the name of CubeSoft Communications, nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#include <sys/audioio.h>

static void sun_panic(int fd, char *s)
{
    perror(s);
    close(fd);
    esd_audio_fd = -1;
}

#define ARCH_esd_audio_devices
const char *esd_audio_devices()
{
    return "/dev/audio";
}


#define ARCH_esd_audio_open
int esd_audio_open()
{
    const char *device;
    int afd = -1;
    int fmt = 0, channels = 0;
    int mode = O_WRONLY;
    audio_encoding_t enc;
    audio_info_t info;

    AUDIO_INITINFO(&info);

    /* set the appropriate mode */
    if((esd_audio_format & ESD_MASK_FUNC) == ESD_RECORD) {
        mode = O_RDWR;
	info.mode = AUMODE_PLAY | AUMODE_RECORD;
    } else {
	info.mode = AUMODE_PLAY;
    }

    mode |= O_NONBLOCK;

    /* open the sound device */
    device = esd_audio_device ? esd_audio_device : "/dev/audio";
    if ((afd = open(device, mode, 0)) == -1) {
        perror(device);
        return( -2 );
    }

    mode = fcntl(afd, F_GETFL);
    mode &= ~O_NONBLOCK;
    fcntl(afd, F_SETFL, mode);

    /* set the requested mode */
    if(ioctl(afd, AUDIO_SETINFO, &info) < 0) {
	sun_panic(afd, "AUDIO_SETINFO");
	return(-1);
    }

    /* set full-duplex mode if we are recording */
    if ( (esd_audio_format & ESD_MASK_FUNC) == ESD_RECORD ) {
	if (ioctl(afd, AUDIO_SETFD, 1) == -1) {
	    sun_panic(afd, "AUDIO_SETFD");
	    return(-1);
	}
    }

    /* pick a supported encoding */
    
#if defined(WORDS_BIGENDIAN)
    fmt = ( (esd_audio_format & ESD_MASK_BITS) == ESD_BITS16 )
        ? /* 16 bit */  AUDIO_ENCODING_SLINEAR_BE
	: /* 8 bit */	AUDIO_ENCODING_PCM8;
#else
    fmt = ( (esd_audio_format & ESD_MASK_BITS) == ESD_BITS16 )
        ? /* 16 bit */	AUDIO_ENCODING_SLINEAR_LE
	: /* 8 bit */	AUDIO_ENCODING_PCM8;
#endif

    enc.index = 0;
    while(!ioctl(afd, AUDIO_GETENC, &enc) && enc.encoding != fmt) {
	enc.index++;
    }
    
    /* number of channels */
    channels = (((esd_audio_format & ESD_MASK_CHAN) == ESD_STEREO)
        ? /* stereo */	2
	: /* mono */	1);

    info.play.channels = channels;
    if((esd_audio_format & ESD_MASK_FUNC) == ESD_RECORD) {
	info.record.channels = channels;
    }

    info.play.encoding = fmt;
    info.play.precision = enc.precision;

    if(ioctl(afd, AUDIO_SETINFO, &info) == -1) {
	fprintf(stderr, "Unsupported encoding: %i-bit \"%s\" (0x%x)\n",
	    enc.precision, enc.name, fmt);
	sun_panic(afd, "SETINFO");
	return(-1);
    }

    /* finally, set the sample rate */
    info.play.sample_rate = esd_audio_rate;
    if(ioctl(afd, AUDIO_SETINFO, &info) < 0 || 
	fabs(info.play.sample_rate - esd_audio_rate) > esd_audio_rate * 0.05) {
	fprintf(stderr, "Unsupported rate: %i Hz\n", esd_audio_rate);
	sun_panic(afd, "SETINFO");
	return(-1);
    }

    return(esd_audio_fd = afd);
}

#define ARCH_esd_audio_pause
void esd_audio_pause()
{
    audio_info_t info;
    int afd = esd_audio_fd;

    AUDIO_INITINFO(&info);    

    if(ioctl(afd, AUDIO_GETINFO, &info) < 0) {
	sun_panic(afd, "AUDIO_GETINFO");
	return;
    }

    if((info.mode & AUMODE_PLAY) == AUMODE_PLAY)
	info.play.pause = !info.play.pause;
    if((info.mode & AUMODE_RECORD) == AUMODE_RECORD)
	info.record.pause = !info.record.pause;

    return;
}
