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