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