shithub: sox

ref: 7ecafdb3d70342eaf7e5299cb2989bf1967f1e64
dir: /src/sunaudio.c/

View raw version
/*
 * Copyright 1997 Chris Bagwell And Sundry Contributors
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained.
 * Rick Richardson, Lance Norskog And Sundry Contributors are not
 * responsible for the consequences of using this software.
 */

/* Direct to Sun Audio Driver
 *
 * Added by Chris Bagwell (cbagwell@sprynet.com) on 2/26/96
 * Based on oss driver.
 *
 * Cleaned up changes of format somewhat in sunstartwrite on 03/31/98
 *
 */

#include "st_i.h"

#if     defined(HAVE_SUNAUDIO)

#include <sys/ioctl.h>
#if defined(__SVR4) || defined(__NetBSD__) || defined(__OpenBSD__)
#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/types.h>  /* This should be in audioio.h itself but its not */
#endif
#include <sys/audioio.h>
#else
#include <sun/audioio.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#if !defined(__NetBSD__) && !defined(__OpenBSD__)
#include <stropts.h>
#endif
#ifdef _HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
/*
 * Do anything required before you start reading samples.
 * Read file header.
 *      Find out sampling rate,
 *      size and encoding of samples,
 *      mono/stereo/quad.
 */
int st_sunstartread(ft_t ft)
{
    int samplesize, encoding;
    audio_info_t audio_if;
#ifdef __SVR4
    audio_device_t audio_dev;
#endif
    char simple_hw=0;

    /* Hard code for now. */
    ft->file.count = 0;
    ft->file.pos = 0;
    ft->file.eof = 0;
    ft->file.size = 1024;
    if ((ft->file.buf = malloc (ft->file.size)) == NULL) {
        st_fail_errno(ft,ST_ENOMEM,"unable to allocate input buffer of size %d", ft->file.size);
        return ST_EOF;
    }

    if (ft->info.rate == 0.0) ft->info.rate = 8000;
    if (ft->info.size == -1) ft->info.size = ST_SIZE_BYTE;
    if (ft->info.encoding == -1) ft->info.encoding = ST_ENCODING_ULAW;

#ifdef __SVR4
    /* Read in old values, change to what we need and then send back */
    if (ioctl(fileno(ft->fp), AUDIO_GETDEV, &audio_dev) < 0) {
        st_fail_errno(ft,errno,"Unable to get device information.");
        return(ST_EOF);
    }
    st_report("Hardware detected:  %s\n",audio_dev.name);
    if (strcmp("SUNW,am79c30",audio_dev.name) == 0)
    {
        simple_hw = 1;
    }
#endif

    // If simple hardware detected in force data to ulaw.
    if (simple_hw)
    {
        if (ft->info.size == ST_SIZE_BYTE)
        {
            if (ft->info.encoding != ST_ENCODING_ULAW &&
                ft->info.encoding != ST_ENCODING_ALAW)
            {
                st_report("Warning: Detected simple hardware.  Forcing output to ULAW");
                ft->info.encoding = ST_ENCODING_ULAW;
            }
        }
        else if (ft->info.size == ST_SIZE_WORD)
        {
            st_report("Warning: Detected simple hardware.  Forcing output to ULAW");
            ft->info.size = ST_SIZE_BYTE;
            ft->info.encoding = ST_ENCODING_ULAW;
        }
    }

    if (ft->info.size == ST_SIZE_BYTE) {
        samplesize = 8;
        if (ft->info.encoding != ST_ENCODING_ULAW &&
            ft->info.encoding != ST_ENCODING_ALAW &&
            ft->info.encoding != ST_ENCODING_SIGN2) {
            st_fail_errno(ft,ST_EFMT,"Sun Audio driver only supports ULAW, ALAW, and Signed Linear for bytes.");
                return (ST_EOF);
        }
        if ((ft->info.encoding == ST_ENCODING_ULAW ||
             ft->info.encoding == ST_ENCODING_ALAW) && ft->info.channels == 2)
        {
            st_report("Warning: only support mono for ULAW and ALAW data.  Forcing to mono");
            ft->info.channels = 2;
        }
    }
    else if (ft->info.size == ST_SIZE_WORD) {
        samplesize = 16;
        if (ft->info.encoding != ST_ENCODING_SIGN2) {
            st_fail_errno(ft,ST_EFMT,"Sun Audio driver only supports Signed Linear for words.");
            return(ST_EOF);
        }
    }
    else {
        st_fail_errno(ft,ST_EFMT,"Sun Audio driver only supports bytes and words");
        return(ST_EOF);
    }

    if (ft->info.channels == -1) ft->info.channels = 1;
    else if (ft->info.channels > 1) {
        st_report("Warning: some sun audio devices can not play stereo");
        st_report("at all or sometime only with signed words.  If the");
        st_report("sound seems sluggish then this is probably the case.");
        st_report("Try forcing output to signed words or use the avg");
        st_report("filter to reduce the number of channels.");
        ft->info.channels = 2;
    }

    /* Read in old values, change to what we need and then send back */
    if (ioctl(fileno(ft->fp), AUDIO_GETINFO, &audio_if) < 0) {
        st_fail_errno(ft,errno,"Unable to initialize /dev/audio");
        return(ST_EOF);
    }
    audio_if.record.precision = samplesize;
    audio_if.record.channels = ft->info.channels;
    audio_if.record.sample_rate = ft->info.rate;
    if (ft->info.encoding == ST_ENCODING_ULAW)
        encoding = AUDIO_ENCODING_ULAW;
    else if (ft->info.encoding == ST_ENCODING_ALAW)
        encoding = AUDIO_ENCODING_ALAW;
    else
        encoding = AUDIO_ENCODING_LINEAR;
    audio_if.record.encoding = encoding;

    ioctl(fileno(ft->fp), AUDIO_SETINFO, &audio_if);
    if (audio_if.record.precision != samplesize) {
        st_fail_errno(ft,errno,"Unable to initialize sample size for /dev/audio");
        return(ST_EOF);
    }
    if (audio_if.record.channels != ft->info.channels) {
        st_fail_errno(ft,errno,"Unable to initialize number of channels for /dev/audio");
        return(ST_EOF);
    }
    if (audio_if.record.sample_rate != ft->info.rate) {
        st_fail_errno(ft,errno,"Unable to initialize rate for /dev/audio");
        return(ST_EOF);
    }
    if (audio_if.record.encoding != encoding) {
        st_fail_errno(ft,errno,"Unable to initialize encoding for /dev/audio");
        return(ST_EOF);
    }
    /* Flush any data in the buffers - its probably in the wrong format */
#if defined(__NetBSD__) || defined(__OpenBSD__)
    ioctl(fileno(ft->fp), AUDIO_FLUSH);
#else
    ioctl(fileno(ft->fp), I_FLUSH, FLUSHR);
#endif
    /* Change to non-buffered I/O*/
    setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size);

    return (ST_SUCCESS);
}

