load_voc.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 17 Dec 2001 05:03:10 +0000
changeset 144 11a2e00a16c4
parent 142 59564a17ae18
child 180 1f1da1e35e4d
permissions -rw-r--r--
*** empty log message ***
slouken@111
     1
/*
slouken@111
     2
    SDL_mixer:  An audio mixer library based on the SDL library
slouken@138
     3
    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
slouken@111
     4
slouken@111
     5
    This library is free software; you can redistribute it and/or
slouken@111
     6
    modify it under the terms of the GNU Library General Public
slouken@111
     7
    License as published by the Free Software Foundation; either
slouken@111
     8
    version 2 of the License, or (at your option) any later version.
slouken@111
     9
slouken@111
    10
    This library is distributed in the hope that it will be useful,
slouken@111
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@111
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@111
    13
    Library General Public License for more details.
slouken@111
    14
slouken@111
    15
    You should have received a copy of the GNU Library General Public
slouken@111
    16
    License along with this library; if not, write to the Free
slouken@111
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@111
    18
slouken@111
    19
    This is the source needed to decode a Creative Labs VOC file into a
slouken@111
    20
    waveform. It's pretty straightforward once you get going. The only
slouken@111
    21
    externally-callable function is Mix_LoadVOC_RW(), which is meant to
slouken@111
    22
    act as identically to SDL_LoadWAV_RW() as possible.
slouken@111
    23
slouken@111
    24
    This file by Ryan C. Gordon (icculus@linuxgames.com).
slouken@111
    25
slouken@111
    26
    Heavily borrowed from sox v12.17.1's voc.c.
slouken@111
    27
        (http://www.freshmeat.net/projects/sox/)
slouken@111
    28
*/
slouken@111
    29
slouken@111
    30
/* $Id$ */
slouken@111
    31
slouken@111
    32
#include <stdio.h>
slouken@111
    33
#include <stdlib.h>
slouken@111
    34
#include <string.h>
slouken@111
    35
slouken@111
    36
#include "SDL_mutex.h"
slouken@111
    37
#include "SDL_endian.h"
slouken@111
    38
#include "SDL_timer.h"
slouken@111
    39
slouken@111
    40
#include "SDL_mixer.h"
slouken@111
    41
#include "load_voc.h"
slouken@111
    42
slouken@111
    43
/* Private data for VOC file */
slouken@111
    44
typedef struct vocstuff {
slouken@111
    45
	Uint32	rest;			/* bytes remaining in current block */
slouken@111
    46
	Uint32	rate;			/* rate code (byte) of this chunk */
slouken@111
    47
	int 	silent;		/* sound or silence? */
slouken@111
    48
	Uint32	srate;			/* rate code (byte) of silence */
slouken@111
    49
	Uint32	blockseek;		/* start of current output block */
slouken@111
    50
	Uint32	samples;		/* number of samples output */
slouken@111
    51
	Uint32	size;		/* word length of data */
slouken@116
    52
	Uint8 	channels;	/* number of sound channels */
slouken@144
    53
	int     has_extended;       /* Has an extended block been read? */
slouken@111
    54
} vs_t;
slouken@111
    55
slouken@111
    56
/* Size field */ 
slouken@111
    57
/* SJB: note that the 1st 3 are sometimes used as sizeof(type) */
slouken@111
    58
#define	ST_SIZE_BYTE	1
slouken@111
    59
#define ST_SIZE_8BIT	1
slouken@111
    60
#define	ST_SIZE_WORD	2
slouken@111
    61
#define ST_SIZE_16BIT	2
slouken@111
    62
#define	ST_SIZE_DWORD	4
slouken@111
    63
#define ST_SIZE_32BIT	4
slouken@111
    64
#define	ST_SIZE_FLOAT	5
slouken@111
    65
#define ST_SIZE_DOUBLE	6
slouken@111
    66
