load_voc.c
changeset 111 3b709e102787
child 114 83ab4ef4458b
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/load_voc.c	Tue Sep 11 18:24:38 2001 +0000
     1.3 @@ -0,0 +1,472 @@
     1.4 +/*
     1.5 +    SDL_mixer:  An audio mixer library based on the SDL library
     1.6 +    Copyright (C) 1997-2001  Sam Lantinga
     1.7 +
     1.8 +    This library is free software; you can redistribute it and/or
     1.9 +    modify it under the terms of the GNU Library General Public
    1.10 +    License as published by the Free Software Foundation; either
    1.11 +    version 2 of the License, or (at your option) any later version.
    1.12 +
    1.13 +    This library is distributed in the hope that it will be useful,
    1.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.16 +    Library General Public License for more details.
    1.17 +
    1.18 +    You should have received a copy of the GNU Library General Public
    1.19 +    License along with this library; if not, write to the Free
    1.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.21 +
    1.22 +    This is the source needed to decode a Creative Labs VOC file into a
    1.23 +    waveform. It's pretty straightforward once you get going. The only
    1.24 +    externally-callable function is Mix_LoadVOC_RW(), which is meant to
    1.25 +    act as identically to SDL_LoadWAV_RW() as possible.
    1.26 +
    1.27 +    This file by Ryan C. Gordon (icculus@linuxgames.com).
    1.28 +
    1.29 +    Heavily borrowed from sox v12.17.1's voc.c.
    1.30 +        (http://www.freshmeat.net/projects/sox/)
    1.31 +*/
    1.32 +
    1.33 +/* $Id$ */
    1.34 +
    1.35 +#include <stdio.h>
    1.36 +#include <stdlib.h>
    1.37 +#include <string.h>
    1.38 +
    1.39 +#include "SDL_mutex.h"
    1.40 +#include "SDL_endian.h"
    1.41 +#include "SDL_timer.h"
    1.42 +
    1.43 +#include "SDL_mixer.h"
    1.44 +#include "load_voc.h"
    1.45 +
    1.46 +#ifdef VOC_SAMPLES
    1.47 +
    1.48 +/* Private data for VOC file */
    1.49 +typedef struct vocstuff {
    1.50 +	Uint32	rest;			/* bytes remaining in current block */
    1.51 +	Uint32	rate;			/* rate code (byte) of this chunk */
    1.52 +	int 	silent;		/* sound or silence? */
    1.53 +	Uint32	srate;			/* rate code (byte) of silence */
    1.54 +	Uint32	blockseek;		/* start of current output block */
    1.55 +	Uint32	samples;		/* number of samples output */
    1.56 +	Uint32	size;		/* word length of data */
    1.57 +	int 	channels;	/* number of sound channels */
    1.58 +	int     extended;       /* Has an extended block been read? */
    1.59 +} vs_t;
    1.60 +
    1.61 +/* Size field */ 
    1.62 +/* SJB: note that the 1st 3 are sometimes used as sizeof(type) */
    1.63 +#define	ST_SIZE_BYTE	1
    1.64 +#define ST_SIZE_8BIT	1
    1.65 +#define	ST_SIZE_WORD	2
    1.66 +#define ST_SIZE_16BIT	2
    1.67 +#define	ST_SIZE_DWORD	4
    1.68 +#define ST_SIZE_32BIT	4
    1.69 +#define	ST_SIZE_FLOAT	5
    1.70 +#define ST_SIZE_DOUBLE	6
    1.71 +#define ST_SIZE_IEEE	7	/* IEEE 80-bit floats. */
    1.72 +
    1.73 +/* Style field */
    1.74 +#define ST_ENCODING_UNSIGNED	1 /* unsigned linear: Sound Blaster */
    1.75 +#define ST_ENCODING_SIGN2	2 /* signed linear 2's comp: Mac */
    1.76 +#define	ST_ENCODING_ULAW	3 /* U-law signed logs: US telephony, SPARC */
    1.77 +#define ST_ENCODING_ALAW	4 /* A-law signed logs: non-US telephony */
    1.78 +#define ST_ENCODING_ADPCM	5 /* Compressed PCM */
    1.79 +#define ST_ENCODING_IMA_ADPCM	6 /* Compressed PCM */
    1.80 +#define ST_ENCODING_GSM		7 /* GSM 6.10 33-byte frame lossy compression */
    1.81 +
    1.82 +#define	VOC_TERM	0
    1.83 +#define	VOC_DATA	1
    1.84 +#define	VOC_CONT	2
    1.85 +#define	VOC_SILENCE	3
    1.86 +#define	VOC_MARKER	4
    1.87 +#define	VOC_TEXT	5
    1.88 +#define	VOC_LOOP	6
    1.89 +#define	VOC_LOOPEND	7
    1.90 +#define VOC_EXTENDED    8
    1.91 +#define VOC_DATA_16	9
    1.92 +
    1.93 +
    1.94 +static inline int voc_check_header(SDL_RWops *src)
    1.95 +{
    1.96 +    /* VOC magic header */
    1.97 +    Uint8  signature[20];  /* "Creative Voice File\032" */
    1.98 +    Uint16 datablockofs;
    1.99 +
   1.100 +    SDL_RWseek(src, 0, SEEK_SET);
   1.101 +
   1.102 +    if (SDL_RWread(src, signature, sizeof (signature), 1) != 1)
   1.103 +        return(0);
   1.104 +
   1.105 +    if (memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0) {
   1.106 +        SDL_SetError("Unrecognized file type (not VOC)");
   1.107 +        return(0);
   1.108 +    }
   1.109 +
   1.110 +        /* get the offset where the first datablock is located */
   1.111 +    if (SDL_RWread(src, &datablockofs, sizeof (Uint16), 1) != 1)
   1.112 +        return(0);
   1.113 +
   1.114 +    datablockofs = SDL_SwapLE16(datablockofs);
   1.115 +
   1.116 +    if (SDL_RWseek(src, datablockofs, SEEK_SET) != datablockofs)
   1.117 +        return(0);
   1.118 +
   1.119 +    return(1);  /* success! */
   1.120 +} /* voc_check_header */
   1.121 +
   1.122 +
   1.123 +/* Read next block header, save info, leave position at start of data */
   1.124 +static int voc_get_block(SDL_RWops *src, vs_t *v, SDL_AudioSpec *spec)
   1.125 +{
   1.126 +    Uint8 bits24[3];
   1.127 +    Uint8 uc, block;
   1.128 +    Uint32 sblen;
   1.129 +    Uint16 new_rate_short;
   1.130 +    Uint32 new_rate_long;
   1.131 +    Uint8 trash[6];
   1.132 +    Uint16 period;
   1.133 +    int i;
   1.134 +
   1.135 +    v->silent = 0;
   1.136 +    while (v->rest == 0)
   1.137 +    {
   1.138 +        if (SDL_RWread(src, &block, sizeof (block), 1) != 1)
   1.139 +            return 1;  /* assume that's the end of the file. */
   1.140 +
   1.141 +        if (block == VOC_TERM)
   1.142 +            return 1;
   1.143 +
   1.144 +        if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1)
   1.145 +            return 1;  /* assume that's the end of the file. */
   1.146 +        
   1.147 +        /* Size is an 24-bit value. Ugh. */
   1.148 +        sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) );
   1.149 +
   1.150 +        switch(block)
   1.151 +        {
   1.152 +            case VOC_DATA:
   1.153 +                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
   1.154 +                    return 0;
   1.155 +
   1.156 +                /* When DATA block preceeded by an EXTENDED     */
   1.157 +                /* block, the DATA blocks rate value is invalid */
   1.158 +                if (!v->extended)
   1.159 +                {
   1.160 +                    if (uc == 0)
   1.161 +                    {
   1.162 +                        SDL_SetError("VOC Sample rate is zero?");
   1.163 +                        return 0;
   1.164 +                    }
   1.165 +
   1.166 +                    if ((v->rate != -1) && (uc != v->rate))
   1.167 +                    {
   1.168 +                        SDL_SetError("VOC sample rate codes differ");
   1.169 +                        return 0;
   1.170 +                    }
   1.171 +
   1.172 +                    v->rate = uc;
   1.173 +                    spec->freq = 1000000.0/(256 - v->rate);
   1.174 +                    v->channels = 1;
   1.175 +                }
   1.176 +
   1.177 +                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
   1.178 +                    return 0;
   1.179 +
   1.180 +                if (uc != 0)
   1.181 +                {
   1.182 +                    SDL_SetError("VOC decoder only interprets 8-bit data");
   1.183 +                    return 0;
   1.184 +                }
   1.185 +
   1.186 +                v->extended = 0;
   1.187 +                v->rest = sblen - 2;
   1.188 +                v->size = ST_SIZE_BYTE;
   1.189 +                return 1;
   1.190 +
   1.191 +            case VOC_DATA_16:
   1.192 +                if (SDL_RWread(src, &new_rate_long, sizeof (new_rate_long), 1) != 1)
   1.193 +                    return 0;
   1.194 +                new_rate_long = SDL_SwapLE32(new_rate_long);
   1.195 +                if (new_rate_long == 0)
   1.196 +                {
   1.197 +                    SDL_SetError("VOC Sample rate is zero?");
   1.198 +                    return 0;
   1.199 +                }
   1.200 +                if ((v->rate != -1) && (new_rate_long != v->rate))
   1.201 +                {
   1.202 +                    SDL_SetError("VOC sample rate codes differ");
   1.203 +                    return 0;
   1.204 +                }
   1.205 +                v->rate = new_rate_long;
   1.206 +                spec->freq = new_rate_long;
   1.207 +
   1.208 +                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
   1.209 +                    return 0;
   1.210 +
   1.211 +                switch (uc)
   1.212 +                {
   1.213 +                    case 8:  v->size = ST_SIZE_BYTE; break;
   1.214 +                    case 16: v->size = ST_SIZE_WORD; break;
   1.215 +                    default:
   1.216 +                        SDL_SetError("VOC with unknown data size");
   1.217 +                        return 0;
   1.218 +                }
   1.219 +
   1.220 +                if (SDL_RWread(src, &v->channels, sizeof (Uint8), 1) != 1)
   1.221 +                    return 0;
   1.222 +
   1.223 +                if (SDL_RWread(src, trash, sizeof (Uint8), 6) != 6)
   1.224 +                    return 0;
   1.225 +
   1.226 +                v->rest = sblen - 12;
   1.227 +                return 1;
   1.228 +
   1.229 +            case VOC_CONT:
   1.230 +                v->rest = sblen;
   1.231 +                return 1;
   1.232 +
   1.233 +            case VOC_SILENCE:
   1.234 +                if (SDL_RWread(src, &period, sizeof (period), 1) != 1)
   1.235 +                    return 0;
   1.236 +                period = SDL_SwapLE16(period);
   1.237 +
   1.238 +                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
   1.239 +                    return 0;
   1.240 +                if (uc == 0)
   1.241 +                {
   1.242 +                    SDL_SetError("VOC silence sample rate is zero");
   1.243 +                    return 0;
   1.244 +                }
   1.245 +
   1.246 +                /*
   1.247 +                 * Some silence-packed files have gratuitously
   1.248 +                 * different sample rate codes in silence.
   1.249 +                 * Adjust period.
   1.250 +                 */
   1.251 +                if ((v->rate != -1) && (uc != v->rate))
   1.252 +                    period = (period * (256 - uc))/(256 - v->rate);
   1.253 +                else
   1.254 +                    v->rate = uc;
   1.255 +                v->rest = period;
   1.256 +                v->silent = 1;
   1.257 +                return 1;
   1.258 +
   1.259 +            case VOC_LOOP:
   1.260 +            case VOC_LOOPEND:
   1.261 +                for(i = 0; i < sblen; i++)   /* skip repeat loops. */
   1.262 +                {
   1.263 +                    if (SDL_RWread(src, trash, sizeof (Uint8), 1) != 1)
   1.264 +                        return 0;
   1.265 +                }
   1.266 +                break;
   1.267 +
   1.268 +            case VOC_EXTENDED:
   1.269 +                /* An Extended block is followed by a data block */
   1.270 +                /* Set this byte so we know to use the rate      */
   1.271 +                /* value from the extended block and not the     */
   1.272 +                /* data block.                     */
   1.273 +                v->extended = 1;
   1.274 +                if (SDL_RWread(src, &new_rate_short, sizeof (new_rate_short), 1) != 1)
   1.275 +                    return 0;
   1.276 +                new_rate_short = SDL_SwapLE16(new_rate_short);
   1.277 +                if (new_rate_short == 0)
   1.278 +                {
   1.279 +                   SDL_SetError("VOC sample rate is zero");
   1.280 +                   return 0;
   1.281 +                }
   1.282 +                if ((v->rate != -1) && (new_rate_short != v->rate))
   1.283 +                {
   1.284 +                   SDL_SetError("VOC sample rate codes differ");
   1.285 +                   return 0;
   1.286 +                }
   1.287 +                v->rate = new_rate_short;
   1.288 +
   1.289 +                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
   1.290 +                    return 0;
   1.291 +
   1.292 +                if (uc != 0)
   1.293 +                {
   1.294 +                    SDL_SetError("VOC decoder only interprets 8-bit data");
   1.295 +                    return 0;
   1.296 +                }
   1.297 +
   1.298 +                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
   1.299 +                    return 0;
   1.300 +
   1.301 +                if (uc)
   1.302 +                    spec->channels = 2;  /* Stereo */
   1.303 +                /* Needed number of channels before finishing
   1.304 +                   compute for rate */
   1.305 +                spec->freq = (256000000L/(65536L - v->rate))/spec->channels;
   1.306 +                /* An extended block must be followed by a data */
   1.307 +                /* block to be valid so loop back to top so it  */
   1.308 +                /* can be grabed.                */
   1.309 +                continue;
   1.310 +
   1.311 +            case VOC_MARKER:
   1.312 +                if (SDL_RWread(src, trash, sizeof (Uint8), 2) != 2)
   1.313 +                    return 0;
   1.314 +
   1.315 +                /* Falling! Falling! */
   1.316 +
   1.317 +            default:  /* text block or other krapola. */
   1.318 +                for(i = 0; i < sblen; i++)
   1.319 +                {
   1.320 +                    if (SDL_RWread(src, &trash, sizeof (Uint8), 1) != 1)
   1.321 +                        return 0;
   1.322 +                }
   1.323 +
   1.324 +                if (block == VOC_TEXT)
   1.325 +                    continue;    /* get next block */
   1.326 +        }
   1.327 +    }
   1.328 +
   1.329 +    return 1;
   1.330 +}
   1.331 +
   1.332 +
   1.333 +static int voc_read(SDL_RWops *src, vs_t *v, Uint8 *buf, SDL_AudioSpec *spec)
   1.334 +{
   1.335 +    int done = 0;
   1.336 +    Uint8 silence = 0x80;
   1.337 +
   1.338 +    if (v->rest == 0)
   1.339 +    {
   1.340 +        if (!voc_get_block(src, v, spec))
   1.341 +            return 0;
   1.342 +    }
   1.343 +
   1.344 +    if (v->rest == 0)
   1.345 +        return 0;
   1.346 +
   1.347 +    if (v->silent)
   1.348 +    {
   1.349 +        if (v->size == ST_SIZE_WORD)
   1.350 +            silence = 0x00;
   1.351 +
   1.352 +        /* Fill in silence */
   1.353 +        memset(buf, silence, v->rest);
   1.354 +        done = v->rest;
   1.355 +        v->rest = 0;
   1.356 +    }
   1.357 +
   1.358 +    else
   1.359 +    {
   1.360 +        done = SDL_RWread(src, buf, 1, v->rest);
   1.361 +        v->rest -= done;
   1.362 +        if (v->size == ST_SIZE_WORD)
   1.363 +        {
   1.364 +            done >>= 1;
   1.365 +            #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
   1.366 +                for (; v->rest > 0; v->rest -= 2)
   1.367 +                {
   1.368 +                    *((Uint16 *) buf) = SDL_SwapLE16(*((Uint16 *) buf));
   1.369 +                    ((Uint16 *) buf)++;
   1.370 +                }
   1.371 +            #endif
   1.372 +        }
   1.373 +    }
   1.374 +
   1.375 +    return done;
   1.376 +} /* voc_read */
   1.377 +
   1.378 +
   1.379 +/* don't call this directly; use Mix_LoadWAV_RW() for now. */
   1.380 +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
   1.381 +        SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
   1.382 +{
   1.383 +    vs_t v;
   1.384 +    int was_error = 1;
   1.385 +    int samplesize;
   1.386 +    Uint8 *fillptr;
   1.387 +    void *ptr;
   1.388 +
   1.389 +    if ( (!src) || (!audio_buf) || (!audio_len) )   /* sanity checks. */
   1.390 +        goto done;
   1.391 +
   1.392 +    if ( !voc_check_header(src) )
   1.393 +        goto done;
   1.394 +
   1.395 +    v.rate = -1;
   1.396 +    v.rest = 0;
   1.397 +    v.extended = 0;
   1.398 +    *audio_buf = NULL;
   1.399 +    *audio_len = 0;
   1.400 +    memset(spec, '\0', sizeof (SDL_AudioSpec));
   1.401 +
   1.402 +    if (!voc_get_block(src, &v, spec))
   1.403 +        goto done;
   1.404 +
   1.405 +    if (v.rate == -1)
   1.406 +    {
   1.407 +        SDL_SetError("VOC data had no sound!");
   1.408 +        goto done;
   1.409 +    }
   1.410 +
   1.411 +    spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8);
   1.412 +    if (spec->channels == 0)
   1.413 +        spec->channels = v.channels;
   1.414 +
   1.415 +    *audio_len = v.rest;
   1.416 +    *audio_buf = malloc(v.rest);
   1.417 +    if (*audio_buf == NULL)
   1.418 +        goto done;
   1.419 +
   1.420 +    fillptr = *audio_buf;
   1.421 +
   1.422 +    while (voc_read(src, &v, fillptr, spec) > 0)
   1.423 +    {
   1.424 +        if (!voc_get_block(src, &v, spec))
   1.425 +            goto done;
   1.426 +
   1.427 +        *audio_len += v.rest;
   1.428 +        ptr = realloc(*audio_buf, *audio_len);
   1.429 +        if (ptr == NULL)
   1.430 +        {
   1.431 +            free(*audio_buf);
   1.432 +            *audio_buf = NULL;
   1.433 +            *audio_len = 0;
   1.434 +            goto done;
   1.435 +        }
   1.436 +
   1.437 +        *audio_buf = ptr;
   1.438 +        fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest);
   1.439 +    }
   1.440 +
   1.441 +    spec->samples = (*audio_len / v.size);
   1.442 +
   1.443 +    was_error = 0;  /* success, baby! */
   1.444 +
   1.445 +    /* Don't return a buffer that isn't a multiple of samplesize */
   1.446 +    samplesize = ((spec->format & 0xFF)/8)*spec->channels;
   1.447 +    *audio_len &= ~(samplesize-1);
   1.448 +
   1.449 +done:
   1.450 +    if (src)
   1.451 +    {
   1.452 +        if (freesrc)
   1.453 +            SDL_RWclose(src);
   1.454 +        else
   1.455 +            SDL_RWseek(src, 0, SEEK_SET);
   1.456 +    }
   1.457 +
   1.458 +    if ( was_error )
   1.459 +        spec = NULL;
   1.460 +
   1.461 +    return(spec);
   1.462 +} /* Mix_LoadVOC_RW */
   1.463 +
   1.464 +#else
   1.465 +
   1.466 +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
   1.467 +        SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
   1.468 +{
   1.469 +    SDL_SetError("VOC file loading not supported");
   1.470 +    return(NULL);
   1.471 +}
   1.472 +
   1.473 +#endif /* VOC_SAMPLES */
   1.474 +
   1.475 +/* end of load_voc.c ... */