C - 处理 WAV 结构中的多个字节长度

问题描述 投票:0回答:1

我正在用 C 语言编写一个 .wav PCM 文件头,但我仍然难以处理的一个部分是文件的每个样本格式参数的位数。现在我只添加了对 8 位和 16 位 PCM 的支持(将音频样本数据存储在

int16_t*
int8_t*
byteArray
中),但如果能够读取在将文件数据加载到内存之前,编写一堆函数来处理每个字节大小(writeToWav8()
writeToWav16()
writeToWav32()
)感觉非常天真。
我能想到的唯一解决方案是

使用 1 字节整数的固定数组并找到一种方法将较大的样本动态“打包”到数组中(例如,将 01000110 装入 0100、0110),我之前尝试过,但最终最终陷入困境,因为它不断导致一些问题精度损失的种类 我找不到根本原因(另外考虑到我必须为不同的字节大小编写不同的处理程序)
  1. 限制程序只能读/写 8、16 和 32 位 PCM,这感觉有点像逃避。
  2. 有没有办法更干净地处理这个问题?

结构现在看起来像这样:

typedef struct RiffHeader { // basic formatting info for the file, not relevant } RIFF_CHUNK; typedef struct FmtHeader { uint32_t Subchunk1ID; // all of these determine how to read the audio uint32_t Subchunk1Size; uint16_t AudioFormat; uint16_t NumChannels; uint32_t SampleRate; uint32_t ByteRate; uint16_t BlockAlign; uint16_t BitsPerSample; // bits per sample, determines how large each bit is in the data array } FMT_CHUNK; typedef struct DataChunk8 { uint32_t Subchunk2ID; uint32_t Subchunk2Size; int8_t *byteArray; // pointer to start of data // data size is equal to subchunk2size } DATA_CHUNK_8; typedef struct DataChunk16 { uint32_t Subchunk2ID; uint32_t Subchunk2Size; int16_t *byteArray; // pointer to start of data // data size is equal to subchunk2size } DATA_CHUNK_16; typedef struct WaveFile8 { RIFF_CHUNK RIFF; FMT_CHUNK FMT; DATA_CHUNK_8 DATA; } WAV_FILE_8; typedef struct WaveFile_16 { RIFF_CHUNK RIFF; FMT_CHUNK FMT; DATA_CHUNK_16 DATA; } WAV_FILE_16;

后面是一堆输入和/或输出 
WAV_FILE_8*

WAV_FILE_16*
 的辅助函数
*此外,我对文件读取有点困惑,因为我目前正在使用两个函数:

WAV_FILE_8* readFromFile8(const char* path) WAV_FILE_16* readFromFile16(const char* path)

其目的是执行您期望的操作,但我不确定在读取未知文件时如何确定并决定返回适当大小的 WAV(并使用适当的函数)。

c file struct byte wav
1个回答
0
投票
灵活数组成员

作为数据部分。对于其余部分,您的代码中有很多重复,因此只需将它们放入标记的联合中即可 typedef struct DataChunk { uint32_t Subchunk2ID; uint32_t Subchunk2Size; union { int8_t byteArray[]; int16_t byteArray[]; }; } DATA_CHUNK; typedef struct WaveFile { RIFF_CHUNK RIFF; FMT_CHUNK FMT; DATA_CHUNK DATA; } WAV_FILE;

这个功能在进入 C 标准之前就已经很常见了。例如,在 GCC 中,它是 
结构末尾长度为零的数组

,而在 MSVC 中,不能拥有长度为零的数组,因此他们使用 大小为 1 的数组。我还使用了匿名结构功能来避免另一个不必要的字段引用 分配结构时,您需要为

整个标头和数据指定足够的内存

// Allocate memory for the header WAV_FILE *wav = malloc(sizeof(*wav)); // Read the header if (fread(wav, sizeof(WAV_FILE), 1, inputfile) == 1) { // Resize the struct to make room for the data WAV_FILE *newWav = realloc(sizeof(*wav) + wav->DATA.Subchunk2Size); if (newWav) { wav = newWav; // Do something with the data } }

我不是语言律师,所以我不确定灵活数组成员是否位于联合或嵌套结构中是否允许。但如果不是,那么您可以轻松地重新组合结构,将数组移动到最外层结构的末尾,例如像这样

typedef struct DataChunk { uint32_t Subchunk2ID; uint32_t Subchunk2Size; } DATA_CHUNK_HEADER; typedef struct WaveFile { RIFF_CHUNK RIFF; FMT_CHUNK FMT; DATA_CHUNK_HEADER DATA_HEADER; union { int8_t byteArray[]; int16_t byteArray[]; }; } WAV_FILE; // or typedef union WaveFile2 { struct { RIFF_CHUNK RIFF; FMT_CHUNK FMT; DATA_CHUNK_HEADER DATA_HEADER; int8_t byteArray[]; }; struct { RIFF_CHUNK RIFF; FMT_CHUNK FMT; DATA_CHUNK_HEADER DATA_HEADER; int16_t byteArray[]; }; } WAV_FILE2;

或者,您可以完全删除 
int8_t *byteArray;

并仅使用

int16_t *byteArray;
,当文件是 8 位文件时,可以将其转换为
char*
int8_t*
,而不违反别名规则
当然,您还需要注意尼尔森评论的字节序

© www.soinside.com 2019 - 2024. All rights reserved.