#define ST_SIZE_IEEE	7	/* IEEE 80-bit floats. */
slouken@111
    67
slouken@111
    68
/* Style field */
slouken@111
    69
#define ST_ENCODING_UNSIGNED	1 /* unsigned linear: Sound Blaster */
slouken@111
    70
#define ST_ENCODING_SIGN2	2 /* signed linear 2's comp: Mac */
slouken@111
    71
#define	ST_ENCODING_ULAW	3 /* U-law signed logs: US telephony, SPARC */
slouken@111
    72
#define ST_ENCODING_ALAW	4 /* A-law signed logs: non-US telephony */
slouken@111
    73
#define ST_ENCODING_ADPCM	5 /* Compressed PCM */
slouken@111
    74
#define ST_ENCODING_IMA_ADPCM	6 /* Compressed PCM */
slouken@111
    75
#define ST_ENCODING_GSM		7 /* GSM 6.10 33-byte frame lossy compression */
slouken@111
    76
slouken@111
    77
#define	VOC_TERM	0
slouken@111
    78
#define	VOC_DATA	1
slouken@111
    79
#define	VOC_CONT	2
slouken@111
    80
#define	VOC_SILENCE	3
slouken@111
    81
#define	VOC_MARKER	4
slouken@111
    82
#define	VOC_TEXT	5
slouken@111
    83
#define	VOC_LOOP	6
slouken@111
    84
#define	VOC_LOOPEND	7
slouken@111
    85
#define VOC_EXTENDED    8
slouken@111
    86
#define VOC_DATA_16	9
slouken@111
    87
slouken@111
    88
slouken@114
    89
static int voc_check_header(SDL_RWops *src)
slouken@111
    90
{
slouken@111
    91
    /* VOC magic header */
slouken@111
    92
    Uint8  signature[20];  /* "Creative Voice File\032" */
slouken@111
    93
    Uint16 datablockofs;
slouken@111
    94
slouken@111
    95
    SDL_RWseek(src, 0, SEEK_SET);
slouken@111
    96
slouken@111
    97
    if (SDL_RWread(src, signature, sizeof (signature), 1) != 1)
slouken@111
    98
        return(0);
slouken@111
    99
slouken@111
   100
    if (memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0) {
slouken@111
   101
        SDL_SetError("Unrecognized file type (not VOC)");
slouken@111
   102
        return(0);
slouken@111
   103
    }
slouken@111
   104
slouken@111
   105
        /* get the offset where the first datablock is located */
slouken@111
   106
    if (SDL_RWread(src, &datablockofs, sizeof (Uint16), 1) != 1)
slouken@111
   107
        return(0);
slouken@111
   108
slouken@111
   109
    datablockofs = SDL_SwapLE16(datablockofs);
slouken@111
   110
slouken@111
   111
    if (SDL_RWseek(src, datablockofs, SEEK_SET) != datablockofs)
slouken@111
   112
        return(0);
slouken@111
   113
slouken@111
   114
    return(1);  /* success! */
slouken@111
   115
} /* voc_check_header */
slouken@111
   116
slouken@111
   117
slouken@111
   118
/* Read next block header, save info, leave position at start of data */
slouken@111
   119
static int voc_get_block(SDL_RWops *src, vs_t *v, SDL_AudioSpec *spec)
slouken@111
   120
{
slouken@111
   121
    Uint8 bits24[3];
slouken@111
   122
    Uint8 uc, block;
slouken@111
   123
    Uint32 sblen;
slouken@111
   124
    Uint16 new_rate_short;
slouken@111
   125
    Uint32 new_rate_long;
slouken@111
   126
    Uint8 trash[6];
slouken@111
   127
    Uint16 period;
slouken@114
   128
    unsigned int i;
slouken@111
   129
slouken@111
   130
    v->silent = 0;
slouken@111
   131
    while (v->rest == 0)
slouken@111
   132
    {
slouken@111
   133
        if (SDL_RWread(src, &block, sizeof (block), 1) != 1)
slouken@111
   134
            return 1;  /* assume that's the end of the file. */
slouken@111
   135
slouken@111
   136
        if (block == VOC_TERM)
slouken@111
   137
            return 1;
slouken@111
   138
slouken@111
   139
        if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1)
slouken@111
   140
            return 1;  /* assume that's the end of the file. */
slouken@111
   141
        
slouken@111
   142
        /* Size is an 24-bit value. Ugh. */
slouken@111
   143
        sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) );
