📄 wav.c
字号:
/*** Copyright (C) 1999-2004 Erik de Castro Lopo <erikd@mega-nerd.com>** Copyright (C) 2004 David Viens <davidv@plogue.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.*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <time.h>#include "sndfile.h"#include "config.h"#include "sfendian.h"#include "common.h"#include "wav_w64.h"/*------------------------------------------------------------------------------ * Macros to handle big/little endian issues. */#define RIFF_MARKER (MAKE_MARKER ('R', 'I', 'F', 'F'))#define WAVE_MARKER (MAKE_MARKER ('W', 'A', 'V', 'E'))#define fmt_MARKER (MAKE_MARKER ('f', 'm', 't', ' '))#define data_MARKER (MAKE_MARKER ('d', 'a', 't', 'a'))#define fact_MARKER (MAKE_MARKER ('f', 'a', 'c', 't'))#define PEAK_MARKER (MAKE_MARKER ('P', 'E', 'A', 'K'))#define cue_MARKER (MAKE_MARKER ('c', 'u', 'e', ' '))#define LIST_MARKER (MAKE_MARKER ('L', 'I', 'S', 'T'))#define slnt_MARKER (MAKE_MARKER ('s', 'l', 'n', 't'))#define wavl_MARKER (MAKE_MARKER ('w', 'a', 'v', 'l'))#define INFO_MARKER (MAKE_MARKER ('I', 'N', 'F', 'O'))#define plst_MARKER (MAKE_MARKER ('p', 'l', 's', 't'))#define adtl_MARKER (MAKE_MARKER ('a', 'd', 't', 'l'))#define labl_MARKER (MAKE_MARKER ('l', 'a', 'b', 'l'))#define ltxt_MARKER (MAKE_MARKER ('l', 't', 'x', 't'))#define note_MARKER (MAKE_MARKER ('n', 'o', 't', 'e'))#define smpl_MARKER (MAKE_MARKER ('s', 'm', 'p', 'l'))#define bext_MARKER (MAKE_MARKER ('b', 'e', 'x', 't'))#define MEXT_MARKER (MAKE_MARKER ('M', 'E', 'X', 'T'))#define DISP_MARKER (MAKE_MARKER ('D', 'I', 'S', 'P'))#define acid_MARKER (MAKE_MARKER ('a', 'c', 'i', 'd'))#define strc_MARKER (MAKE_MARKER ('s', 't', 'r', 'c'))#define PAD_MARKER (MAKE_MARKER ('P', 'A', 'D', ' '))#define afsp_MARKER (MAKE_MARKER ('a', 'f', 's', 'p'))#define clm_MARKER (MAKE_MARKER ('c', 'l', 'm', ' '))#define ISFT_MARKER (MAKE_MARKER ('I', 'S', 'F', 'T'))#define ICRD_MARKER (MAKE_MARKER ('I', 'C', 'R', 'D'))#define ICOP_MARKER (MAKE_MARKER ('I', 'C', 'O', 'P'))#define IARL_MARKER (MAKE_MARKER ('I', 'A', 'R', 'L'))#define IART_MARKER (MAKE_MARKER ('I', 'A', 'R', 'T'))#define INAM_MARKER (MAKE_MARKER ('I', 'N', 'A', 'M'))#define IENG_MARKER (MAKE_MARKER ('I', 'E', 'N', 'G'))#define IART_MARKER (MAKE_MARKER ('I', 'A', 'R', 'T'))#define ICOP_MARKER (MAKE_MARKER ('I', 'C', 'O', 'P'))#define IPRD_MARKER (MAKE_MARKER ('I', 'P', 'R', 'D'))#define ISRC_MARKER (MAKE_MARKER ('I', 'S', 'R', 'C'))#define ISBJ_MARKER (MAKE_MARKER ('I', 'S', 'B', 'J'))#define ICMT_MARKER (MAKE_MARKER ('I', 'C', 'M', 'T'))/* Weird WAVPACK marker which can show up at the start of the DATA section. */#define wvpk_MARKER (MAKE_MARKER ('w', 'v', 'p', 'k'))#define OggS_MARKER (MAKE_MARKER ('O', 'g', 'g', 'S'))enum{ HAVE_RIFF = 0x01, HAVE_WAVE = 0x02, HAVE_fmt = 0x04, HAVE_fact = 0x08, HAVE_PEAK = 0x10, HAVE_data = 0x20, HAVE_other = 0x80000000} ;/* known WAVEFORMATEXTENSIBLE GUIDS */static const EXT_SUBFORMAT MSGUID_SUBTYPE_PCM ={ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }} ;static const EXT_SUBFORMAT MSGUID_SUBTYPE_MS_ADPCM ={ 0x00000002, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }} ;static const EXT_SUBFORMAT MSGUID_SUBTYPE_IEEE_FLOAT ={ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }} ;static const EXT_SUBFORMAT MSGUID_SUBTYPE_ALAW ={ 0x00000006, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }} ;static const EXT_SUBFORMAT MSGUID_SUBTYPE_MULAW ={ 0x00000007, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }} ;#if 0/* maybe interesting one day to read the following through sf_read_raw *//* http://www.bath.ac.uk/~masrwd/pvocex/pvocex.html */static const EXT_SUBFORMAT MSGUID_SUBTYPE_PVOCEX ={ 0x8312B9C2, 0x2E6E, 0x11d4, { 0xA8, 0x24, 0xDE, 0x5B, 0x96, 0xC3, 0xAB, 0x21 }} ;#endif/*------------------------------------------------------------------------------** Private static functions.*/static int wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) ;static int wav_write_header (SF_PRIVATE *psf, int calc_length) ;static void wavex_write_guid (SF_PRIVATE *psf, const EXT_SUBFORMAT * subformat) ;static int wavex_write_header (SF_PRIVATE *psf, int calc_length) ;static int wav_write_tailer (SF_PRIVATE *psf) ;static void wav_write_strings (SF_PRIVATE *psf, int location) ;static int wav_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;static int wav_close (SF_PRIVATE *psf) ;static int wav_subchunk_parse (SF_PRIVATE *psf, int chunk) ;static int wav_read_smpl_chunk (SF_PRIVATE *psf, unsigned int chunklen) ;static int wav_read_acid_chunk (SF_PRIVATE *psf, unsigned int chunklen) ;static int wavex_write_guid_equal (const EXT_SUBFORMAT * first, const EXT_SUBFORMAT * second) ;/*------------------------------------------------------------------------------** Public function.*/intwav_open (SF_PRIVATE *psf){ int format, subformat, error, blockalign = 0, framesperblock = 0 ; if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) { if ((error = wav_read_header (psf, &blockalign, &framesperblock))) return error ; } ; subformat = psf->sf.format & SF_FORMAT_SUBMASK ; if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) { if (psf->is_pipe) return SFE_NO_PIPE_WRITE ; format = psf->sf.format & SF_FORMAT_TYPEMASK ; if (format != SF_FORMAT_WAV && format != SF_FORMAT_WAVEX) return SFE_BAD_OPEN_FORMAT ; psf->endian = SF_ENDIAN_LITTLE ; /* All WAV files are little endian. */ psf->blockwidth = psf->bytewidth * psf->sf.channels ; if (psf->mode != SFM_RDWR || psf->filelength < 44) { psf->filelength = 0 ; psf->datalength = 0 ; psf->dataoffset = 0 ; psf->sf.frames = 0 ; } ; if (subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) { blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; framesperblock = -1 ; /* Corrected later. */ } ; psf->str_flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ; /* By default, add the peak chunk to floating point files. Default behaviour ** can be switched off using sf_command (SFC_SET_PEAK_CHUNK, SF_FALSE). */ if (psf->mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)) { psf->pchunk = calloc (1, sizeof (PEAK_CHUNK) * psf->sf.channels * sizeof (PEAK_POS)) ; if (psf->pchunk == NULL) return SFE_MALLOC_FAILED ; psf->has_peak = SF_TRUE ; psf->peak_loc = SF_PEAK_START ; } ; psf->write_header = (format == SF_FORMAT_WAV) ? wav_write_header : wavex_write_header ; } ; psf->close = wav_close ; psf->command = wav_command ; switch (subformat) { case SF_FORMAT_PCM_U8 : case SF_FORMAT_PCM_16 : case SF_FORMAT_PCM_24 : case SF_FORMAT_PCM_32 : error = pcm_init (psf) ; break ; case SF_FORMAT_ULAW : error = ulaw_init (psf) ; break ; case SF_FORMAT_ALAW : error = alaw_init (psf) ; break ; /* Lite remove start */ case SF_FORMAT_FLOAT : error = float32_init (psf) ; break ; case SF_FORMAT_DOUBLE : error = double64_init (psf) ; break ; case SF_FORMAT_IMA_ADPCM : error = wav_w64_ima_init (psf, blockalign, framesperblock) ; break ; case SF_FORMAT_MS_ADPCM : error = wav_w64_msadpcm_init (psf, blockalign, framesperblock) ; break ; /* Lite remove end */ case SF_FORMAT_GSM610 : error = gsm610_init (psf) ; break ; default : return SFE_UNIMPLEMENTED ; } ; if (psf->mode == SFM_WRITE || (psf->mode == SFM_RDWR && psf->filelength == 0)) return psf->write_header (psf, SF_FALSE) ; return error ;} /* wav_open *//*=========================================================================** Private functions.*/static intwav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock){ WAV_FMT wav_fmt ; FACT_CHUNK fact_chunk ; int dword, marker, RIFFsize, done = 0 ; int parsestage = 0, error, format = 0 ; char *cptr ; /* Set position to start of file to begin reading header. */ psf_binheader_readf (psf, "p", 0) ; while (! done) { psf_binheader_readf (psf, "m", &marker) ; switch (marker) { case RIFF_MARKER : if (parsestage) return SFE_WAV_NO_RIFF ; parsestage |= HAVE_RIFF ; psf_binheader_readf (psf, "e4", &RIFFsize) ; if (psf->fileoffset > 0 && psf->filelength > RIFFsize + 8) { /* Set file length. */ psf->filelength = RIFFsize + 8 ; psf_log_printf (psf, "RIFF : %u\n", RIFFsize) ; } else if (psf->filelength < RIFFsize + 2 * SIGNED_SIZEOF (dword)) { psf_log_printf (psf, "RIFF : %u (should be %D)\n", RIFFsize, psf->filelength - 2 * SIGNED_SIZEOF (dword)) ; RIFFsize = dword ; } else psf_log_printf (psf, "RIFF : %u\n", RIFFsize) ; break ; case WAVE_MARKER : if ((parsestage & HAVE_RIFF) != HAVE_RIFF) return SFE_WAV_NO_WAVE ; parsestage |= HAVE_WAVE ; psf_log_printf (psf, "WAVE\n") ; break ; case fmt_MARKER : if ((parsestage & (HAVE_RIFF | HAVE_WAVE)) != (HAVE_RIFF | HAVE_WAVE)) return SFE_WAV_NO_FMT ; /* If this file has a SECOND fmt chunk, I don't want to know about it. */ if (parsestage & HAVE_fmt) break ; parsestage |= HAVE_fmt ; psf_binheader_readf (psf, "e4", &dword) ; psf_log_printf (psf, "fmt : %d\n", dword) ; if ((error = wav_w64_read_fmt_chunk (psf, &wav_fmt, dword))) return error ; format = wav_fmt.format ; break ; case data_MARKER : if ((parsestage & (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) != (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) return SFE_WAV_NO_DATA ; if (psf->mode == SFM_RDWR && (parsestage & HAVE_other) != 0) return SFE_RDWR_BAD_HEADER ; parsestage |= HAVE_data ; psf_binheader_readf (psf, "e4", &dword) ; psf->datalength = dword ; psf->dataoffset = psf_ftell (psf) ; if (dword == 0 && RIFFsize == 8 && psf->filelength > 44) { psf_log_printf (psf, "*** Looks like a WAV file which wasn't closed properly. Fixing it.\n") ; psf->datalength = dword = psf->filelength - psf->dataoffset ; } ; if (psf->datalength > psf->filelength - psf->dataoffset) { psf_log_printf (psf, "data : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ; psf->datalength = psf->filelength - psf->dataoffset ; } else psf_log_printf (psf, "data : %D\n", psf->datalength) ; /* Only set dataend if there really is data at the end. */ if (psf->datalength + psf->dataoffset < psf->filelength) psf->dataend = psf->datalength + psf->dataoffset ; if (format == WAVE_FORMAT_MS_ADPCM && psf->datalength % 2) { psf->datalength ++ ; psf_log_printf (psf, "*** Data length odd. Increasing it by 1.\n") ; } ; if (! psf->sf.seekable) break ; /* Seek past data and continue reading header. */ psf_fseek (psf, psf->datalength, SEEK_CUR) ; dword = psf_ftell (psf) ; if (dword != (sf_count_t) (psf->dataoffset + psf->datalength)) psf_log_printf (psf, "*** psf_fseek past end error ***\n", dword, psf->dataoffset + psf->datalength) ; break ; case fact_MARKER : if ((parsestage & (HAVE_RIFF | HAVE_WAVE)) != (HAVE_RIFF | HAVE_WAVE)) return SFE_WAV_BAD_FACT ; parsestage |= HAVE_fact ; if ((parsestage & HAVE_fmt) != HAVE_fmt) psf_log_printf (psf, "*** Should have 'fmt ' chunk before 'fact'\n") ; psf_binheader_readf (psf, "e44", &dword, & (fact_chunk.frames)) ; if (dword > SIGNED_SIZEOF (fact_chunk)) psf_binheader_readf (psf, "j", (int) (dword - SIGNED_SIZEOF (fact_chunk))) ; if (dword) psf_log_printf (psf, "%M : %d\n", marker, dword) ; else psf_log_printf (psf, "%M : %d (should not be zero)\n", marker, dword) ; psf_log_printf (psf, " frames : %d\n", fact_chunk.frames) ; break ; case PEAK_MARKER : if ((parsestage & (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) != (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) return SFE_WAV_PEAK_B4_FMT ; parsestage |= HAVE_PEAK ; psf_binheader_readf (psf, "e4", &dword) ; psf_log_printf (psf, "%M : %d\n", marker, dword) ; if (dword != SIGNED_SIZEOF (PEAK_CHUNK) + psf->sf.channels * SIGNED_SIZEOF (PEAK_POS)) { psf_binheader_readf (psf, "j", dword) ; psf_log_printf (psf, "*** File PEAK chunk size doesn't fit with number of channels.\n") ; return SFE_WAV_BAD_PEAK ; } ; psf->pchunk = calloc (1, sizeof (PEAK_CHUNK) * psf->sf.channels * sizeof (PEAK_POS)) ; if (psf->pchunk == NULL) return SFE_MALLOC_FAILED ; /* read in rest of PEAK chunk. */ psf_binheader_readf (psf, "e44", & (psf->pchunk->version), & (psf->pchunk->timestamp)) ; if (psf->pchunk->version != 1) psf_log_printf (psf, " version : %d *** (should be version 1)\n", psf->pchunk->version) ; else psf_log_printf (psf, " version : %d\n", psf->pchunk->version) ; psf_log_printf (psf, " time stamp : %d\n", psf->pchunk->timestamp) ; psf_log_printf (psf, " Ch Position Value\n") ; cptr = psf->u.cbuf ; for (dword = 0 ; dword < psf->sf.channels ; dword++) { psf_binheader_readf (psf, "ef4", & (psf->pchunk->peaks [dword].value), & (psf->pchunk->peaks [dword].position)) ; LSF_SNPRINTF (cptr, sizeof (psf->u.cbuf), " %2d %-12d %g\n", dword, psf->pchunk->peaks [dword].position, psf->pchunk->peaks [dword].value) ; cptr [sizeof (psf->u.cbuf) - 1] = 0 ; psf_log_printf (psf, cptr) ; } ; psf->has_peak = SF_TRUE ; /* Found PEAK chunk. */ psf->peak_loc = ((parsestage & HAVE_data) == 0) ? SF_PEAK_START : SF_PEAK_END ; break ; case cue_MARKER : parsestage |= HAVE_other ; { int bytesread, cue_count ; int id, position, chunk_id, chunk_start, block_start, offset ; bytesread = psf_binheader_readf (psf, "e44", &dword, &cue_count) ; bytesread -= 4 ; /* Remove bytes for first dword. */ psf_log_printf (psf, "%M : %u\n Count : %d\n", marker, dword, cue_count) ; while (cue_count) { bytesread += psf_binheader_readf (psf, "e444444", &id, &position, &chunk_id, &chunk_start, &block_start, &offset) ; psf_log_printf (psf, " Cue ID : %2d" " Pos : %5u Chunk : %M" " Chk Start : %d Blk Start : %d" " Offset : %5d\n", id, position, chunk_id, chunk_start, block_start, offset) ; cue_count -- ; } ; if (bytesread != dword) { psf_log_printf (psf, "**** Chunk size weirdness (%d != %d)\n", dword, bytesread) ; psf_binheader_readf (psf, "j", dword - bytesread) ; } ; } ; break ; case smpl_MARKER : parsestage |= HAVE_other ; psf_binheader_readf (psf, "e4", &dword) ; psf_log_printf (psf, "smpl : %u\n", dword) ; if ((error = wav_read_smpl_chunk (psf, dword)))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -