shithub: sox

Download patch

ref: 18d0f1f3e1b320d3fa691cd333cf04d0367fe8e4
parent: 65f3eca4990d29f08256ef7b72f07561062f7682
author: rrt <rrt>
date: Mon Nov 13 20:32:50 EST 2006

Add file missing from earlier commit

--- /dev/null
+++ b/src/xa.c
@@ -1,0 +1,354 @@
+/*
+ * xa.c  Support for Maxis .XA file format
+ * 
+ *      Copyright (C) 2006 Dwayne C. Litzenberger <dlitz@dlitz.net>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Library General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301
+ *   USA
+ *
+ */
+
+/* Thanks to Valery V. Anisimovsky <samael@avn.mccme.ru> for the 
+ * "Maxis XA Audio File Format Description", dated 5-01-2002. */
+
+#include <string.h>             /* Included for strncmp */
+#include <stdlib.h>             /* Included for malloc and free */
+#include <stdio.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>             /* For SEEK_* defines if not found in stdio */
+#endif
+
+#include "st_i.h"
+
+#define HNIBBLE(byte) (((byte) >> 4) & 0xf)
+#define LNIBBLE(byte) ((byte) & 0xf)
+
+/* .xa file header */
+typedef struct {
+    char magic[4];  /* "XA\0\0", "XAI\0" (sound/speech), or "XAJ\0" (music) */
+    uint32_t outSize;       /* decompressed size of the stream (in bytes) */
+
+    /* WAVEFORMATEX structure for the decompressed data */
+    uint16_t tag;           /* 0x0001 - PCM data */
+    uint16_t channels;      /* number of channels */
+    uint32_t sampleRate;    /* sample rate (samples/sec) */
+    uint32_t avgByteRate;   /* sampleRate * align */
+    uint16_t align;         /* bits / 8 * channels */
+    uint16_t bits;          /* 8 or 16 */
+} xa_header_t;
+
+typedef struct {
+    int32_t curSample;      /* current sample */
+    int32_t prevSample;     /* previous sample */
+    int32_t c1;
+    int32_t c2;
+    unsigned int shift;
+} xa_state_t;
+
+/* Private data for .xa file */
+typedef struct xastuff {
+    xa_header_t header;
+    xa_state_t *state;
+    unsigned int blockSize;
+    unsigned int bufPos;    /* position within the current block */
+    unsigned char *buf;     /* buffer for the current block */
+    unsigned int bytesDecoded;  /* number of decompressed bytes read */
+} *xa_t;
+
+/* coefficients for EA ADPCM */
+static int32_t EA_ADPCM_Table[]= {
+    0, 240,  460,  392,
+    0,   0, -208, -220,
+    0,   1,    3,    4,
+    7,   8,   10,   11,
+    0,  -1,   -3,   -4
+};
+
+/* Clip sample to 16 bits */
+static inline int32_t clip16(int32_t sample)
+{
+    if (sample > 32767) {
+        return 32767;
+    } else if (sample < -32768) {
+        return -32768;
+    } else {
+        return sample;
+    }
+}
+
+static int st_xastartread(ft_t ft)
+{
+    xa_t xa = (xa_t) ft->priv;
+    char *magic = xa->header.magic;
+
+    /* Check for the magic value */
+    if (st_readbuf(ft, xa->header.magic, 1, 4) != 4 ||
+        (memcmp("XA\0\0", xa->header.magic, 4) != 0 &&
+         memcmp("XAI\0", xa->header.magic, 4) != 0 &&
+         memcmp("XAJ\0", xa->header.magic, 4) != 0))
+    {
+        st_fail_errno(ft, ST_EHDR, "XA: Header not found");
+        return ST_EOF;
+    }
+    
+    /* Byte-swap on big-endian systems */
+    if (ST_IS_BIGENDIAN) ft->swap = ft->swap ? 0 : 1;
+
+    /* Read the rest of the header */
+    if (st_readdw(ft, &xa->header.outSize) != ST_SUCCESS) return ST_EOF;
+    if (st_readw(ft, &xa->header.tag) != ST_SUCCESS) return ST_EOF;
+    if (st_readw(ft, &xa->header.channels) != ST_SUCCESS) return ST_EOF;
+    if (st_readdw(ft, &xa->header.sampleRate) != ST_SUCCESS) return ST_EOF;
+    if (st_readdw(ft, &xa->header.avgByteRate) != ST_SUCCESS) return ST_EOF;
+    if (st_readw(ft, &xa->header.align) != ST_SUCCESS) return ST_EOF;
+    if (st_readw(ft, &xa->header.bits) != ST_SUCCESS) return ST_EOF;
+
+    /* Output the data from the header */
+    st_report("XA Header:");
+    st_report(" szID:          %02x %02x %02x %02x  |%c%c%c%c|",
+        magic[0], magic[1], magic[2], magic[3],
+        (magic[0] >= 0x20 && magic[0] <= 0x7e) ? magic[0] : '.',
+        (magic[1] >= 0x20 && magic[1] <= 0x7e) ? magic[1] : '.',
+        (magic[2] >= 0x20 && magic[2] <= 0x7e) ? magic[2] : '.',
+        (magic[3] >= 0x20 && magic[3] <= 0x7e) ? magic[3] : '.');
+    st_report(" dwOutSize:     %u", xa->header.outSize);
+    st_report(" wTag:          0x%04x", xa->header.tag);
+    st_report(" wChannels:     %u", xa->header.channels);
+    st_report(" dwSampleRate:  %u", xa->header.sampleRate);
+    st_report(" dwAvgByteRate: %u", xa->header.avgByteRate);
+    st_report(" wAlign:        %u", xa->header.align);
+    st_report(" wBits:         %u", xa->header.bits);
+
+    /* Populate the st_soundstream structure */
+    ft->info.encoding = ST_ENCODING_SIGN2;
+    
+    if (ft->info.size == -1 || ft->info.size == (xa->header.bits >> 3)) {
+        ft->info.size = xa->header.bits >> 3;
+    } else {
+        st_report("User options overriding size read in .xa header");
+    }
+    
+    if (ft->info.channels == -1 || ft->info.channels == xa->header.channels) {
+        ft->info.channels = xa->header.channels;
+    } else {
+        st_report("User options overriding channels read in .xa header");
+    }
+    
+    if (ft->info.rate == 0 || ft->info.rate == xa->header.sampleRate) {
+        ft->info.rate = xa->header.sampleRate;
+    } else {
+        st_report("User options overriding rate read in .xa header");
+    }
+    
+    /* Check for supported formats */
+    if (ft->info.size != 2) {
+        st_fail_errno(ft, ST_EFMT, "%d-bit sample resolution not supported.",
+            ft->info.size << 3);
+        return ST_EOF;
+    }
+    
+    /* Validate the header */
+    if (xa->header.bits != ft->info.size << 3) {
+        st_report("Invalid sample resolution %d bits.  Assuming %d bits.",
+            xa->header.bits, ft->info.size << 3);
+        xa->header.bits = ft->info.size << 3;
+    }
+    if (xa->header.align != ft->info.size * xa->header.channels) {
+        st_report("Invalid sample alignment value %d.  Assuming %d.",
+            xa->header.align, ft->info.size * xa->header.channels);
+        xa->header.align = ft->info.size * xa->header.channels;
+    }
+    if (xa->header.avgByteRate != (xa->header.align * xa->header.sampleRate)) {
+        st_report("Invalid dwAvgByteRate value %d.  Assuming %d.",
+            xa->header.avgByteRate, xa->header.align * xa->header.sampleRate);
+        xa->header.avgByteRate = xa->header.align * xa->header.sampleRate;
+    }
+
+    /* Set up the block buffer */
+    xa->blockSize = ft->info.channels * 0xf;
+    xa->bufPos = xa->blockSize;
+
+    /* Allocate memory for the block buffer */
+    xa->buf = (unsigned char *) calloc(1, xa->blockSize);
+    if (xa->buf == NULL) {
+        st_fail_errno(ft, ST_ENOMEM, "Unable to allocate block buffer");
+        return ST_EOF;
+    }
+    
+    /* Allocate memory for the state */
+    xa->state = (xa_state_t *) calloc(sizeof(xa_state_t), ft->info.channels);
+    if (xa->state == NULL) {
+        /* Free xa->buf */
+        free(xa->buf);
+        xa->buf = NULL;
+        
+        /* Return error */
+        st_fail_errno(ft, ST_ENOMEM, "Unable to allocate state variables");
+        return ST_EOF;
+    }
+    
+    /* Final initialization */
+    xa->bytesDecoded = 0;
+    
+    return ST_SUCCESS;
+}
+
+/* 
+ * Read up to len samples from a file, converted to signed longs.
+ * Return the number of samples read.
+ */
+static st_ssize_t st_xaread(ft_t ft, st_sample_t *buf, st_ssize_t len)
+{
+    xa_t xa = (xa_t) ft->priv;
+    st_ssize_t done;
+    st_ssize_t bytes;
+    int32_t sample;
+    unsigned char inByte;
+    unsigned int i;
+
+    ft->st_errno = ST_SUCCESS;
+    done = 0;
+    while (done < len) {
+        if (xa->bufPos >= xa->blockSize) {
+            /* Read the next block */
+            bytes = st_readbuf(ft, xa->buf, 1, xa->blockSize);
+            if (bytes < xa->blockSize) {
+                if (st_eof(ft)) {
+                    if (done > 0) {
+                        return done;
+                    }
+                    st_fail_errno(ft,ST_EOF,"Premature EOF on .xa input file");
+                    return ST_EOF;
+                } else {
+                    /* error */
+                    st_fail_errno(ft,ST_EOF,"read error on input stream");
+                    return ST_EOF;
+                }
+            }
+            xa->bufPos = 0;
+            
+            for (i = 0; i < ft->info.channels; i++) {
+                inByte = xa->buf[i];
+                xa->state[i].c1 = EA_ADPCM_Table[HNIBBLE(inByte)];
+                xa->state[i].c2 = EA_ADPCM_Table[HNIBBLE(inByte) + 4];
+                xa->state[i].shift = LNIBBLE(inByte) + 8;
+            }
+            xa->bufPos += ft->info.channels;
+        } else {
+            /* Process the block */
+            for (i = 0; i < ft->info.channels && done < len; i++) {
+                /* high nibble */
+                sample = HNIBBLE(xa->buf[xa->bufPos+i]);
+                sample = (sample << 28) >> xa->state[i].shift;
+                sample = (sample +
+                          xa->state[i].curSample * xa->state[i].c1 +
+                          xa->state[i].prevSample * xa->state[i].c2 + 0x80) >> 8;
+                sample = clip16(sample);
+                xa->state[i].prevSample = xa->state[i].curSample;
+                xa->state[i].curSample = sample;
+                
+                buf[done++] = ST_SIGNED_WORD_TO_SAMPLE(sample);
+                xa->bytesDecoded += ft->info.size;
+            }
+            for (i = 0; i < ft->info.channels && done < len; i++) {
+                /* low nibble */
+                sample = LNIBBLE(xa->buf[xa->bufPos+i]);
+                sample = (sample << 28) >> xa->state[i].shift;
+                sample = (sample +
+                          xa->state[i].curSample * xa->state[i].c1 +
+                          xa->state[i].prevSample * xa->state[i].c2 + 0x80) >> 8;
+                sample = clip16(sample);
+                xa->state[i].prevSample = xa->state[i].curSample;
+                xa->state[i].curSample = sample;
+                
+                buf[done++] = ST_SIGNED_WORD_TO_SAMPLE(sample);
+                xa->bytesDecoded += ft->info.size;
+            }
+
+            xa->bufPos += ft->info.channels;
+        }
+    }
+    if (done == 0) {
+        return ST_EOF;
+    }
+    return done;
+}
+
+static int st_xastopread(ft_t ft)
+{
+    xa_t xa = (xa_t) ft->priv;
+
+    ft->st_errno = ST_SUCCESS;
+
+    /* Free memory */
+    if (xa->buf != NULL) {
+        free(xa->buf);
+        xa->buf = NULL;
+    }
+    if (xa->state != NULL) {
+        free(xa->state);
+        xa->state = NULL;
+    }
+    
+    return ST_SUCCESS;
+}
+
+static int st_xastartwrite(ft_t ft)
+{
+    st_fail_errno(ft, ST_ENOTSUP, ".XA writing not supported");
+    return ST_EOF;
+}
+
+static st_ssize_t st_xawrite(ft_t ft, st_sample_t *buf, st_ssize_t len)
+{
+    st_fail_errno(ft, ST_ENOTSUP, ".XA writing not supported");
+    return ST_EOF;
+}
+
+static int st_xastopwrite(ft_t ft)
+{
+    st_fail_errno(ft, ST_ENOTSUP, ".XA writing not supported");
+    return ST_EOF;
+}
+
+static int st_xaseek(ft_t ft, st_size_t offset)
+{
+    return st_format_nothing_seek(ft, offset);
+}
+
+/* Maxis .xa */
+static char *xanames[] = {
+    "xa",
+    NULL
+};
+
+st_format_t st_xa_format = {
+  xanames,
+  NULL,
+  ST_FILE_STEREO,
+  st_xastartread,
+  st_xaread,
+  st_xastopread,
+  st_xastartwrite,
+  st_xawrite,
+  st_xastopwrite,
+  st_xaseek
+};
+
+const st_format_t *st_xa_format_fn(void)
+{
+  return &st_xa_format;
+}