slouken@111
   144
slouken@111
   145
        switch(block)
slouken@111
   146
        {
slouken@111
   147
            case VOC_DATA:
slouken@111
   148
                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
slouken@111
   149
                    return 0;
slouken@111
   150
slouken@111
   151
                /* When DATA block preceeded by an EXTENDED     */
slouken@111
   152
                /* block, the DATA blocks rate value is invalid */
slouken@144
   153
                if (!v->has_extended)
slouken@111
   154
                {
slouken@111
   155
                    if (uc == 0)
slouken@111
   156
                    {
slouken@111
   157
                        SDL_SetError("VOC Sample rate is zero?");
slouken@111
   158
                        return 0;
slouken@111
   159
                    }
slouken@111
   160
slouken@111
   161
                    if ((v->rate != -1) && (uc != v->rate))
slouken@111
   162
                    {
slouken@111
   163
                        SDL_SetError("VOC sample rate codes differ");
slouken@111
   164
                        return 0;
slouken@111
   165
                    }
slouken@111
   166
slouken@111
   167
                    v->rate = uc;
slouken@114
   168
                    spec->freq = (Uint16)(1000000.0/(256 - v->rate));
slouken@111
   169
                    v->channels = 1;
slouken@111
   170
                }
slouken@111
   171
slouken@111
   172
                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
slouken@111
   173
                    return 0;
slouken@111
   174
slouken@111
   175
                if (uc != 0)
slouken@111
   176
                {
slouken@111
   177
                    SDL_SetError("VOC decoder only interprets 8-bit data");
slouken@111
   178
                    return 0;
slouken@111
   179
                }
slouken@111
   180
slouken@144
   181
                v->has_extended = 0;
slouken@111
   182
                v->rest = sblen - 2;
slouken@111
   183
                v->size = ST_SIZE_BYTE;
slouken@111
   184
                return 1;
slouken@111
   185
slouken@111
   186
            case VOC_DATA_16:
slouken@111
   187
                if (SDL_RWread(src, &new_rate_long, sizeof (new_rate_long), 1) != 1)
slouken@111
   188
                    return 0;
slouken@111
   189
                new_rate_long = SDL_SwapLE32(new_rate_long);
slouken@111
   190
                if (new_rate_long == 0)
slouken@111
   191
                {
slouken@111
   192
                    SDL_SetError("VOC Sample rate is zero?");
slouken@111
   193
                    return 0;
slouken@111
   194
                }
slouken@111
   195
                if ((v->rate != -1) && (new_rate_long != v->rate))
slouken@111
   196
                {
slouken@111
   197
                    SDL_SetError("VOC sample rate codes differ");
slouken@111
   198
                    return 0;
slouken@111
   199
                }
slouken@111
   200
                v->rate = new_rate_long;
slouken@111
   201
                spec->freq = new_rate_long;
slouken@111
   202
slouken@111
   203
                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
slouken@111
   204
                    return 0;
slouken@111
   205
slouken@111
   206
                switch (uc)
slouken@111
   207
                {
slouken@111
   208
                    case 8:  v->size = ST_SIZE_BYTE; break;
slouken@111
   209
                    case 16: v->size = ST_SIZE_WORD; break;
slouken@111
   210
                    default:
slouken@111
   211
                        SDL_SetError("VOC with unknown data size");
slouken@111
   212
                        return 0;
slouken@111
   213
                }
slouken@111
   214
slouken@111
   215
                if (SDL_RWread(src, &v->channels, sizeof (Uint8), 1) != 1)
slouken@111
   216
                    return 0;
slouken@111
   217
slouken@111
   218
                if (SDL_RWread(src, trash, sizeof (Uint8), 6) != 6)
slouken@111
   219
                    return 0;
slouken@111
   220
slouken@111
   221
                v->rest = sblen - 12;
slouken@111
   222
                return 1;
slouken@111
   223
slouken@111
   224
            case VOC_CONT:
slouken@111
   225
                v->rest = sblen;
slouken@111
   226
                return 1;
slouken@111
   227
slouken@111
   228
            case VOC_SILENCE:
slouken@111
   229
                if (SDL_RWread(src, &period, sizeof (period), 1) != 1)
slouken@111
   230
                    return 0;
slouken@111
   231
                period = SDL_SwapLE16(period);
slouken@111
   232
slouken@111
   233
                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
slouken@111
   234
                    return 0;
slouken@111
   235
                if (uc == 0)
slouken@111
   236
                {
slouken@111
   237
                    SDL_SetError("VOC silence sample rate is zero");
slouken@111
   238
                    return 0;
slouken@111
   239
                }
slouken@111
   240
slouken@111
   241
                /*
slouken@111
   242
                 * Some silence-packed files have gratuitously
slouken@111
   243
                 * different sample rate codes in silence.
slouken@111
   244
                 * Adjust period.
slouken@111
   245
                 */
slouken@111
   246
                if ((v->rate != -1) && (uc != v->rate))
slouken@111
   247
                    period = (period * (256 - uc))/(256 - v->rate);
slouken@111
   248
                else
slouken@111
   249
                    v->rate = uc;
slouken@111
   250
                v->rest = period;
slouken@111
   251
                v->silent = 1;
slouken@111
   252
                return 1;
slouken@111
   253
slouken@111
   254
            case VOC_LOOP:
slouken@111
   255
            case VOC_LOOPEND:
slouken@111
   256
                for(i = 0; i < sblen; i++)   /* skip repeat loops. */
slouken@111
   257
                {
slouken@111
   258
                    if (SDL_RWread(src, trash, sizeof (Uint8), 1) != 1)
slouken@111
   259
                        return 0;
slouken@111
   260
                }
slouken@111
   261
                break;
slouken@111
   262
slouken@111
   263
            case VOC_EXTENDED:
slouken@111
   264
                /* An Extended block is followed by a data block */
slouken@111
   265
                /* Set this byte so we know to use the rate      */
slouken@111
   266
                /* value from the extended block and not the     */
slouken@111
   267
                /* data block.                     */
slouken@144
   268
                v->has_extended = 1;
slouken@111
   269
                if (SDL_RWread(src, &new_rate_short, sizeof (new_rate_short), 1) != 1)
slouken@111
   270
                    return 0;
slouken@111
   271
                new_rate_short = SDL_SwapLE16(new_rate_short);
slouken@111
   272
                if (new_rate_short == 0)
slouken@111
   273
                {
slouken@111
   274
                   SDL_SetError("VOC sample rate is zero");
slouken@111
   275
                   return 0;
slouken@111
   276
                }
slouken@111
   277
                if ((v->rate != -1) && (new_rate_short != v->rate))
slouken@111
   278
                {
slouken@111
   279
                   SDL_SetError("VOC sample rate codes differ");
slouken@111
   280
                   return 0;
slouken@111
   281
                }
slouken@111
   282
                v->rate = new_rate_short;
slouken@111
   283
slouken@111
   284
                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
slouken@111
   285
                    return 0;
slouken@111
   286
slouken@111
   287
                if (uc != 0)
slouken@111
   288
                {
slouken@111
   289
                    SDL_SetError("VOC decoder only interprets 8-bit data");
slouken@111
   290
                    return 0;
slouken@111
   291
                }
slouken@111
   292
slouken@111
   293
                if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
slouken@111
   294
                    return 0;
slouken@111
   295
slouken@111
   296
                if (uc)
slouken@111
   297
                    spec->channels = 2;  /* Stereo */
slouken@111
   298
                /* Needed number of channels before finishing
slouken@111
   299
                   compute for rate */
slouken@111
   300
                spec->freq = (256000000L/(65536L - v->rate))/spec->channels;
slouken@111
   301
                /* An extended block must be followed by a data */
slouken@111
   302
                /* block to be valid so loop back to top so it  */
slouken@111
   303
                /* can be grabed.                */
slouken@111
   304
                continue;
slouken@111
   305
slouken@111
   306
            case VOC_MARKER:
slouken@111
   307
                if (SDL_RWread(src, trash, sizeof (Uint8), 2) != 2)
slouken@111
   308
                    return 0;
slouken@111
   309
slouken@111
   310
                /* Falling! Falling! */
slouken@111
   311
slouken@111
   312
            default:  /* text block or other krapola. */
slouken@111
   313
                for(i = 0; i < sblen; i++)
slouken@111
   314
                {
slouken@111
   315
                    if (SDL_RWread(src, &trash, sizeof (Uint8), 1) != 1)
slouken@111
   316
                        return 0;
slouken@111
   317
                }
slouken@111
   318
slouken@111
   319
                if (block == VOC_TEXT)
slouken@111
   320
                    continue;    /* get next block */
slouken@111
   321
        }
