load_voc.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 12 Nov 2018 16:54:24 -0800
changeset 925 5945988b4a41
parent 848 3907db698eb5
child 926 d6c9518fb5ee
permissions -rw-r--r--
Fixed bug 4371 - tvOS Simulator devices not listed

Caleb Cornett

In the Xcode-iOS project, when selecting the libSDL_mixer-tvOS target, no tvOS simulators appear in the available device dropdown.

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