如果您了解 RIFF 文件结构,您可能也已经知道 PCM 音频是如何存储在其中的。
常见的格式是 16 位立体声 pcm。在这种情况下,每个样本都是 2 个字节,并且两个样本属于在一起(左+右)。但您需要检查格式块的确切格式。但我假设您现在正在操作 16 位立体声 pcm wav 文件。
您可以使用 16 位整数类型(short、_int16、int16_t)来操作样本。例如,要减小音量,您可以将每个样本除以某个数字。但如果你将其除以 2,并不意味着声音会自动减半。看这个帖子 http://www.ypass.net/blog/2010/01/pcm-audio-part-3-basic-audio-effects-volume-control/.
如果您只是操作样本,RIFF 标头不会更改,因此您可以从源中复制它们。
如果要删除或添加样本,数据块的大小将会改变,riff-header 中整个文件的大小也会改变。
例如,您可以简单地每隔 10 个样本删除一次,然后从数据块中复制 9*4=36 字节,跳过 4 字节,复制 36 字节等等。但如果你这样做的话,听起来会很糟糕。听到结果的最佳方法是操纵正弦波。如果正弦不完全正确,则很容易听到。要以正确的方式删除样本,您可能需要使用快速傅立叶变换 (FFT)。
根据您的评论,我添加以下内容:
See C++ 二进制文件 I/O http://www.angelfire.com/country/aldev0/cpphowto/cpp_BinaryFileIO.html有关文件 I/O 的快速指南。你的link https://ccrma.stanford.edu/courses/422/projects/WaveFormat/描述 RIFF 格式看起来正确,但并不完整。根据该描述,标头始终为 44 字节。但可以向标头添加更多信息。
您应该做的是跳过前 12 个字节(尽管您可以使用它来验证文件是否确实是波形文件)。
然后循环读取下一个块的名称和大小。如果它是您知道的块(“fmt”或“data”),您可以处理它,否则跳过它。
例如,它看起来像这样:
ifstream myFile ("example.wav", ios::in | ios::binary);
char buffer[12];
myFile.read (buffer, 12); // skip RIFF header
char chunkName[5];
unsigned long chunksize;
while (myFile.read (chunkName, 4)) {
chunkName[4]='\0'; // add trailing zero
myFile.read((char*)&chunksize, 4);
// if chunkname is 'fmt ' or 'data' process it here,
// otherwise skip any unknown chunk:
myFile.seekg(chunksize, ios_base::cur);
}