slouken@111
   322
    }
slouken@111
   323
slouken@111
   324
    return 1;
slouken@111
   325
}
slouken@111
   326
slouken@111
   327
slouken@111
   328
static int voc_read(SDL_RWops *src, vs_t *v, Uint8 *buf, SDL_AudioSpec *spec)
slouken@111
   329
{
slouken@111
   330
    int done = 0;
slouken@111
   331
    Uint8 silence = 0x80;
slouken@111
   332
slouken@111
   333
    if (v->rest == 0)
slouken@111
   334
    {
slouken@111
   335
        if (!voc_get_block(src, v, spec))
slouken@111
   336
            return 0;
slouken@111
   337
    }
slouken@111
   338
slouken@111
   339
    if (v->rest == 0)
slouken@111
   340
        return 0;
slouken@111
   341
slouken@111
   342
    if (v->silent)
slouken@111
   343
    {
slouken@111
   344
        if (v->size == ST_SIZE_WORD)
slouken@111
   345
            silence = 0x00;
slouken@111
   346
slouken@111
   347
        /* Fill in silence */
slouken@111
   348
        memset(buf, silence, v->rest);
slouken@111
   349
        done = v->rest;
slouken@111
   350
        v->rest = 0;
slouken@111
   351
    }
slouken@111
   352
slouken@111
   353
    else
slouken@111
   354
    {
slouken@111
   355
        done = SDL_RWread(src, buf, 1, v->rest);
slouken@111
   356
        v->rest -= done;
slouken@111
   357
        if (v->size == ST_SIZE_WORD)
slouken@111
   358
        {
slouken@111
   359
            done >>= 1;
slouken@111
   360
            #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
slouken@111
   361
                for (; v->rest > 0; v->rest -= 2)
slouken@111
   362
                {
slouken@111
   363
                    *((Uint16 *) buf) = SDL_SwapLE16(*((Uint16 *) buf));
slouken@111
   364
                    ((Uint16 *) buf)++;
slouken@111
   365
                }
slouken@111
   366
            #endif
slouken@111
   367
        }
slouken@111
   368
    }
slouken@111
   369
slouken@111
   370
    return done;
slouken@111
   371
} /* voc_read */
slouken@111
   372
