きっかけは「マイナスワン」というプレーヤです。ソースコード付きだったのですが、あまりにも混沌としていて読むことが出来ませんでした。今なら読めるかもしれません。
C言語です。WAVE形式のファイルからデータを抜き出し、モノラル化(L+R)またはボーカルキャンセル(L-R)を行っています。特に難しいところはありません。ただ、WINAPIを使います。
一番役に立った。
リファレンスならここ。
MMIOでWAVE読みとりはここ。
最初の方に見つけた。
一番最初に参考にした。
#include <windows.h> //ライブラリをくっつける #pragma comment(lib, "winmm.lib") //#pragma comment(lib, "C:\\borland\\bcc55\\Lib\\PSDK\\winmm.lib") /**** 標準 *****/ LRESULT CALLBACK MainWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam); BOOL InitInstance(HINSTANCE hInst); HWND MakeWnd(HWND *hWnd,HINSTANCE hInst); static LPSTR lpClassName = "My Class"; static LPSTR lpClassTitle = "First Your WindowsApplication"; static LPSTR lpMenuName = "My Soft"; /******* WAVE系 ********/ //ファイルネーム static LPCTSTR openFileName = TEXT("i:\\temp\\test.wav"); //バッファ数(2以上にすること) #define BUFFER_COUNT 2 //チャンネル数(2固定) static const int channel =2; //再生時の周波数 int srate; //バッファ関連情報 int bufferSize; int bufferLenght; //バッファセレクター int bufferSelect; //バッファ static short *(wWave[BUFFER_COUNT]); //出力のため構造体 static HWAVEOUT hWave; static WAVEHDR whdr[BUFFER_COUNT]; //WAVEファイルを読み込むための構造体 static HMMIO hmmio; //フォーマットチャンクのための構造体 static MMCKINFO ckRiff; //再生予定のWAVEデータサイズ int waveDataSize; //再生済のWAVEデータのサイズ int readDataSize; int initWave(HWND hWnd); int oepnWaveFile(WAVEFORMATEX *wfe); void closeWave(void); LRESULT wmWomDone(void); int readWaveData(HMMIO hmmio,short *buf,int bufferSize,int bufferLenght); /***************************************************************************** * メイン *****************************************************************************/ INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, INT nCmdShow) { HWND hWndMain; MSG msg; if(!InitInstance(hInstance)){ MessageBox(0, "ウィンドウの登録に失敗しました。","MyApp", MB_OK); return FALSE; } if(!MakeWnd(&hWndMain,hInstance)){ MessageBox(0, "ウィンドウの作成に失敗しました。","MyApp", MB_OK); return FALSE; } ShowWindow(hWndMain, nCmdShow); UpdateWindow(hWndMain); //Wave系準備 if(initWave(hWndMain) ==FALSE)return FALSE; while(GetMessage(&msg, NULL, 0, 0)){ DispatchMessage(&msg); } //後処理 closeWave(); return msg.wParam; } /***************************************************************************** ウィンドウ作成 *****************************************************************************/ BOOL InitInstance(HINSTANCE hInst){ WNDCLASSEX wndclass; wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.hInstance = hInst; wndclass.lpszClassName = lpClassName; wndclass.lpfnWndProc =MainWndProc; wndclass.style =0; wndclass.hIcon =LoadIcon(NULL, IDI_APPLICATION); wndclass.hIconSm =LoadIcon(NULL, IDI_WINLOGO); wndclass.hCursor =LoadCursor(NULL, IDC_ARROW); wndclass.lpszMenuName =NULL; wndclass.cbClsExtra = wndclass.cbWndExtra =0; wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH); return RegisterClassEx(&wndclass); } HWND MakeWnd(HWND *hWnd,HINSTANCE hInstance){ *hWnd=CreateWindow(lpClassName,lpClassTitle,WS_OVERLAPPEDWINDOW ,CW_USEDEFAULT,CW_USEDEFAULT,100,100,HWND_DESKTOP ,NULL,hInstance,NULL); return *hWnd; } /***************************************************************************** ウィンドウプロシージャー *****************************************************************************/ LRESULT CALLBACK MainWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam){ switch (Message){ case MM_WOM_DONE: //バッファが切れたときに呼び出される。 return wmWomDone(); case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, Message, wParam, lParam); } return FALSE; } /***************************************************************************** WAVEファイルの読み込み *****************************************************************************/ int oepnWaveFile(WAVEFORMATEX *wfe) { //WAVEファイルのオープン hmmio = mmioOpen((LPSTR)openFileName,NULL,MMIO_READ); if(!hmmio){ MessageBox(0, "WAVEファイルのオープンに失敗","MyApp", MB_OK); return FALSE; } //WAVEファイルかのチェック if(mmioDescend(hmmio,&ckRiff, NULL, 0) != MMSYSERR_NOERROR || ckRiff.ckid != FOURCC_RIFF || ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E')) { MessageBox(0, "WAVEファイルではありません","MyApp", MB_OK); mmioClose(hmmio, 0); return FALSE; } // フォーマットチャンクの読み込み ckRiff.ckid = mmioFOURCC('f', 'm', 't', ' '); //フォーマットチャンクに侵入 if(mmioDescend(hmmio, &ckRiff, NULL, MMIO_FINDCHUNK) != MMSYSERR_NOERROR){ MessageBox(0, "フォーマットチャンクへの侵入に失敗","MyApp", MB_OK); mmioClose(hmmio, 0); return FALSE; } //フォーマットチャンクを読み込む ZeroMemory(wfe, sizeof(*wfe)); if(mmioRead(hmmio, (HPSTR)wfe, (long)(ckRiff.cksize)) != (long)(ckRiff.cksize)){ MessageBox(0, "フォーマットチャンクの読み込みに失敗","MyApp", MB_OK); mmioClose(hmmio, 0); return FALSE; } //フォーマットチャンクから抜け出す if(mmioAscend(hmmio, &ckRiff, 0) != MMSYSERR_NOERROR){ MessageBox(0, "フォーマットチャンクからの抜け出しに失敗","MyApp", MB_OK); mmioClose(hmmio, 0); return FALSE; } // データチャンクの読み込み ckRiff.ckid = mmioFOURCC('d', 'a', 't', 'a'); //データチャンクに侵入 if(mmioDescend(hmmio, &ckRiff, NULL, MMIO_FINDCHUNK) != MMSYSERR_NOERROR){ MessageBox(0, "データチャンクの侵入に失敗","MyApp", MB_OK); mmioClose(hmmio, 0); return FALSE; } //16Bitステレオかをチェック if(wfe->nChannels !=2 || wfe->wBitsPerSample !=16){ MessageBox(0, "16Bitまたはステレオではないので再生できません","MyApp", MB_OK); return FALSE; } //データのサイズ waveDataSize = ckRiff.cksize; //再生周波数を設定する。(自由に変えても良い。再生速度が変わる) srate = wfe->nSamplesPerSec; //srate = 44100; //srate = 49500; return TRUE; } /***************************************************************************** Wave系の初期化全部 *****************************************************************************/ int initWave(HWND hWnd) { int i; WAVEFORMATEX wfe; //WAVEデータ読み込みのための設定をする if(oepnWaveFile(&wfe) ==FALSE){ return FALSE; } //バッファの長さを計算 bufferLenght = srate * channel; bufferSize =bufferLenght * sizeof(short); //バッファのための領域確保 for(i =0;i<BUFFER_COUNT;i++){ wWave[i] = (short *)malloc(bufferSize); } //出力デバイスの設定 wfe.wFormatTag = WAVE_FORMAT_PCM; wfe.nSamplesPerSec = srate; wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample / 8; wfe.nAvgBytesPerSec = wfe.nSamplesPerSec * wfe.nBlockAlign; //出力デバイスを開く waveOutOpen(&hWave , WAVE_MAPPER , &wfe , (DWORD)hWnd , 0 , CALLBACK_WINDOW); for(i=0;i<BUFFER_COUNT;i++){ //再生バッファにデータを送るための設定 whdr[i].lpData = (LPSTR)wWave[i]; whdr[i].dwBufferLength = 0; whdr[i].dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; whdr[i].dwLoops = 1; waveOutPrepareHeader(hWave , &(whdr[i]) , sizeof(WAVEHDR)); } //マルチバッファリングをするために空データを書き込み waveOutWrite(hWave , &(whdr[0]) , sizeof(WAVEHDR)); waveOutWrite(hWave , &(whdr[1]) , sizeof(WAVEHDR)); //セレクタの設定 bufferSelect = 2%BUFFER_COUNT; return TRUE; } /****************************************************************************** Wave系の後処理全般 ******************************************************************************/ void closeWave(void) { int i; waveOutReset((HWAVEOUT)hWave); for( i=0;i<BUFFER_COUNT;i++){ waveOutUnprepareHeader((HWAVEOUT)hWave , &(whdr[i]) , sizeof(WAVEHDR)); free(wWave[i]); } //クローズ waveOutClose(hWave); //データチャンクから抜け出す mmioAscend(hmmio, &ckRiff, 0); mmioClose(hmmio,0); } /***************************************************************************** バッファが切れたときに呼び出される *****************************************************************************/ LRESULT wmWomDone(void) { int readsize; //最後まで再生した場合は最初にシークする。 if(waveDataSize - readDataSize == 0){ mmioSeek(hmmio,-waveDataSize,SEEK_CUR); readDataSize = 0; //MM_WOM_DONEを起こすために「waveOutWrite」を呼ぶ whdr[bufferSelect].dwBufferLength = 0; waveOutWrite((HWAVEOUT)hWave , &(whdr[bufferSelect]) , sizeof(WAVEHDR)); //バッファセレクターのインクリメント bufferSelect = (bufferSelect+1)%BUFFER_COUNT; return 0; } //読み込むサイズを決定する。 readsize =bufferSize; if(readsize > waveDataSize - readDataSize){ readsize = waveDataSize -readDataSize; readDataSize = waveDataSize; }else{ readDataSize+=readsize; } //読み込み readsize = readWaveData(hmmio,wWave[bufferSelect],readsize,bufferLenght); //再生バッファに書き込み whdr[bufferSelect].dwBufferLength =readsize; waveOutWrite((HWAVEOUT)hWave , &(whdr[bufferSelect]) , sizeof(WAVEHDR)); //バッファセレクターのインクリメント bufferSelect = (bufferSelect+1)%BUFFER_COUNT; return 0; } /***************************************************************************** ファイルからデータを読みとり、加工する *****************************************************************************/ int readWaveData(HMMIO hmmio,short *buf,int bufferSize,int bufferLenght) { //読み込み int ret = mmioRead(hmmio,(HPSTR)buf,bufferSize); //手抜きボーカルキャンセラー&モノラル化 if(0){ int i; for(i=0;i<bufferLenght;i+=2){ //モノラル化 //short t1 = (short)((((int)buf[i]) +((int)buf[i+1]))/2); //真ん中の音を消す short t1 = (short)(((int)buf[i]) -((int)buf[i+1])); buf[i] = buf[i+1] =t1; } } return ret; }
え~と、参考資料の方を見てください。あんまり説明することがありません。使っているテクニックは
あんまりたいしたこと無いです。詳しく知りたい方は一方下されば説明書きます。そのうちCodeZineに載るかもしれません(確証無し)。
無限リピートです。適当なところで止めてやってください。