Programming/Example

WAVE out test code[펌]

루나s 2017. 2. 6. 13:14

ref:http://tapito.tistory.com/360

오류가 있어서 조금 수정

waveout.zip



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 ---