slouken@111
   373
slouken@111
   374
/* don't call this directly; use Mix_LoadWAV_RW() for now. */
slouken@111
   375
SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
slouken@111
   376
        SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
slouken@111
   377
{
slouken@111
   378
    vs_t v;
slouken@111
   379
    int was_error = 1;
slouken@111
   380
    int samplesize;
slouken@111
   381
    Uint8 *fillptr;
slouken@111
   382
    void *ptr;
slouken@111
   383
slouken@111
   384
    if ( (!src) || (!audio_buf) || (!audio_len) )   /* sanity checks. */
slouken@111
   385
        goto done;
slouken@111
   386
slouken@111
   387
    if ( !voc_check_header(src) )
slouken@111
   388
        goto done;
slouken@111
   389
slouken@111
   390
    v.rate = -1;
slouken@111
   391
    v.rest = 0;
slouken@144
   392
    v.has_extended = 0;
slouken@111
   393
    *audio_buf = NULL;
slouken@111
   394
    *audio_len = 0;
slouken@111
   395
    memset(spec, '\0', sizeof (SDL_AudioSpec));
slouken@111
   396
slouken@111
   397
    if (!voc_get_block(src, &v, spec))
slouken@111
   398
        goto done;
slouken@111
   399
slouken@111
   400
    if (v.rate == -1)
slouken@111
   401
    {
slouken@111
   402
        SDL_SetError("VOC data had no sound!");
slouken@111
   403
        goto done;
slouken@111
   404
    }
slouken@111
   405
slouken@111
   406
    spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8);
