WAVE out test code[펌]
ref:http://tapito.tistory.com/360
오류가 있어서 조금 수정
callback 함수 관련 정보
https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd743869(v=vs.85).aspx
waveform 관련 api
https://msdn.microsoft.com/en-us/library/windows/desktop/dd743834(v=vs.85).aspx
--- source ---
#pragma comment(lib, "winmm.lib")
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <assert.h>
#include <conio.h>
#include <tchar.h>
#pragma pack(push, 1)
typedef struct
{
CHAR szChunkID[4]; // 항상 'R', 'I', 'F', 'F'라는 4개의 ASCII 문자가 옵니다.
DWORD dwChunkSize; // 이 필드 바로 다음부터 파일의 맨 끝까지의 크기를 저장합니다.
CHAR dwFormat[4]; // 이 필드 다음에 오는 형식의 종류를 지정합니다. 여기에서는 'W', 'A', 'V', 'E'의 4개의 ASCII 문자가 옵니다.
} RIFF_HEADER;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
CHAR szChunkID[4]; // 항상 'f', 'm', 't', ' '라는 4개의 ASCII 문자가 옵니다. 맨 마지막 글자는 공백(0x20)입니다.
DWORD dwChunkSize; // 이 필드 바로 다음부터 파일의 맨 끝까지의 크기를 저장합니다.
WORD wFormatTag; // 데이터의 종류를 지정합니다. PCM 형식은 1입니다.
WORD nChannels; // 채널의 수를 지정합니다.
DWORD nSamplesPerSec; // 초당 샘플링 회수를 지정합니다.
DWORD nAvgBytesPerSec; // 1초당 소리 크기를 바이트 수로 지정합니다.
WORD nBlockAlign; // 채널의 총 수를 고려하여 sample 1회당 바이트 수를 나타낸 것입니다.
WORD wBitsPerSample; // 1개 채널당 sample 1회당 바이트 수를 나타낸 것입니다.
} RIFF_FORMAT;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
CHAR szChunkID[4]; // 항상 'd', 'a', 't', 'a'라는 4개의 ASCII 문자가 옵니다.
DWORD dwChunkSize; // 이 필드 바로 다음부터 파일의 맨 끝까지의 크기를 저장합니다. 헤더 정보를 뺀 순수 데이터의 크기입니다.
} RIFF_DATA;
#pragma pack(pop)
RIFF_HEADER riffHeader;
RIFF_FORMAT riffFormat;
RIFF_DATA riffData;
void CALLBACK WaveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
static WAVEHDR waveHeader;
switch (uMsg)
{
case WOM_OPEN: // waveOutOpen 함수가 실행되었을 경우에는 여기 부분이 처리
_tprintf(TEXT("waveOutProc: WOM_OPEN\n"));
break;
case WOM_DONE: // waveOutWrite 함수로 작성한 데이터의 재생이 완료되었을 경우에는 여기 부분이 처리
_tprintf(TEXT("waveOutProc: WOM_DONE\n"));
break;
case WOM_CLOSE: // waveOutClose 함수가 실행되었을 경우에는 여기 부분이 처리
_tprintf(TEXT("waveOutProc: WOM_CLOSE\n"));
break;
default:
break;
}
}
int main(int argc, char * argv[])
{
TCHAR szWaveFile[MAX_PATH]; // WAVE 파일의 경로입니다.
HANDLE hWaveFile = NULL; // WAVE 파일을 읽는 핸들입니다.
DWORD dwWaveFileSize = 0; // WAVE 파일의 크기입니다.
DWORD dwWaveFileRead = 0; // 읽어들인 WAVE 파일의 크기입니다. 정상이라면 dwWaveFileSize와 같은 값을 갖습니다.
DWORD dwWaveData = 0; // 메모리 할당받을 크기입니다. 정상이라면 dwWaveFileRead와 같은 값을 갖습니다.
HGLOBAL hWaveData = NULL; // 할당받은 메모리에 대한 핸들입니다.
LPBYTE lpWaveData = NULL; // WAVE 데이터를 읽고 쓸 포인터입니다.
HWAVEOUT hWaveOut = NULL; // WAVE 출력 장치에 대한 핸들입니다.
WAVEFORMATEX waveFormatEx; // WAVE 출력 형식을 지정한 구조체입니다.
DWORD dwDelay = 0; // 프로그램 종료 지연 시간입니다. WAVE 파일의 재생 시간과 같습니다.
WAVEHDR waveHeader; // waveOutPrepareHeader, waveOutUnprepareHeader에서 사용하는 구조체입니다.
// 유니코드 설정합니다.
_tsetlocale(LC_ALL, TEXT(""));
// 재생할 파일 이름을 지정합니다.
_tcscpy(szWaveFile, TEXT("test.wav"));
// 파일 크기를 구합니다.
hWaveFile = CreateFile(szWaveFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); assert(hWaveFile != NULL);
{
dwWaveFileSize = GetFileSize(hWaveFile, NULL); assert(dwWaveFileSize > 0);
}
CloseHandle(hWaveFile);
// 파일 크기만큼 메모리를 할당 받습니다.
hWaveData = GlobalAlloc(GMEM_MOVEABLE, dwWaveFileSize); assert(hWaveData != NULL);
{
// 파일을 읽어 메모리에 복사하기
lpWaveData = GlobalLock(hWaveData); assert(lpWaveData);
hWaveFile = CreateFile(szWaveFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); assert(hWaveFile != NULL);
{
SetFilePointer(hWaveFile, 0, NULL, SEEK_SET);
ReadFile(hWaveFile, lpWaveData, dwWaveFileSize, &dwWaveFileRead, NULL); assert(dwWaveFileSize == dwWaveFileRead);
}
assert(CloseHandle(hWaveFile) != 0);
assert(GlobalUnlock(hWaveData) == 0);
// 헤더 부분을 읽어 구조체로 복사하기
lpWaveData = GlobalLock(hWaveData); assert(lpWaveData != NULL);
{
CopyMemory(&riffHeader, lpWaveData, sizeof(RIFF_HEADER));
lpWaveData += sizeof(RIFF_HEADER);
CopyMemory(&riffFormat, lpWaveData, sizeof(RIFF_FORMAT));
lpWaveData += sizeof(RIFF_FORMAT);
CopyMemory(&riffData, lpWaveData, sizeof(RIFF_DATA));
lpWaveData += sizeof(RIFF_DATA);
}
assert(GlobalUnlock(hWaveFile));
// 웨이브 출력 장치 열기
lpWaveData = GlobalLock(hWaveData); assert(lpWaveData != NULL);
{
// 읽은 데이터 중 일부를 WAVEFORMAT 구조체로 옮김
waveFormatEx.nAvgBytesPerSec = riffFormat.nAvgBytesPerSec;
waveFormatEx.nBlockAlign = riffFormat.nBlockAlign;
waveFormatEx.nChannels = riffFormat.nChannels;
waveFormatEx.nSamplesPerSec = riffFormat.nSamplesPerSec;
waveFormatEx.wFormatTag = riffFormat.wFormatTag;
waveFormatEx.wBitsPerSample = riffFormat.wBitsPerSample;
waveFormatEx.cbSize = 0;
// 소리 출력 장치를 열기
assert(waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormatEx, (DWORD)WaveOutProc, (DWORD)lpWaveData, CALLBACK_FUNCTION) == MMSYSERR_NOERROR);
{
// 1 [sec]당 소요된 바이트 수를 구합니다.
// (1회 샘플크기[비트] / 8) * (1[sec]당 샘플 회수) * (채널 수:1=모노, 2=스테레오)
dwDelay = (riffFormat.wBitsPerSample / 8) * riffFormat.nSamplesPerSec * riffFormat.nChannels;
_tprintf(TEXT("bytesPerSec = %d\n"), dwDelay);
// 프로그램이 종료되는 것을 지연시키는 시간을 구합니다.
// ("data"로 시작하는 구조체의 dwChunkSize값[바이트]) / (1[sec]당 소요된 바이트)
dwDelay = riffData.dwChunkSize / dwDelay;
_tprintf(TEXT("TotalSec = %d (%02d:%02d)\n"), dwDelay, (dwDelay / 60), (dwDelay % 60));
waveHeader.lpData = (LPVOID)lpWaveData; // 소리 데이터가 있는 위치
waveHeader.dwBufferLength = riffData.dwChunkSize; // 소리 데이터의 크기
waveHeader.dwBytesRecorded = 0;
waveHeader.dwFlags = 0L;
waveHeader.dwLoops = 0L;
waveHeader.dwUser = 0;
waveHeader.lpNext = NULL;
waveHeader.reserved = 0L;
waveOutPrepareHeader(hWaveOut, &waveHeader, sizeof(WAVEHDR));
{
waveOutWrite(hWaveOut, &waveHeader, sizeof(WAVEHDR));
// 소리가 완전히 끝날때까지 프로그램 종료를 지연
Sleep(dwDelay*1000 + 1000);
}
waveOutUnprepareHeader(hWaveOut, &waveHeader, sizeof(WAVEHDR));
}
assert(waveOutClose(hWaveOut) == MMSYSERR_NOERROR);
}
GlobalUnlock(hWaveData);
}
GlobalFree(hWaveData);
_tprintf(TEXT("END"));
_getch();
return 0;
}
--- source end ---