🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

8662閲覧

リアルタイムでマイク入力をフーリエ変換

YMD_kts

総合スコア23

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/10/29 12:12

こちらのサイトを元にリアルタイムで録音するアプリケーションを作成したのですが、このマイク入力してる音声に対しFFTをかけてリアルタイムで周波数を取り出したいと考えています。

MM_WIM_DATAのメッセージを受け取ったときの処理にフーリエ変換を入れればよいのだろうとは思うのですが、bTempに一度録音データでいっぱいになったバッファをコピーしているので、このbTempにFFTをかけるのだと思っているのですが、BYTE型に対して数式の処理のFFTをかけるということがいまいちピンときていません。

それともFFTをかけるべき変数が違うのでしょうか。マイク入力を逐次FFTしていく方法を教えていただきたいです。

また、可能であれば、逐次的にFFTしたデータをCSVなどで出力する場合、録音を終了し、終了するまでに逐次的にかけていたFFTで得たデータの平均を出力すればよいのでしょうか。

C++

1#include <windows.h> 2//#include <mmeapi.h> 3#include "resource.h" 4 5#pragma comment(lib, "Winmm.lib") 6 7#define TITLE TEXT("REC") 8#define SRATE 48000 9 10LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) 11{ 12 //各種クラスの宣言 13 static WAVEFORMATEX wfe; 14 static HWAVEOUT hWaveOut; 15 static HWAVEIN hWaveIn; 16 static BYTE *bWave1 , *bWave2 , *bSave , *bTmp; 17 static WAVEHDR whdr1 , whdr2; 18 static DWORD dwLength = 0, dwCount; 19 static BOOL blReset = FALSE; 20 21 22 //メッセージ毎の挙動 23 switch (msg) 24 { 25 //解放 26 case WM_DESTROY: 27 if(bSave) free(bSave); 28 PostQuitMessage(0); 29 return 0; 30 //作成 31 case WM_CREATE: 32 //WAVEのフォーマットを定義 33 //モノラル,48kHz,16bit 34 wfe.wFormatTag = WAVE_FORMAT_PCM; 35 wfe.nChannels = 1; 36 wfe.nSamplesPerSec = SRATE; 37 wfe.nAvgBytesPerSec = SRATE*2; //48k * 1ch * 16bit /8 38 wfe.wBitsPerSample = 16; 39 wfe.nBlockAlign = 2; //wfe.nChannels * wfe.wBitsPerSample / 8 40 //書き込み、録音、再生はどれもnBlockAlignの整数倍で行う 41 wfe.cbSize = 0; 42 return 0; 43 case WM_COMMAND: 44 switch(LOWORD(wp)) 45 { 46 case IDB_REC: 47 bWave1 = (BYTE*)malloc(SRATE * 10); 48 bWave2 = (BYTE*)malloc(SRATE * 10); 49 50 whdr1.lpData = (char*)bWave1; 51 whdr1.dwBufferLength = SRATE * 5; 52 whdr1.dwBytesRecorded = 0; 53 whdr1.dwFlags = 0; 54 whdr1.dwLoops = 1; 55 whdr1.lpNext = NULL; 56 whdr1.dwUser = 0; 57 whdr1.reserved = 0; 58 59 whdr2.lpData = (char*)bWave2; 60 whdr2.dwBufferLength = SRATE * 5; 61 whdr2.dwBytesRecorded = 0; 62 whdr2.dwFlags = 0; 63 whdr2.dwLoops = 1; 64 whdr2.lpNext = NULL; 65 whdr2.dwUser = 0; 66 whdr2.reserved = 0; 67 68 waveInOpen(&hWaveIn, WAVE_MAPPER, &wfe, (DWORD)hWnd, 0, CALLBACK_WINDOW); 69 waveInPrepareHeader(hWaveIn, &whdr1, sizeof(WAVEHDR)); 70 waveInPrepareHeader(hWaveIn, &whdr2, sizeof(WAVEHDR)); 71 break; 72 case IDB_PLAY: 73 whdr1.lpData = (char*)bSave; 74 whdr1.dwBufferLength = dwLength; 75 whdr1.dwBytesRecorded = 0; 76 whdr1.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; 77 whdr1.dwLoops = 1; 78 whdr1.dwUser = 0; 79 whdr1.reserved = 0; 80 81 waveOutOpen(&hWaveOut , WAVE_MAPPER , &wfe , (DWORD)hWnd , 0 , CALLBACK_WINDOW); 82 waveOutPrepareHeader(hWaveOut , &whdr1 , sizeof(WAVEHDR)); 83 waveOutWrite(hWaveOut, &whdr1, sizeof(WAVEHDR)); 84 break; 85 case IDB_REC_END: 86 blReset = TRUE; 87 waveInReset(hWaveIn); 88 break; 89 case IDB_PLY_END: 90 waveOutReset(hWaveOut); 91 break; 92 } 93 return 0; 94 case MM_WIM_OPEN: 95 dwLength = 0; 96 bSave = (BYTE*)realloc(bSave, 1); 97 98 EnableWindow(GetDlgItem(hWnd , IDB_PLAY) , FALSE); 99 EnableWindow(GetDlgItem(hWnd , IDB_REC) , FALSE); 100 EnableWindow(GetDlgItem(hWnd , IDB_REC_END) , TRUE); 101 waveInAddBuffer(hWaveIn , &whdr1 , sizeof(WAVEHDR)); 102 waveInAddBuffer(hWaveIn , &whdr2, sizeof(WAVEHDR)); 103 waveInStart(hWaveIn); 104 return 0; 105 case MM_WIM_DATA: 106 bTmp = (BYTE*)realloc(bSave, dwLength + ((LPWAVEHDR)lp)->dwBytesRecorded); 107 if (blReset || !bTmp) 108 { 109 waveInClose(hWaveIn); 110 blReset = FALSE; 111 return 0; 112 } 113 bSave = bTmp; 114 for(dwCount = 0; dwCount < ((LPWAVEHDR)lp)->dwBytesRecorded; dwCount++) 115 *(bSave + dwLength + dwCount) = *(((LPWAVEHDR)lp)->lpData + dwCount); 116 dwLength += ((LPWAVEHDR)lp)->dwBytesRecorded; 117 waveInAddBuffer(hWaveIn, (LPWAVEHDR)lp, sizeof(WAVEHDR)); 118 return 0; 119 case MM_WIM_CLOSE: 120 waveInUnprepareHeader(hWaveIn, &whdr1, sizeof(WAVEHDR)); 121 waveInUnprepareHeader(hWaveIn, &whdr2, sizeof(WAVEHDR)); 122 free(bWave1); 123 free(bWave2); 124 125 EnableWindow(GetDlgItem(hWnd, IDB_PLAY), TRUE); 126 EnableWindow(GetDlgItem(hWnd, IDB_REC), TRUE); 127 EnableWindow(GetDlgItem(hWnd, IDB_REC_END), FALSE); 128 return 0; 129 case MM_WOM_OPEN: 130 EnableWindow(GetDlgItem(hWnd, IDB_PLAY), FALSE); 131 EnableWindow(GetDlgItem(hWnd, IDB_REC), FALSE); 132 EnableWindow(GetDlgItem(hWnd, IDB_PLY_END), TRUE); 133 return 0; 134 case MM_WOM_DONE: 135 waveOutClose(hWaveOut); 136 return 0; 137 case MM_WOM_CLOSE: 138 waveOutUnprepareHeader(hWaveOut, &whdr1, sizeof(WAVEHDR)); 139 EnableWindow(GetDlgItem(hWnd, IDB_PLAY), TRUE); 140 EnableWindow(GetDlgItem(hWnd, IDB_REC), TRUE); 141 EnableWindow(GetDlgItem(hWnd, IDB_PLY_END), FALSE); 142 return 0; 143 } 144 return DefWindowProc(hWnd , msg , wp , lp); 145 146} 147 148int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance , 149 PSTR lpCmdLine , int nCmdShow) 150{ 151 HWND hwnd; 152 MSG msg; 153 WNDCLASS winc; 154 155 winc.style = CS_HREDRAW | CS_VREDRAW; 156 winc.lpfnWndProc = WndProc; 157 winc.cbClsExtra = 0; 158 winc.cbWndExtra = DLGWINDOWEXTRA; 159 winc.hInstance = hInstance; 160 winc.hIcon = LoadIcon(NULL , IDI_APPLICATION); 161 winc.hCursor = LoadCursor(NULL , IDC_ARROW); 162 winc.hbrBackground = (HBRUSH)CreateSolidBrush(GetSysColor(COLOR_MENU)); 163 winc.lpszMenuName = NULL; 164 winc.lpszClassName = TEXT("KITTY"); 165 166 if (!RegisterClass(&winc)) return -1; 167 hwnd = CreateDialog(hInstance , TEXT("KITTY") , 0 , NULL); 168 169 if (hwnd == NULL) return -1; 170 171 while(GetMessage(&msg , NULL , 0 , 0)) { 172 TranslateMessage(&msg); 173 DispatchMessage(&msg); 174 } 175 return msg.wParam; 176 177}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

