C++ 中的音频操作
我希望这是发布此内容的正确位置,并且有人可以提供帮助。
我是一名音乐技术学生,最近开始学习 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 中执行此操作(如果有的话)?我需要什么库?我知道这不是简单的事情,因此我们将不胜感激任何帮助。
感谢您的帮助和时间。
詹姆斯
I hope this is the right place to post this and somebody can help.
I am a music technology student and I've recently picked up learning C++ as it would greatly help my career knowing a programming language, especially this one since it is used in the video games industry.
Anyways onto the main topic. What I want to create is a program (in C++) that lets the user load a 16bit linear PCM WAVE file. Then I want to manipulate the audio sample data within that wave file. I want to either remove every nth sample or randomise them within a certain parameter (±10%). Then write it as a new WAVE file.
I am familier with the structure of WAVE files and the RIFF header. I also at the moment use Xcode as my IDE (since my macbook pro is my work computer), but I can code on my PC if necessary using codeblocks.
So in simple terms it should display something similar to this? I know there are errors in this, just so you get an idea of what I'm after:
#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;
}
Is it possible to do this in Xcode, if at all? What libraries would I need? I understand this is not simple stuff, so any help will be greatly appreciated.
Thankyou for your help and time.
James
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您了解 RIFF 文件结构,您可能也已经知道 PCM 音频是如何存储在其中的。
常见的格式是 16 位立体声 pcm。在这种情况下,每个样本都是 2 个字节,并且两个样本属于在一起(左+右)。但您需要检查格式块的确切格式。但我假设您现在正在操作 16 位立体声 pcm wav 文件。
您可以使用 16 位整数类型(short、_int16、int16_t)来操作样本。例如,要减小音量,您可以将每个样本除以某个数字。但如果将其除以 2,并不意味着声音会自动降低一半。请参阅这篇文章< /a>.
如果您只是操作样本,RIFF 标头不会更改,因此您可以从源中复制它们。
如果要删除或添加样本,数据块的大小以及 riff-header 中整个文件的大小都会发生变化。
例如,您可以简单地每隔 10 个样本删除一次,然后从数据块中复制 9*4=36 字节,跳过 4 字节,复制 36 字节等等。但如果你这样做的话,听起来会很糟糕。听到结果的最佳方法是操纵正弦波。如果正弦不完全正确,则很容易听到。要以正确的方式删除样本,您可能需要使用快速傅立叶变换 (FFT)。
根据您的评论,我添加以下内容:
请参阅C++ 二进制文件 I/O 有关文件 I/O 的快速指南。您描述 RIFF 格式的 链接 看起来正确,但不完整。根据该描述,标头始终为 44 字节。但可以向标头添加更多信息。
您应该做的是跳过前 12 个字节(尽管您可以使用它来验证文件是否确实是波形文件)。
然后循环读取下一个块的名称和大小。如果它是您知道的块(“fmt”或“data”),您可以处理它,否则跳过它。
例如,它看起来像这样:
If you know the RIFF file structure, you might also already know how PCM audio is stored in it.
A common format is 16-bit stereo pcm. In that case each sample is 2 bytes, and two samples belong together (left+right). But you need to check the format chunk for the exact format. But I asume for now you are manipulating a 16-bit stereo pcm wav file.
You can manipulate the samples using a 16 bit integer type (short, _int16, int16_t). For example to decrease the volume, you can divide every sample by some number. But if you divide it by 2, it does not automatically mean it will become half as loud. See this post.
If you just manipulate samples, the RIFF headers do not change, so you can copy them from the source.
If you want to remove or add samples, the size of the data chunk will change, and also the size of the whole file in the riff-header.
You could simply for example drop every 10th sample, then you would copy 9*4=36 bytes from the data chunk, skip 4 bytes, copy 36 bytes and so on. But if you do something like that, it will sound very bad. The best way to hear the result is to manipulate a sine wave. If the sine is not fully correct, it will be easy to hear it. To drop samples the right way, you probably need to use a Fast Fourier Transform (FFT).
As an addition based on your comments I add the following:
See C++ Binary File I/O for a quick howto on file I/O. Your link describing the RIFF format looks correct but is not complete. According to that description the header is always 44 bytes. But it is possible to add more information to the header.
What you should do is skip the first 12 bytes (although you can use it to verify if a file is really a wave file).
And then in a loop read the name and size of the next chunk. If it is a chunk you know ('fmt ' or 'data') you can process it, otherwise skip it.
So it can look like this for example:
这是我的 wav2pcm 和 pcm2wav 实用程序的(相对)可移植源:
http://nishi.dreamhosters.com/u/wav2pcm_v0.rar
Here're the (relatively) portable sources of my wav2pcm and pcm2wav utils:
http://nishi.dreamhosters.com/u/wav2pcm_v0.rar
请参阅 libsnd 文件。
http://en.wikipedia.org/wiki/Libsndfile
See libsndfile.
http://en.wikipedia.org/wiki/Libsndfile