slouken@111
   407
    if (spec->channels == 0)
slouken@111
   408
        spec->channels = v.channels;
slouken@111
   409
slouken@111
   410
    *audio_len = v.rest;
slouken@111
   411
    *audio_buf = malloc(v.rest);
slouken@111
   412
    if (*audio_buf == NULL)
slouken@111
   413
        goto done;
slouken@111
   414
slouken@111
   415
    fillptr = *audio_buf;
slouken@111
   416
slouken@111
   417
    while (voc_read(src, &v, fillptr, spec) > 0)
slouken@111
   418
    {
slouken@111
   419
        if (!voc_get_block(src, &v, spec))
slouken@111
   420
            goto done;
slouken@111
   421
slouken@111
   422
        *audio_len += v.rest;
slouken@111
   423
        ptr = realloc(*audio_buf, *audio_len);
slouken@111
   424
        if (ptr == NULL)
slouken@111
   425
        {
slouken@111
   426
            free(*audio_buf);
slouken@111
   427
            *audio_buf = NULL;
slouken@111
   428
            *audio_len = 0;
slouken@111
   429
            goto done;
slouken@111
   430
        }
slouken@111
   431
slouken@111
   432
        *audio_buf = ptr;
slouken@111
   433
        fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest);
slouken@111
   434
    }
slouken@111
   435
slouken@111
   436
    spec->samples = (*audio_len / v.size);
slouken@111
   437
slouken@111
   438
    was_error = 0;  /* success, baby! */
slouken@111
   439
slouken@111
   440
    /* Don't return a buffer that isn't a multiple of samplesize */
slouken@111
   441
    samplesize = ((spec->format & 0xFF)/8)*spec->channels;
slouken@111
   442
    *audio_len &= ~(samplesize-1);
slouken@111
   443
slouken@111
   444
done:
slouken@111
   445
    if (src)
slouken@111
   446
    {
slouken@111
   447
        if (freesrc)
slouken@111
   448
            SDL_RWclose(src);
slouken@111
   449
        else
slouken@111
   450
            SDL_RWseek(src, 0, SEEK_SET);
slouken@111
   451
    }
slouken@111
   452
slouken@111
   453
    if ( was_error )
slouken@111
   454
        spec = NULL;
slouken@111
   455
slouken@111
   456
    return(spec);
slouken@111
   457
} /* Mix_LoadVOC_RW */
slouken@111
   458
slouken@111
   459
/* end of load_voc.c ... */