load_voc.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Dec 2001 12:56:55 +0000
changeset 138 4d0dc6b4985d
parent 116 fa8c35dfe1d1
child 142 59564a17ae18
permissions -rw-r--r--
Updated the copyright information
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  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@linuxgames.com).
    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 #ifdef VOC_SAMPLES
    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     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, 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, 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->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->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 = (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->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             done >>= 1;
   362             #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
   363                 for (; v->rest > 0; v->rest -= 2)
   364                 {
   365                     *((Uint16 *) buf) = SDL_SwapLE16(*((Uint16 *) buf));
   366                     ((Uint16 *) buf)++;
   367                 }
   368             #endif
   369         }
   370     }
   371 
   372     return done;
   373 } /* voc_read */
   374 
   375 
   376 /* don't call this directly; use Mix_LoadWAV_RW() for now. */
   377 SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
   378         SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
   379 {
   380     vs_t v;
   381     int was_error = 1;
   382     int samplesize;
   383     Uint8 *fillptr;
   384     void *ptr;
   385 
   386     if ( (!src) || (!audio_buf) || (!audio_len) )   /* sanity checks. */
   387         goto done;
   388 
   389     if ( !voc_check_header(src) )
   390         goto done;
   391 
   392     v.rate = -1;
   393     v.rest = 0;
   394     v.extended = 0;
   395     *audio_buf = NULL;
   396     *audio_len = 0;
   397     memset(spec, '\0', sizeof (SDL_AudioSpec));
   398 
   399     if (!voc_get_block(src, &v, spec))
   400         goto done;
   401 
   402     if (v.rate == -1)
   403     {
   404         SDL_SetError("VOC data had no sound!");
   405         goto done;
   406     }
   407 
   408     spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8);
   409     if (spec->channels == 0)
   410         spec->channels = v.channels;
   411 
   412     *audio_len = v.rest;
   413     *audio_buf = malloc(v.rest);
   414     if (*audio_buf == NULL)
   415         goto done;
   416 
   417     fillptr = *audio_buf;
   418 
   419     while (voc_read(src, &v, fillptr, spec) > 0)
   420     {
   421         if (!voc_get_block(src, &v, spec))
   422             goto done;
   423 
   424         *audio_len += v.rest;
   425         ptr = realloc(*audio_buf, *audio_len);
   426         if (ptr == NULL)
   427         {
   428             free(*audio_buf);
   429             *audio_buf = NULL;
   430             *audio_len = 0;
   431             goto done;
   432         }
   433 
   434         *audio_buf = ptr;
   435         fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest);
   436     }
   437 
   438     spec->samples = (*audio_len / v.size);
   439 
   440     was_error = 0;  /* success, baby! */
   441 
   442     /* Don't return a buffer that isn't a multiple of samplesize */
   443     samplesize = ((spec->format & 0xFF)/8)*spec->channels;
   444     *audio_len &= ~(samplesize-1);
   445 
   446 done:
   447     if (src)
   448     {
   449         if (freesrc)
   450             SDL_RWclose(src);
   451         else
   452             SDL_RWseek(src, 0, SEEK_SET);
   453     }
   454 
   455     if ( was_error )
   456         spec = NULL;
   457 
   458     return(spec);
   459 } /* Mix_LoadVOC_RW */
   460 
   461 #else
   462 
   463 SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
   464         SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
   465 {
   466     SDL_SetError("VOC file loading not supported");
   467     return(NULL);
   468 }
   469 
   470 #endif /* VOC_SAMPLES */
   471 
   472 /* end of load_voc.c ... */