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