int st_sunstartwrite(ft_t ft)
{
    int samplesize, encoding;
    audio_info_t audio_if;
#ifdef __SVR4
    audio_device_t audio_dev;
#endif
    char simple_hw=0;

    /* Hard code for now. */
    ft->file.count = 0;
    ft->file.pos = 0;
    ft->file.eof = 0;
    ft->file.size = 1024;
    if ((ft->file.buf = malloc (ft->file.size)) == NULL) {
        st_fail_errno(ft,ST_ENOMEM,"unable to allocate output buffer of size %d", ft->file.size);
        return(ST_EOF);
    }

#ifdef __SVR4
    /* Read in old values, change to what we need and then send back */
    if (ioctl(fileno(ft->fp), AUDIO_GETDEV, &audio_dev) < 0) {
        st_fail_errno(ft,errno,"Unable to get device information.");
        return(ST_EOF);
    }
    st_report("Hardware detected:  %s\n",audio_dev.name);
    if (strcmp("SUNW,am79c30",audio_dev.name) == 0)
    {
        simple_hw = 1;
    }
#endif

    if (simple_hw)
    {
        if (ft->info.size == ST_SIZE_BYTE)
        {
            if (ft->info.encoding != ST_ENCODING_ULAW &&
                ft->info.encoding != ST_ENCODING_ALAW)
            {
                st_report("Warning: Detected simple hardware.  Forcing output to ULAW");
                ft->info.encoding = ST_ENCODING_ULAW;
            }
        }
        else if (ft->info.size == ST_SIZE_WORD)
        {
            st_report("Warning: Detected simple hardware.  Forcing output to ULAW");
            ft->info.size = ST_SIZE_BYTE;
            ft->info.encoding = ST_ENCODING_ULAW;
        }
    }

    if (ft->info.rate == 0.0) ft->info.rate = 8000;
    if (ft->info.size == -1) ft->info.size = ST_SIZE_BYTE;
    if (ft->info.encoding == -1) ft->info.encoding = ST_ENCODING_ULAW;

    if (ft->info.size == ST_SIZE_BYTE) {
        samplesize = 8;
        if (ft->info.encoding != ST_ENCODING_ULAW &&
            ft->info.encoding != ST_ENCODING_ALAW &&
            ft->info.encoding != ST_ENCODING_SIGN2) {
            st_report("Sun Audio driver only supports ULAW, ALAW, and Signed Linear for bytes.");
            st_report("Forcing to ULAW");
            ft->info.encoding = ST_ENCODING_ULAW;
        }
        if ((ft->info.encoding == ST_ENCODING_ULAW ||
             ft->info.encoding == ST_ENCODING_ALAW) && ft->info.channels == 2)
        {
            st_report("Warning: only support mono for ULAW and ALAW data.  Forcing to mono");
            ft->info.channels = 2;
        }

    }
    else if (ft->info.size == ST_SIZE_WORD) {
        samplesize = 16;
        if (ft->info.encoding != ST_ENCODING_SIGN2) {
            st_report("Sun Audio driver only supports Signed Linear for words.");
            st_report("Forcing to Signed Linear");
            ft->info.encoding = ST_ENCODING_SIGN2;
        }
    }
    else {
        st_report("Sun Audio driver only supports bytes and words");
        ft->info.size = ST_SIZE_WORD;
        samplesize = 16;
    }

    if (ft->info.channels == -1) ft->info.channels = 1;
    else if (ft->info.channels > 1) ft->info.channels = 2;

    /* Read in old values, change to what we need and then send back */
    if (ioctl(fileno(ft->fp), AUDIO_GETINFO, &audio_if) < 0) {
        st_fail_errno(ft,errno,"Unable to initialize /dev/audio");
        return(ST_EOF);
    }
    audio_if.play.precision = samplesize;
    audio_if.play.channels = ft->info.channels;
    audio_if.play.sample_rate = ft->info.rate;
    if (ft->info.encoding == ST_ENCODING_ULAW)
        encoding = AUDIO_ENCODING_ULAW;
    else if (ft->info.encoding == ST_ENCODING_ALAW)
        encoding = AUDIO_ENCODING_ALAW;
    else
        encoding = AUDIO_ENCODING_LINEAR;
    audio_if.play.encoding = encoding;

    ioctl(fileno(ft->fp), AUDIO_SETINFO, &audio_if);
    if (audio_if.play.precision != samplesize) {
        st_fail_errno(ft,errno,"Unable to initialize sample size for /dev/audio");
        return(ST_EOF);
    }
    if (audio_if.play.channels != ft->info.channels) {
        st_fail_errno(ft,errno,"Unable to initialize number of channels for /dev/audio");
        return(ST_EOF);
    }
    if (audio_if.play.sample_rate != ft->info.rate) {
        st_fail_errno(ft,errno,"Unable to initialize rate for /dev/audio");
        return(ST_EOF);
    }
    if (audio_if.play.encoding != encoding) {
        st_fail_errno(ft,errno,"Unable to initialize encoding for /dev/audio");
        return(ST_EOF);
    }
    /* Change to non-buffered I/O */
    setvbuf(ft->fp, NULL, _IONBF, sizeof(char) * ft->file.size);

    return (ST_SUCCESS);
}

/* Sun /dev/audio player */
static char *sunnames[] = {
  "sunau",
  NULL
};

static st_format_t st_sun_format = {
  sunnames,
  NULL,
  ST_FILE_STEREO,
  st_sunstartread,
  st_rawread,
  st_rawstopread,
  st_sunstartwrite,
  st_rawwrite,
  st_rawstopwrite,
  st_format_nothing_seek
};

const st_format_t *st_sun_format_fn(void)
{
    return &st_sun_format;
}
#endif