リアルタイムで周波数を取り出したいと考えています。

スペアナのようなものを想定しているのでしょうか?

でしたら、データの一部の時間区間を窓関数で切り抜いてフーリエ変換を行い、窓関数の時間軸をずらしながら上記の処理を繰り返していく『短時間フーリエ変換』という方法がありますので、こちらを使用するとよいのではないでしょうか。

投稿2019/10/30 02:53

magichan

総合スコア15898

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

case MM_WIM_DATA: bTmp = (BYTE*)realloc(bSave, dwLength + ((LPWAVEHDR)lp)->dwBytesRecorded); if (blReset || !bTmp) { waveInClose(hWaveIn); blReset = FALSE; return 0; } bSave = bTmp; for(dwCount = 0; dwCount < ((LPWAVEHDR)lp)->dwBytesRecorded; dwCount++) *(bSave + dwLength + dwCount) = *(((LPWAVEHDR)lp)->lpData + dwCount); dwLength += ((LPWAVEHDR)lp)->dwBytesRecorded; waveInAddBuffer(hWaveIn, (LPWAVEHDR)lp, sizeof(WAVEHDR)); return 0;

bTmpですと、0秒から現在までの録音できた全てのデータになってしまいますね。
普通リアルタイムでFFTするというとある一定時間をサンプリングしたものを用いますので
((LPWAVEHDR)lp)->lpDataが指す領域に((LPWAVEHDR)lp)->dwBytesRecordedbyte分格納されているデータをFFTするとよいと思います。

//WAVEのフォーマットを定義 //モノラル,48kHz,16bit

なのでwavの仕様から、BYTEよりもint16_tを使ったほうがよいでしょう。

投稿2019/10/30 03:16

asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YMD_kts

2019/11/02 13:28

FFTで円周率を扱うのでdouble* が良いのではないかと思ったのですが、SHORTの方が、キレイに波形データのバイトとマッチしてて良いのですかね。
asm

2019/11/02 15:55

最終および中間の結果がdoubleになるのはいいのですが 入力であるwavデータ自体は符号付き16bit整数が配列状になっているものであることに注意が必要です。
guest

0

BYTE型に対して数式の処理のFFTをかけるということがいまいちピンときていません

そのピンと来ていないのがよくわからないのですが、FFTは通常2のn乗個のデータを渡して処理するものです。何個渡すかによって周波数の解像度がかわります。当然たくさん渡したほうが解像度はよくなりますが、それだけのデータが貯まるまで時間はかかるし、計算量も多くなります。

Windowsのそのへんの仕組みは知りませんが、bTempというのは何サンプルぐらいのデータが得られるのでしょう? FFTに渡したいデータ個数よりbTempのデータ数の方が少ない場合があるなら、別途バッファを設けてFFTにわたすデータを貯めてやらなきゃいけないかも知れません。

投稿2019/10/29 13:11

thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問