load_voc.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 23 Sep 2001 17:34:08 +0000
changeset 116 fa8c35dfe1d1
parent 114 83ab4ef4458b
child 138 4d0dc6b4985d
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@111
     3
    Copyright (C) 1997-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
#ifdef VOC_SAMPLES
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@111
    55
	int     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@111
    97
    SDL_RWseek(src, 0, 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@111
   113
    if (SDL_RWseek(src, datablockofs, 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@111
   155
                if (!v->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@111
   183
                v->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@111
   249
                    period = (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@111
   270
                v->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
            done >>= 1;
slouken@111
   362
            #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
slouken@111
   363
                for (; v->rest > 0; v->rest -= 2)
slouken@111
   364
                {
slouken@111
   365
                    *((Uint16 *) buf) = SDL_SwapLE16(*((Uint16 *) buf));
slouken@111
   366
                    ((Uint16 *) buf)++;
slouken@111
   367
                }
slouken@111
   368
            #endif
slouken@111
   369
        }
slouken@111
   370
    }
slouken@111
   371
slouken@111
   372
    return done;
slouken@111
   373
} /* voc_read */
slouken@111
   374
slouken@111
   375
slouken@111
   376
/* don't call this directly; use Mix_LoadWAV_RW() for now. */
slouken@111
   377
SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
slouken@111
   378
        SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
slouken@111
   379
{
slouken@111
   380
    vs_t v;
slouken@111
   381
    int was_error = 1;
slouken@111
   382
    int samplesize;
slouken@111
   383
    Uint8 *fillptr;
slouken@111
   384
    void *ptr;
slouken@111
   385
slouken@111
   386
    if ( (!src) || (!audio_buf) || (!audio_len) )   /* sanity checks. */
slouken@111
   387
        goto done;
slouken@111
   388
slouken@111
   389
    if ( !voc_check_header(src) )
slouken@111
   390
        goto done;
slouken@111
   391
slouken@111
   392
    v.rate = -1;
slouken@111
   393
    v.rest = 0;
slouken@111
   394
    v.extended = 0;
slouken@111
   395
    *audio_buf = NULL;
slouken@111
   396
    *audio_len = 0;
slouken@111
   397
    memset(spec, '\0', sizeof (SDL_AudioSpec));
slouken@111
   398
slouken@111
   399
    if (!voc_get_block(src, &v, spec))
slouken@111
   400
        goto done;
slouken@111
   401
slouken@111
   402
    if (v.rate == -1)
slouken@111
   403
    {
slouken@111
   404
        SDL_SetError("VOC data had no sound!");
slouken@111
   405
        goto done;
slouken@111
   406
    }
slouken@111
   407
slouken@111
   408
    spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8);
slouken@111
   409
    if (spec->channels == 0)
slouken@111
   410
        spec->channels = v.channels;
slouken@111
   411
slouken@111
   412
    *audio_len = v.rest;
slouken@111
   413
    *audio_buf = malloc(v.rest);
slouken@111
   414
    if (*audio_buf == NULL)
slouken@111
   415
        goto done;
slouken@111
   416
slouken@111
   417
    fillptr = *audio_buf;
slouken@111
   418
slouken@111
   419
    while (voc_read(src, &v, fillptr, spec) > 0)
slouken@111
   420
    {
slouken@111
   421
        if (!voc_get_block(src, &v, spec))
slouken@111
   422
            goto done;
slouken@111
   423
slouken@111
   424
        *audio_len += v.rest;
slouken@111
   425
        ptr = realloc(*audio_buf, *audio_len);
slouken@111
   426
        if (ptr == NULL)
slouken@111
   427
        {
slouken@111
   428
            free(*audio_buf);
slouken@111
   429
            *audio_buf = NULL;
slouken@111
   430
            *audio_len = 0;
slouken@111
   431
            goto done;
slouken@111
   432
        }
slouken@111
   433
slouken@111
   434
        *audio_buf = ptr;
slouken@111
   435
        fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest);
slouken@111
   436
    }
slouken@111
   437
slouken@111
   438
    spec->samples = (*audio_len / v.size);
slouken@111
   439
slouken@111
   440
    was_error = 0;  /* success, baby! */
slouken@111
   441
slouken@111
   442
    /* Don't return a buffer that isn't a multiple of samplesize */
slouken@111
   443
    samplesize = ((spec->format & 0xFF)/8)*spec->channels;
slouken@111
   444
    *audio_len &= ~(samplesize-1);
slouken@111
   445
slouken@111
   446
done:
slouken@111
   447
    if (src)
slouken@111
   448
    {
slouken@111
   449
        if (freesrc)
slouken@111
   450
            SDL_RWclose(src);
slouken@111
   451
        else
slouken@111
   452
            SDL_RWseek(src, 0, SEEK_SET);
slouken@111
   453
    }
slouken@111
   454
slouken@111
   455
    if ( was_error )
slouken@111
   456
        spec = NULL;
slouken@111
   457
slouken@111
   458
    return(spec);
slouken@111
   459
} /* Mix_LoadVOC_RW */
slouken@111
   460
slouken@111
   461
#else
slouken@111
   462
slouken@111
   463
SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
slouken@111
   464
        SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
slouken@111
   465
{
slouken@111
   466
    SDL_SetError("VOC file loading not supported");
slouken@111
   467
    return(NULL);
slouken@111
   468
}
slouken@111
   469
slouken@111
   470
#endif /* VOC_SAMPLES */
slouken@111
   471
slouken@111
   472
/* end of load_voc.c ... */