root/usr.bin/audio/common/sun.c
/*      $NetBSD: sun.c,v 1.11 2024/02/27 21:05:34 gson Exp $    */

/*
 * Copyright (c) 2002, 2013, 2015 Matthew R. Green
 * 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. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 */

/*
 * XXX this is slightly icky in places...
 */
#include <sys/cdefs.h>

#ifndef lint
__RCSID("$NetBSD: sun.c,v 1.11 2024/02/27 21:05:34 gson Exp $");
#endif


#include <sys/types.h>
#include <sys/audioio.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "libaudio.h"
#include "auconv.h"

/*
 * SunOS/NeXT .au format helpers
 */
static const struct {
        int     file_encoding;
        int     encoding;
        int     precision;
} file2sw_encodings[] = {
        { AUDIO_FILE_ENCODING_MULAW_8,          AUDIO_ENCODING_ULAW,    8 },
        { AUDIO_FILE_ENCODING_LINEAR_8,         AUDIO_ENCODING_SLINEAR_BE, 8 },
        { AUDIO_FILE_ENCODING_LINEAR_16,        AUDIO_ENCODING_SLINEAR_BE, 16 },
        { AUDIO_FILE_ENCODING_LINEAR_24,        AUDIO_ENCODING_SLINEAR_BE, 24 },
        { AUDIO_FILE_ENCODING_LINEAR_32,        AUDIO_ENCODING_SLINEAR_BE, 32 },
#if 0
        /*
         * we should make some of these available.  the, eg ultrasparc, port
         * can use the VIS instructions (if available) do do some of these
         * mpeg ones.
         */
        { AUDIO_FILE_ENCODING_FLOAT,            AUDIO_ENCODING_ULAW,    32 },
        { AUDIO_FILE_ENCODING_DOUBLE,           AUDIO_ENCODING_ULAW,    64 },
        { AUDIO_FILE_ENCODING_ADPCM_G721,       AUDIO_ENCODING_ULAW,    4 },
        { AUDIO_FILE_ENCODING_ADPCM_G722,       AUDIO_ENCODING_ULAW,    0 },
        { AUDIO_FILE_ENCODING_ADPCM_G723_3,     AUDIO_ENCODING_ULAW,    3 },
        { AUDIO_FILE_ENCODING_ADPCM_G723_5,     AUDIO_ENCODING_ULAW,    5 },
#endif
        { AUDIO_FILE_ENCODING_ALAW_8,           AUDIO_ENCODING_ALAW,    8 },
        { -1, -1, -1 }
};

int
audio_sun_to_encoding(int sun_encoding, u_int *encp, u_int *precp)
{
        int i;

        for (i = 0; file2sw_encodings[i].file_encoding != -1; i++)
                if (file2sw_encodings[i].file_encoding == sun_encoding) {
                        *precp = file2sw_encodings[i].precision;
                        *encp = file2sw_encodings[i].encoding;
                        return (0);
                }
        return (1);
}

int
audio_encoding_to_sun(int encoding, int precision, int *sunep)
{
        int i;

        for (i = 0; file2sw_encodings[i].file_encoding != -1; i++)
                if (file2sw_encodings[i].encoding == encoding &&
                    file2sw_encodings[i].precision == precision) {
                        *sunep = file2sw_encodings[i].file_encoding;
                        return (0);
                }
        return (1);
}

int
sun_prepare_header(struct track_info *ti, void **hdrp, size_t *lenp, int *leftp)
{
        static int warned = 0;
        static sun_audioheader auh;
        int sunenc, oencoding = ti->encoding;

        /* only perform conversions if we don't specify the encoding */
        switch (ti->encoding) {

        case AUDIO_ENCODING_ULINEAR_LE:
#if BYTE_ORDER == LITTLE_ENDIAN
        case AUDIO_ENCODING_ULINEAR:
#endif
                if (ti->precision == 16 || ti->precision == 32)
                        ti->encoding = AUDIO_ENCODING_SLINEAR_BE;
                break;

        case AUDIO_ENCODING_ULINEAR_BE:
#if BYTE_ORDER == BIG_ENDIAN
        case AUDIO_ENCODING_ULINEAR:
#endif
                if (ti->precision == 16 || ti->precision == 32)
                        ti->encoding = AUDIO_ENCODING_SLINEAR_BE;
                break;

        case AUDIO_ENCODING_SLINEAR_LE:
#if BYTE_ORDER == LITTLE_ENDIAN
        case AUDIO_ENCODING_SLINEAR:
#endif
                if (ti->precision == 16 || ti->precision == 32)
                        ti->encoding = AUDIO_ENCODING_SLINEAR_BE;
                break;

#if BYTE_ORDER == BIG_ENDIAN
        case AUDIO_ENCODING_SLINEAR:
                ti->encoding = AUDIO_ENCODING_SLINEAR_BE;
                break;
#endif
        }

        /* if we can't express this as a Sun header, don't write any */
        if (audio_encoding_to_sun(ti->encoding, ti->precision, &sunenc) != 0) {
                if (!ti->qflag && !warned) {
                        const char *s = audio_enc_from_val(oencoding);

                        if (s == NULL)
                                s = "(unknown)";
                        warnx("failed to convert to sun encoding from %s "
                              "(precision %d);\nSun audio header not written",
                              s, ti->precision);
                }
                ti->format = AUDIO_FORMAT_NONE;
                warned = 1;
                return -1;
        }

        auh.magic = htonl(AUDIO_FILE_MAGIC);
        if (ti->outfd == STDOUT_FILENO)
                auh.data_size = htonl(AUDIO_UNKNOWN_SIZE);
        else if (ti->total_size != -1)
                auh.data_size = htonl(ti->total_size);
        else
                auh.data_size = 0;
        auh.encoding = htonl(sunenc);
        auh.sample_rate = htonl(ti->sample_rate);
        auh.channels = htonl(ti->channels);
        if (ti->header_info) {
                int     len, infolen;

                infolen = ((len = strlen(ti->header_info)) + 7) & 0xfffffff8;
                *leftp = infolen - len;
                auh.hdr_size = htonl(sizeof(auh) + infolen);
        } else {
                *leftp = sizeof(audio_default_info);
                auh.hdr_size = htonl(sizeof(auh) + *leftp);
        }
        *(sun_audioheader **)hdrp = &auh;
        *lenp = sizeof auh;
        return 0;
}

write_conv_func
sun_write_get_conv_func(struct track_info *ti)
{
        write_conv_func conv_func = NULL;

        /* only perform conversions if we don't specify the encoding */
        switch (ti->encoding) {

        case AUDIO_ENCODING_ULINEAR_LE:
#if BYTE_ORDER == LITTLE_ENDIAN
        case AUDIO_ENCODING_ULINEAR:
#endif
                if (ti->precision == 16)
                        conv_func = change_sign16_swap_bytes_le;
                else if (ti->precision == 32)
                        conv_func = change_sign32_swap_bytes_le;
                break;

        case AUDIO_ENCODING_ULINEAR_BE:
#if BYTE_ORDER == BIG_ENDIAN
        case AUDIO_ENCODING_ULINEAR:
#endif
                if (ti->precision == 16)
                        conv_func = change_sign16_be;
                else if (ti->precision == 32)
                        conv_func = change_sign32_be;
                break;

        case AUDIO_ENCODING_SLINEAR_LE:
#if BYTE_ORDER == LITTLE_ENDIAN
        case AUDIO_ENCODING_SLINEAR:
#endif
                if (ti->precision == 16)
                        conv_func = swap_bytes;
                else if (ti->precision == 32)
                        conv_func = swap_bytes32;
                break;
        }

        return conv_func;
}