C++ 中的音频操作

Posted

技术标签:

【中文标题】C++ 中的音频操作【英文标题】:Audio Manipulation In C++ 【发布时间】:2011-03-21 14:51:09 【问题描述】:

我希望这是发布此内容的正确位置,并且有人可以提供帮助。

我是一名音乐技术专业的学生,​​最近开始学习 C++,因为这将极大地帮助我了解一门编程语言,尤其是这门语言,因为它用于视频游戏行业。

总之进入主题。我想要创建的是一个程序(在 C++ 中),它允许用户加载一个 16 位线性 PCM WAVE 文件。然后我想操作该波形文件中的音频样本数据。我想删除每第 n 个样本或在某个参数(±10%)内随机化它们。然后将其写入新的 WAVE 文件。

我比较熟悉 WAVE 文件的结构和 RIFF 标头。我现在也使用 Xcode 作为我的 IDE(因为我的 macbook pro 是我的工作计算机),但如果需要,我可以使用代码块在我的 PC 上编码。

所以简单来说它应该显示类似这样的东西?我知道这其中有错误,只是为了让您了解我所追求的:

#include <iostream>
using namespace std;

class main()    //function start

    string fileinput;   //variable
    string outlocation; //variable

    cout << "please type file path directory: \n \n";
    cin >> fileinput;   //navigate to file by typing

    cout << "Where would you like to save new file? \n \n";
    cin >> outlocation; //select output by typing

    // Then all the maths and manipulation is done

    cout << "Your file has been created at ";
    cout << outlocation;
    cout << "\n \n";

    system("pause");

    return 0;

如果有的话,是否可以在 Xcode 中执行此操作?我需要哪些库?我知道这不是简单的事情,所以任何帮助将不胜感激。

感谢您的帮助和时间。

詹姆斯

【问题讨论】:

是的 - 这应该很简单 - 你只需要一个合适的库来在内存中的 WAV 文件数据和原始 PCM 样本之间进行转换。 【参考方案1】:

如果您知道 RIFF 文件结构,您可能也已经知道 PCM 音频是如何存储在其中的。

常见的格式是 16 位立体声 pcm。在这种情况下,每个样本是 2 个字节,并且两个样本属于一起(左+右)。但是您需要检查格式块的确切格式。但我现在假设您正在处理一个 16 位立体声 pcm wav 文件。

您可以使用 16 位整数类型(short、_int16、int16_t)来处理样本。例如,要减小音量,您可以将每个样本除以某个数字。但如果你将它除以 2,它并不自动意味着它会变响一半。见this post。

如果您只是操作样本,则 RIFF 标头不会更改,因此您可以从源中复制它们。

如果你想删除或添加样本,数据块的大小将会改变,同时 riff-header 中整个文件的大小也会改变。 例如,您可以简单地删除每 10 个样本,然后从数据块中复制 9*4=36 个字节,跳过 4 个字节,复制 36 个字节等等。但如果你这样做,听起来会很糟糕。听到结果的最佳方法是操纵正弦波。如果正弦不完全正确,则很容易听到。要以正确的方式删除样本,您可能需要使用快速傅里叶变换 (FFT)。

作为基于您的 cmets 的补充,我添加以下内容:

请参阅C++ Binary File I/O 了解有关文件 I/O 的快速操作方法。您描述 RIFF 格式的 link 看起来正确但不完整。根据该描述,标头始终为 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);

【讨论】:

好的,但我仍然不确定如何写这个?有人建议我需要一个音频文件阅读器并将所有内容存储在缓冲区中。然后操作样本并导出。但是把它写成代码,特别是当我还是新手时,这很困难。另外,如果我只是更改样本值而不删除任何值,我认为我不需要更改 RIFF 标头中的任何内容? 我认为您最好将其拆分为较小的任务。不要尝试立即编写此内容,而是首先解析 RIFF 标头并在屏幕上显示有关它的信息。如果您对此不熟悉,那么您最好自己开始并在遇到困难时寻求帮助。但是,如果您让其他人创建所有代码,您将一无所获。关于您的评论,如果文件不是太大,您只能将所有内容存储在缓冲区中。否则你会耗尽内存。但是您也可以只将文件的一部分放在缓冲区中,然后对其进行操作。但这可能更困难。 好的,我一直在使用这个网站查看 WAVE 和 RIFF 的资源:@​​987654324@。我在理论上知道其中的大部分内容,但从来没有绕开编程之类的。我环顾四周,一直在研究导致我写这个的代码:link 我使用了一张图片,因为使用代码(或 pastebin)弄乱了我的注释格式。但是这段代码还没有任何内容。如何让它读取音频以便显示此信息?卸载? 我在上面添加了更多信息。【参考方案2】:

这是我的 wav2pcm 和 pcm2wav 实用程序的(相对)便携源: http://nishi.dreamhosters.com/u/wav2pcm_v0.rar

【讨论】:

【参考方案3】:

参见 libsndfile。

http://en.wikipedia.org/wiki/Libsndfile

【讨论】:

以上是关于C++ 中的音频操作的主要内容,如果未能解决你的问题,请参考以下文章

libvorbis 音频从 C++ 中的内存解码

python中的音频嗅探?

从 16 位 PCM 中去除 C++ 中的音频噪声(嘶嘶声)

C ++中的Linux音频捕获

C++ 在控制台应用程序中播放视频音频

尝试使用 Win32 WASAPI C++ 中的“捕获流”创建 wav 文件