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