📄 voc.c
字号:
/*** Copyright (C) 2001-2004 Erik de Castro Lopo <erikd@mega-nerd.com>**** This program is free software; you can redistribute it and/or modify** it under the terms of the GNU Lesser General Public License as published by** the Free Software Foundation; either version 2.1 of the License, or** (at your option) any later version.**** This program 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 Lesser General Public License for more details.**** You should have received a copy of the GNU Lesser General Public License** along with this program; if not, write to the Free Software** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//* RANT:** The VOC file format is the most brain damaged format I have yet had to deal** with. No one programmer could have bee stupid enough to put this together.** Instead it looks like a series of manic, dyslexic assembly language programmers** hacked it to fit their needs.** Utterly woeful.*/#include "config.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include "sndfile.h"#include "sfendian.h"#include "common.h"/*------------------------------------------------------------------------------ * Typedefs for file chunks.*/#define VOC_MAX_SECTIONS 200enum{ VOC_TERMINATOR = 0, VOC_SOUND_DATA = 1, VOC_SOUND_CONTINUE = 2, VOC_SILENCE = 3, VOC_MARKER = 4, VOC_ASCII = 5, VOC_REPEAT = 6, VOC_END_REPEAT = 7, VOC_EXTENDED = 8, VOC_EXTENDED_II = 9} ;typedef struct{ int samples ; int offset ; /* Offset of zero => silence. */} SND_DATA_BLOCKS ;typedef struct{ unsigned int sections, section_types ; int samplerate, channels, bitwidth ; SND_DATA_BLOCKS blocks [VOC_MAX_SECTIONS] ;} VOC_DATA ;/*------------------------------------------------------------------------------ * Private static functions.*/static int voc_close (SF_PRIVATE *psf) ;static int voc_write_header (SF_PRIVATE *psf, int calc_length) ;static int voc_read_header (SF_PRIVATE *psf) ;static const char* voc_encoding2str (int encoding) ;#if 0/* These functions would be required for files with more than one VOC_SOUND_DATA** segment. Not sure whether to bother implementing this.*/static int voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc) ;static int voc_multi_read_uc2s (SF_PRIVATE *psf, short *ptr, int len) ;static int voc_multi_read_les2s (SF_PRIVATE *psf, short *ptr, int len) ;static int voc_multi_read_uc2i (SF_PRIVATE *psf, int *ptr, int len) ;static int voc_multi_read_les2i (SF_PRIVATE *psf, int *ptr, int len) ;static int voc_multi_read_uc2f (SF_PRIVATE *psf, float *ptr, int len) ;static int voc_multi_read_les2f (SF_PRIVATE *psf, float *ptr, int len) ;static int voc_multi_read_uc2d (SF_PRIVATE *psf, double *ptr, int len) ;static int voc_multi_read_les2d (SF_PRIVATE *psf, double *ptr, int len) ;#endif/*------------------------------------------------------------------------------** Public function.*/intvoc_open (SF_PRIVATE *psf){ int subformat, error = 0 ; if (psf->is_pipe) return SFE_VOC_NO_PIPE ; if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) { if ((error = voc_read_header (psf))) return error ; } ; subformat = psf->sf.format & SF_FORMAT_SUBMASK ; if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_VOC) return SFE_BAD_OPEN_FORMAT ; psf->endian = SF_ENDIAN_LITTLE ; if ((error = voc_write_header (psf, SF_FALSE))) return error ; psf->write_header = voc_write_header ; } ; psf->blockwidth = psf->bytewidth * psf->sf.channels ; psf->close = voc_close ; switch (subformat) { case SF_FORMAT_PCM_U8 : case SF_FORMAT_PCM_16 : error = pcm_init (psf) ; break ; case SF_FORMAT_ALAW : error = alaw_init (psf) ; break ; case SF_FORMAT_ULAW : error = ulaw_init (psf) ; break ; default : return SFE_UNIMPLEMENTED ; } ; return error ;} /* voc_open *//*------------------------------------------------------------------------------*/static intvoc_read_header (SF_PRIVATE *psf){ VOC_DATA *pvoc ; char creative [20] ; unsigned char block_type, rate_byte ; short version, checksum, encoding, dataoffset ; int offset ; /* Set position to start of file to begin reading header. */ offset = psf_binheader_readf (psf, "pb", 0, creative, SIGNED_SIZEOF (creative)) ; if (creative [sizeof (creative) - 1] != 0x1A) return SFE_VOC_NO_CREATIVE ; /* Terminate the string. */ creative [sizeof (creative) - 1] = 0 ; if (strcmp ("Creative Voice File", creative)) return SFE_VOC_NO_CREATIVE ; psf_log_printf (psf, "%s\n", creative) ; offset += psf_binheader_readf (psf, "e222", &dataoffset, &version, &checksum) ; psf->dataoffset = dataoffset ; psf_log_printf (psf, "dataoffset : %d\n" "version : 0x%X\n" "checksum : 0x%X\n", psf->dataoffset, version, checksum) ; if (version != 0x010A && version != 0x0114) return SFE_VOC_BAD_VERSION ; if (! (psf->fdata = malloc (sizeof (VOC_DATA)))) return SFE_MALLOC_FAILED ; pvoc = (VOC_DATA*) psf->fdata ; memset (pvoc, 0, sizeof (VOC_DATA)) ; /* Set the default encoding now. */ psf->sf.format = SF_FORMAT_VOC ; /* Major format */ encoding = SF_FORMAT_PCM_U8 ; /* Minor format */ psf->endian = SF_ENDIAN_LITTLE ; while (1) { offset += psf_binheader_readf (psf, "1", &block_type) ; switch (block_type) { case VOC_ASCII : { int size ; offset += psf_binheader_readf (psf, "e3", &size) ; psf_log_printf (psf, " ASCII : %d\n", size) ; offset += psf_binheader_readf (psf, "b", psf->header, size) ; psf->header [size] = 0 ; psf_log_printf (psf, " text : %s\n", psf->header) ; } ; continue ; case VOC_SOUND_DATA : case VOC_EXTENDED : case VOC_EXTENDED_II : break ; default : psf_log_printf (psf, "*** Weird block marker (%d)\n", block_type) ; } ; break ; } ; if (block_type == VOC_SOUND_DATA) { unsigned char compression ; int size ; offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ; psf->sf.samplerate = 1000000 / (256 - (rate_byte & 0xFF)) ; psf_log_printf (psf, " Sound Data : %d\n sr : %d => %dHz\n comp : %d\n", size, rate_byte, psf->sf.samplerate, compression) ; if (offset + size - 1 > psf->filelength) { psf_log_printf (psf, "Seems to be a truncated file.\n") ; psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; return SFE_VOC_BAD_SECTIONS ; } else if (offset + size - 1 < psf->filelength) { psf_log_printf (psf, "Seems to be a multi-segment file (#1).\n") ; psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; return SFE_VOC_BAD_SECTIONS ; } ; psf->dataoffset = offset ; psf->dataend = psf->filelength - 1 ; psf->sf.channels = 1 ; psf->bytewidth = 1 ; psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ; return 0 ; } ; if (block_type == VOC_EXTENDED) { unsigned char pack, stereo, compression ; unsigned short rate_short ; int size ; offset += psf_binheader_readf (psf, "e3211", &size, &rate_short, &pack, &stereo) ; psf_log_printf (psf, " Extended : %d\n", size) ; if (size == 4) psf_log_printf (psf, " size : 4\n") ; else psf_log_printf (psf, " size : %d (should be 4)\n", size) ; psf_log_printf (psf, " pack : %d\n" " stereo : %s\n", pack, (stereo ? "yes" : "no")) ; if (stereo) { psf->sf.channels = 2 ; psf->sf.samplerate = 128000000 / (65536 - rate_short) ; } else { psf->sf.channels = 1 ; psf->sf.samplerate = 256000000 / (65536 - rate_short) ; } ; psf_log_printf (psf, " sr : %d => %dHz\n", (rate_short & 0xFFFF), psf->sf.samplerate) ; offset += psf_binheader_readf (psf, "1", &block_type) ; if (block_type != VOC_SOUND_DATA) { psf_log_printf (psf, "*** Expecting VOC_SOUND_DATA section.\n") ; return SFE_VOC_BAD_FORMAT ; } ; offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ; psf_log_printf (psf, " Sound Data : %d\n" " sr : %d\n" " comp : %d\n", size, rate_byte, compression) ; if (offset + size - 1 > psf->filelength) { psf_log_printf (psf, "Seems to be a truncated file.\n") ; psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; return SFE_VOC_BAD_SECTIONS ; } else if (offset + size - 1 < psf->filelength) { psf_log_printf (psf, "Seems to be a multi-segment file (#2).\n") ; psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; return SFE_VOC_BAD_SECTIONS ; } ; psf->dataoffset = offset ; psf->dataend = psf->filelength - 1 ; psf->bytewidth = 1 ; psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ; return 0 ; } if (block_type == VOC_EXTENDED_II) { unsigned char bitwidth, channels ; int size, fourbytes ; offset += psf_binheader_readf (psf, "e341124", &size, &psf->sf.samplerate, &bitwidth, &channels, &encoding, &fourbytes) ; if (size * 2 == psf->filelength - 39) { int temp_size = psf->filelength - 31 ; psf_log_printf (psf, " Extended II : %d (SoX bug: should be %d)\n", size, temp_size) ; size = temp_size ; } else psf_log_printf (psf, " Extended II : %d\n", size) ; psf_log_printf (psf, " sample rate : %d\n" " bit width : %d\n" " channels : %d\n", psf->sf.samplerate, bitwidth, channels) ; if (bitwidth == 16 && encoding == 0) { encoding = 4 ; psf_log_printf (psf, " encoding : 0 (SoX bug: should be 4 for 16 bit signed PCM)\n") ; } else psf_log_printf (psf, " encoding : %d => %s\n", encoding, voc_encoding2str (encoding)) ; psf_log_printf (psf, " fourbytes : %X\n", fourbytes) ; psf->sf.channels = channels ; psf->dataoffset = offset ; psf->dataend = psf->filelength - 1 ; if (size + 31 == psf->filelength + 1) { /* Hack for reading files produced using ** sf_command (SFC_UPDATE_HEADER_NOW). */ psf_log_printf (psf, "Missing zero byte at end of file.\n") ; size = psf->filelength - 30 ; psf->dataend = 0 ; } else if (size + 31 > psf->filelength) { psf_log_printf (psf, "Seems to be a truncated file.\n") ; size = psf->filelength - 31 ; } else if (size + 31 < psf->filelength) psf_log_printf (psf, "Seems to be a multi-segment file (#3).\n") ; switch (encoding) { case 0 : psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ; psf->bytewidth = 1 ; break ; case 4 : psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_16 ; psf->bytewidth = 2 ; break ; case 6 : psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ALAW ; psf->bytewidth = 1 ; break ; case 7 : psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ULAW ; psf->bytewidth = 1 ; break ; default : /* Unknown */ return SFE_UNKNOWN_FORMAT ; break ; } ; } ; return 0 ;} /* voc_read_header *//*====================================================================================*/static intvoc_write_header (SF_PRIVATE *psf, int calc_length){ sf_count_t current ; int rate_const, subformat ; current = psf_ftell (psf) ; if (calc_length) { psf->filelength = psf_get_filelen (psf) ; psf->datalength = psf->filelength - psf->dataoffset ; if (psf->dataend) psf->datalength -= psf->filelength - psf->dataend ; psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; } ; subformat = psf->sf.format & SF_FORMAT_SUBMASK ; /* Reset the current header length to zero. */ psf->header [0] = 0 ; psf->headindex = 0 ; psf_fseek (psf, 0, SEEK_SET) ; /* VOC marker and 0x1A byte. */ psf_binheader_writef (psf, "eb1", "Creative Voice File", 19, 0x1A) ; /* Data offset, version and other. */ psf_binheader_writef (psf, "e222", 26, 0x0114, 0x111F) ; /* Use same logic as SOX. ** If the file is mono 8 bit data, use VOC_SOUND_DATA. ** If the file is mono 16 bit data, use VOC_EXTENED. ** Otherwise use VOC_EXTENED_2.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -