load_voc.c
上传用户:nini_0081
上传日期:2022-07-21
资源大小:2628k
文件大小:13k
- /*
- SDL_mixer: An audio mixer library based on the SDL library
- Copyright (C) 1997-2009 Sam Lantinga
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- This is the source needed to decode a Creative Labs VOC file into a
- waveform. It's pretty straightforward once you get going. The only
- externally-callable function is Mix_LoadVOC_RW(), which is meant to
- act as identically to SDL_LoadWAV_RW() as possible.
- This file by Ryan C. Gordon (icculus@icculus.org).
- Heavily borrowed from sox v12.17.1's voc.c.
- (http://www.freshmeat.net/projects/sox/)
- */
- /* $Id: load_voc.c 5214 2009-11-08 17:11:09Z slouken $ */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "SDL_mutex.h"
- #include "SDL_endian.h"
- #include "SDL_timer.h"
- #include "SDL_mixer.h"
- #include "load_voc.h"
- /* Private data for VOC file */
- typedef struct vocstuff {
- Uint32 rest; /* bytes remaining in current block */
- Uint32 rate; /* rate code (byte) of this chunk */
- int silent; /* sound or silence? */
- Uint32 srate; /* rate code (byte) of silence */
- Uint32 blockseek; /* start of current output block */
- Uint32 samples; /* number of samples output */
- Uint32 size; /* word length of data */
- Uint8 channels; /* number of sound channels */
- int has_extended; /* Has an extended block been read? */
- } vs_t;
- /* Size field */
- /* SJB: note that the 1st 3 are sometimes used as sizeof(type) */
- #define ST_SIZE_BYTE 1
- #define ST_SIZE_8BIT 1
- #define ST_SIZE_WORD 2
- #define ST_SIZE_16BIT 2
- #define ST_SIZE_DWORD 4
- #define ST_SIZE_32BIT 4
- #define ST_SIZE_FLOAT 5
- #define ST_SIZE_DOUBLE 6
- #define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */
- /* Style field */
- #define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */
- #define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */
- #define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */
- #define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */
- #define ST_ENCODING_ADPCM 5 /* Compressed PCM */
- #define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */
- #define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */
- #define VOC_TERM 0
- #define VOC_DATA 1
- #define VOC_CONT 2
- #define VOC_SILENCE 3
- #define VOC_MARKER 4
- #define VOC_TEXT 5
- #define VOC_LOOP 6
- #define VOC_LOOPEND 7
- #define VOC_EXTENDED 8
- #define VOC_DATA_16 9
- static int voc_check_header(SDL_RWops *src)
- {
- /* VOC magic header */
- Uint8 signature[20]; /* "Creative Voice File 32" */
- Uint16 datablockofs;
- SDL_RWseek(src, 0, RW_SEEK_SET);
- if (SDL_RWread(src, signature, sizeof (signature), 1) != 1)
- return(0);
- if (memcmp(signature, "Creative Voice File 32", sizeof (signature)) != 0) {
- SDL_SetError("Unrecognized file type (not VOC)");
- return(0);
- }
- /* get the offset where the first datablock is located */
- if (SDL_RWread(src, &datablockofs, sizeof (Uint16), 1) != 1)
- return(0);
- datablockofs = SDL_SwapLE16(datablockofs);
- if (SDL_RWseek(src, datablockofs, RW_SEEK_SET) != datablockofs)
- return(0);
- return(1); /* success! */
- } /* voc_check_header */
- /* Read next block header, save info, leave position at start of data */
- static int voc_get_block(SDL_RWops *src, vs_t *v, SDL_AudioSpec *spec)
- {
- Uint8 bits24[3];
- Uint8 uc, block;
- Uint32 sblen;
- Uint16 new_rate_short;
- Uint32 new_rate_long;
- Uint8 trash[6];
- Uint16 period;
- unsigned int i;
- v->silent = 0;
- while (v->rest == 0)
- {
- if (SDL_RWread(src, &block, sizeof (block), 1) != 1)
- return 1; /* assume that's the end of the file. */
- if (block == VOC_TERM)
- return 1;
- if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1)
- return 1; /* assume that's the end of the file. */
-
- /* Size is an 24-bit value. Ugh. */
- sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) );
- switch(block)
- {
- case VOC_DATA:
- if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
- return 0;
- /* When DATA block preceeded by an EXTENDED */
- /* block, the DATA blocks rate value is invalid */
- if (!v->has_extended)
- {
- if (uc == 0)
- {
- SDL_SetError("VOC Sample rate is zero?");
- return 0;
- }
- if ((v->rate != -1) && (uc != v->rate))
- {
- SDL_SetError("VOC sample rate codes differ");
- return 0;
- }
- v->rate = uc;
- spec->freq = (Uint16)(1000000.0/(256 - v->rate));
- v->channels = 1;
- }
- if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
- return 0;
- if (uc != 0)
- {
- SDL_SetError("VOC decoder only interprets 8-bit data");
- return 0;
- }
- v->has_extended = 0;
- v->rest = sblen - 2;
- v->size = ST_SIZE_BYTE;
- return 1;
- case VOC_DATA_16:
- if (SDL_RWread(src, &new_rate_long, sizeof (new_rate_long), 1) != 1)
- return 0;
- new_rate_long = SDL_SwapLE32(new_rate_long);
- if (new_rate_long == 0)
- {
- SDL_SetError("VOC Sample rate is zero?");
- return 0;
- }
- if ((v->rate != -1) && (new_rate_long != v->rate))
- {
- SDL_SetError("VOC sample rate codes differ");
- return 0;
- }
- v->rate = new_rate_long;
- spec->freq = new_rate_long;
- if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
- return 0;
- switch (uc)
- {
- case 8: v->size = ST_SIZE_BYTE; break;
- case 16: v->size = ST_SIZE_WORD; break;
- default:
- SDL_SetError("VOC with unknown data size");
- return 0;
- }
- if (SDL_RWread(src, &v->channels, sizeof (Uint8), 1) != 1)
- return 0;
- if (SDL_RWread(src, trash, sizeof (Uint8), 6) != 6)
- return 0;
- v->rest = sblen - 12;
- return 1;
- case VOC_CONT:
- v->rest = sblen;
- return 1;
- case VOC_SILENCE:
- if (SDL_RWread(src, &period, sizeof (period), 1) != 1)
- return 0;
- period = SDL_SwapLE16(period);
- if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
- return 0;
- if (uc == 0)
- {
- SDL_SetError("VOC silence sample rate is zero");
- return 0;
- }
- /*
- * Some silence-packed files have gratuitously
- * different sample rate codes in silence.
- * Adjust period.
- */
- if ((v->rate != -1) && (uc != v->rate))
- period = (Uint16)((period * (256 - uc))/(256 - v->rate));
- else
- v->rate = uc;
- v->rest = period;
- v->silent = 1;
- return 1;
- case VOC_LOOP:
- case VOC_LOOPEND:
- for(i = 0; i < sblen; i++) /* skip repeat loops. */
- {
- if (SDL_RWread(src, trash, sizeof (Uint8), 1) != 1)
- return 0;
- }
- break;
- case VOC_EXTENDED:
- /* An Extended block is followed by a data block */
- /* Set this byte so we know to use the rate */
- /* value from the extended block and not the */
- /* data block. */
- v->has_extended = 1;
- if (SDL_RWread(src, &new_rate_short, sizeof (new_rate_short), 1) != 1)
- return 0;
- new_rate_short = SDL_SwapLE16(new_rate_short);
- if (new_rate_short == 0)
- {
- SDL_SetError("VOC sample rate is zero");
- return 0;
- }
- if ((v->rate != -1) && (new_rate_short != v->rate))
- {
- SDL_SetError("VOC sample rate codes differ");
- return 0;
- }
- v->rate = new_rate_short;
- if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
- return 0;
- if (uc != 0)
- {
- SDL_SetError("VOC decoder only interprets 8-bit data");
- return 0;
- }
- if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
- return 0;
- if (uc)
- spec->channels = 2; /* Stereo */
- /* Needed number of channels before finishing
- compute for rate */
- spec->freq = (256000000L/(65536L - v->rate))/spec->channels;
- /* An extended block must be followed by a data */
- /* block to be valid so loop back to top so it */
- /* can be grabed. */
- continue;
- case VOC_MARKER:
- if (SDL_RWread(src, trash, sizeof (Uint8), 2) != 2)
- return 0;
- /* Falling! Falling! */
- default: /* text block or other krapola. */
- for(i = 0; i < sblen; i++)
- {
- if (SDL_RWread(src, &trash, sizeof (Uint8), 1) != 1)
- return 0;
- }
- if (block == VOC_TEXT)
- continue; /* get next block */
- }
- }
- return 1;
- }
- static int voc_read(SDL_RWops *src, vs_t *v, Uint8 *buf, SDL_AudioSpec *spec)
- {
- int done = 0;
- Uint8 silence = 0x80;
- if (v->rest == 0)
- {
- if (!voc_get_block(src, v, spec))
- return 0;
- }
- if (v->rest == 0)
- return 0;
- if (v->silent)
- {
- if (v->size == ST_SIZE_WORD)
- silence = 0x00;
- /* Fill in silence */
- memset(buf, silence, v->rest);
- done = v->rest;
- v->rest = 0;
- }
- else
- {
- done = SDL_RWread(src, buf, 1, v->rest);
- v->rest -= done;
- if (v->size == ST_SIZE_WORD)
- {
- #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
- Uint16 *samples = (Uint16 *)buf;
- for (; v->rest > 0; v->rest -= 2)
- {
- *samples = SDL_SwapLE16(*samples);
- samples++;
- }
- #endif
- done >>= 1;
- }
- }
- return done;
- } /* voc_read */
- /* don't call this directly; use Mix_LoadWAV_RW() for now. */
- SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc,
- SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
- {
- vs_t v;
- int was_error = 1;
- int samplesize;
- Uint8 *fillptr;
- void *ptr;
- if ( (!src) || (!audio_buf) || (!audio_len) ) /* sanity checks. */
- goto done;
- if ( !voc_check_header(src) )
- goto done;
- v.rate = -1;
- v.rest = 0;
- v.has_extended = 0;
- *audio_buf = NULL;
- *audio_len = 0;
- memset(spec, ' ', sizeof (SDL_AudioSpec));
- if (!voc_get_block(src, &v, spec))
- goto done;
- if (v.rate == -1)
- {
- SDL_SetError("VOC data had no sound!");
- goto done;
- }
- spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8);
- if (spec->channels == 0)
- spec->channels = v.channels;
- *audio_len = v.rest;
- *audio_buf = malloc(v.rest);
- if (*audio_buf == NULL)
- goto done;
- fillptr = *audio_buf;
- while (voc_read(src, &v, fillptr, spec) > 0)
- {
- if (!voc_get_block(src, &v, spec))
- goto done;
- *audio_len += v.rest;
- ptr = realloc(*audio_buf, *audio_len);
- if (ptr == NULL)
- {
- free(*audio_buf);
- *audio_buf = NULL;
- *audio_len = 0;
- goto done;
- }
- *audio_buf = ptr;
- fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest);
- }
- spec->samples = (Uint16)(*audio_len / v.size);
- was_error = 0; /* success, baby! */
- /* Don't return a buffer that isn't a multiple of samplesize */
- samplesize = ((spec->format & 0xFF)/8)*spec->channels;
- *audio_len &= ~(samplesize-1);
- done:
- if (src)
- {
- if (freesrc)
- SDL_RWclose(src);
- else
- SDL_RWseek(src, 0, RW_SEEK_SET);
- }
- if ( was_error )
- spec = NULL;
- return(spec);
- } /* Mix_LoadVOC_RW */
- /* end of load_voc.c ... */