2017-04-18 112 views
1

我正在嘗試使用SDL2和FFTW3在C++中創建音樂可視化工具。 我的目標是加載.wav音頻文件,然後同時播放音頻並使用SDL2回叫功能執行實時快速傅立葉變換。 我想獲取頻譜數據,以便日後可以實現圖形可視化。如何在SDL2音頻流數據上執行實時FFT

我按照SDL YouTube指南加載.wav並使用回調函數播放音頻,但我不明白如何對此數據執行FFT。我遵循另一個使用FFTW和SDL的C指南來產生類似的效果,但我仍然不確定如何實際執行它。

Uint8* sampData; 
SDL_AudioSpec wavSpec; 
Uint8* wavStart; 
Uint32 wavLength; 
SDL_AudioDeviceID aDevice; 

struct AudioData { 
    Uint8* filePosition; 
    Uint32 fileLength; 
}; 

void PlayAudioCallback(void* userData, Uint8* stream, int streamLength) { 
    AudioData* audio = (AudioData*)userData; 
    sampData = new Uint8; 

    if (audio->fileLength == 0) { 
     return; 
    } 

    Uint32 length = (Uint32)streamLength; 
    length = (length > audio->fileLength ? audio->fileLength : length); 

    SDL_memcpy(stream, audio->filePosition, length); 

    // HERE is where i'd like to implement the FFT on 'stream' data 
    // but i don't know how to implement this using FFTW 

    audio->filePosition += length; 
    audio->fileLength -= length; 
} 

int main() { 
    SDL_Init(SDL_INIT_AUDIO); 

    // Load .wav file 
    if (SDL_LoadWAV(FILE_PATH, &wavSpec, &wavStart, &wavLength) == NULL) { 
     cerr << "Couldnt load file: " << FILE_PATH << endl; 
     getchar(); 
    } 
    cout << "Loaded " << FILE_PATH << endl; 

    AudioData audio; 
    audio.filePosition = wavStart; 
    audio.fileLength = wavLength; 

    wavSpec.callback = PlayAudioCallback; 
    wavSpec.userdata = &audio; 

    // Open audio playback endpoint 
    aDevice = SDL_OpenAudioDevice(NULL, 0, &wavSpec, NULL, SDL_AUDIO_ALLOW_ANY_CHANGE); 
    if (aDevice == 0) { 
     cerr << "Audio Device connection failed: " << SDL_GetError() << endl; 
     getchar(); 
    } 
    // Play audio on playback endpoint 
    SDL_PauseAudioDevice(aDevice, 0); 

    // Do nothing while there's still data to be played 
    while (audio.fileLength > 0) { 
     SDL_Delay(100); 
    } 
} 

從我使用NumPy的爲.wav數據解壓到一個NumPy的陣列以往的經驗,在發送前內置NumPy的-FFT功能,但我什麼與SDL流數據做是無能我在這裏。

回答

2

你想要的是短期的FFT。您可以在執行FFT之前從流中收集樣本的緩衝區,並將窗函數應用於樣本。然後您收集另一個緩衝區,保留來自第一個緩衝區的一些樣本並追加新的樣本。重複,直到所有的數據已被處理。由於您的輸入數據是實數,因此FFT是對稱的,因此您只需要第一個N/2 + 1複雜輸出分檔。這些代表來自直流的頻率。到Fs/2。以他們的大小和情節。對每個FFT重複一次。

+0

幾乎正確。所描述的解決方案的問題是窗口函數的應用是破壞性的。當你說「保留第一個緩衝區的一些樣本」時,你會保留窗口樣本。事實上,在重疊較小的情況下,您可以將每個窗口邊緣的最大濾波部分保留在窗口函數幾乎爲零的位置。如果使用重疊窗口,最好在複製樣本時應用窗口函數。即'auto windowed = buffer * windowFunction'而不是'buffer * = windowFunction'。 – MSalters

+0

當然你是對的。我在提供細節時非常不透明,因爲我試圖在iPad鍵盤上輸入此信息。不過,它是一個重要的細節。謝謝。 – sizzzzlerz