2011-03-21 110 views
3

我希望这是张贴这个和有人可以帮助的正确地方。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?我需要什么库?我明白这不是简单的东西,所以任何帮助将不胜感激。

谢谢你的帮助和时间。

詹姆斯

+1

是 - 这应该是很简单 - 你只需要一个合适的库内存WAV文件数据和原始PCM样本之间的转换。 – 2011-03-21 14:53:50

回答

5

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

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

您可以使用16位整数类型(简称_int16,int16_t)来操作样本。例如,要减小音量,可以将每个样本除以某个数字。但是如果将它除以2,它并不意味着它会变成一样大声。请参阅this post

如果你只是操作样本,RIFF头不会改变,所以你可以从源复制它们。

如果要删除或添加样本,数据块的大小将会更改,并且还会更改riff标题中整个文件的大小。例如,你可以简单地放下每一个第10个样本,然后从数据块中复制9 * 4 = 36个字节,跳过4个字节,复制36个字节等等。但如果你这样做,听起来会很糟糕。听到结果的最好方法是操纵正弦波。如果正弦不完全正确,听起来很容易。为了以正确的方式取样,您可能需要使用快速傅立叶变换(FFT)。

作为基于您的评论的另外我想补充以下内容:

对文件I/O快速HOWTO见C++ Binary File 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); 
} 
+0

好的,但我仍然不确定如何写这个?有人建议我需要一个音频文件阅读器并将所有内容存储在缓冲区中。然后操纵样本并导出。但是把这个写到代码中,特别是当我还是新手时很难。此外,如果即时通讯只是更改样本值,并不删除任何,我不认为我需要改变任何东西在RIFF头? – 2011-03-22 13:04:59

+0

我认为你最好把它分成更小的任务。不要试图一次写入,但首先解析RIFF标题并在屏幕上显示它的相关信息。如果你是新手,那么当你陷入困境时,开始自己并寻求帮助也更好。但是如果你让别人创建所有的代码,你什么都不会学。关于您的评论,如果文件不是太大,您只能将所有内容存储在缓冲区中。否则,你将耗尽内存。但是,您也可以将文件的一部分放入缓冲区,并对其进行操作。但是这可能会更困难。 – wimh 2011-03-22 21:32:44

+0

好的,我一直在使用这个网站查看WAVE和RIFF的资源:[link](https://ccrma.stanford.edu/courses/422/projects/WaveFormat/)。我理论上大部分都是这样认识的,但从来没有围绕过编程这样的东西。我查了一下abit,并一直在研究一个代码,这导致我写这个:[链接](http://img42.imageshack.us/i/screenshot20110323at133.png/)我使用的图片,因为使用代码(或pastebin)弄乱了我的注释的格式。但是这个代码还没有进入它。我如何才能读取音频以便显示此信息? FLOAD? – 2011-03-23 13:39:21