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