2011-03-21 81 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