< WaveBuffer.h >
#ifndef __WAVE_BUFFER_H__
#define __WAVE_BUFFER_H__
#include <windows.h>
#include <MMSystem.h>
#include "BufferQueue.h"
class WaveBuffer {
public:
WAVEHDR* pWaveHdr;
int length;
bool bDeleteData;
WaveBuffer(WAVEHDR *hdr, int len, bool deleteData = true) {
pWaveHdr = hdr;
length = len;
bDeleteData = deleteData;
}
virtual ~WaveBuffer() {
if (pWaveHdr && bDeleteData) {
delete[] pWaveHdr->lpData;
delete pWaveHdr;
}
}
};
class WaveBufferQueue {
public:
WaveBufferQueue(bool deleteData = true);
virtual ~WaveBufferQueue();
int push_back(WAVEHDR *pWaveHdr);
WaveBuffer* pop_front();
void clear() { m_pWaveQue->clear(); }
int count() { return m_pWaveQue->count(); }
private:
BufferQueue<WaveBuffer> *m_pWaveQue;
int m_nCount;
bool m_bDeleteData;
};
#endif
BufferQueue 템플릿 클래스는 C++ double linked-list Template Queue 포스트 참조< WaveBuffer.cpp >
#include "WaveBuffer.h"
WaveBufferQueue::WaveBufferQueue(bool deleteData)
{
m_pWaveQue = new BufferQueue<WaveBuffer>(0); // 무제한 버퍼
m_bDeleteData = deleteData;
}
WaveBufferQueue::~WaveBufferQueue()
{
if (m_pWaveQue) {
delete m_pWaveQue;
m_pWaveQue = NULL;
}
}
int WaveBufferQueue::push_back(WAVEHDR *pWaveHdr)
{
WaveBuffer *wave = new WaveBuffer(pWaveHdr, pWaveHdr->dwBufferLength, m_bDeleteData);
return m_pWaveQue->push_back(wave);
}
WaveBuffer* WaveBufferQueue::pop_front()
{
return m_pWaveQue->pop_front();
}
< WaveIn.h >
#ifndef __WAVE_IN_H__
#define __WAVE_IN_H__
#include <windows.h>
#include <MMSystem.h>
#include "WaveBuffer.h"
class WaveIn
{
public:
WaveIn();
virtual ~WaveIn();
/* WaveIn API */
int open(WAVEFORMATEX waveInFormat, int buff_count, double duration);
void close();
void setWaveInFunc(void (*func)(WAVEHDR *pHdr, void *pData), void *pData)
{
waveInHandlerFunc = func;
m_pWaveInHandlerData = pData;
}
public:
void processWaveInDone(WAVEHDR *pHdr);
void processWaveInClose();
void running();
protected:
HWAVEIN m_hWaveIn;
HANDLE m_hWaveInThread;
BOOL m_bWaveInRun;
WAVEHDR* m_pWaveInHdr;
int m_nWaveInHdrCount;
int m_nWaveInHdrPrepareCount;
WaveBufferQueue* m_pWaveBufferQue;
WAVEFORMATEX m_waveInFormatEx;
void (*waveInHandlerFunc)(WAVEHDR *pHdr, void *pData);
void *m_pWaveInHandlerData;
void waveInPrintError(MMRESULT result, LPCTSTR str);
};
#endif
<WaveIn.cpp >
#include "WaveIn.h"
#include "GlobalEnv.h"
#include <assert.h>
#include <process.h>
#include <stdio.h>
#pragma comment(lib, "winmm.lib")
WaveIn::WaveIn()
{
m_hWaveIn = NULL;
m_hWaveInThread = NULL;
m_bWaveInRun = FALSE;
memset(&m_waveInFormatEx, 0, sizeof(m_waveInFormatEx));
m_pWaveInHdr = NULL;
m_nWaveInHdrCount = 0;
m_pWaveBufferQue = NULL;
waveInHandlerFunc = NULL;
m_pWaveInHandlerData = NULL;
}
WaveIn::~WaveIn()
{
close();
}
unsigned __stdcall Thread_WaveIn(LPVOID lpParam)
{
WaveIn *pWaveIn = (WaveIn *)lpParam;
pWaveIn->running();
return 0;
}
void WaveIn::running()
{
MMRESULT mRes;
WAVEHDR *pHdr;
WaveBuffer *pWave;
while (1)
{
pWave = m_pWaveBufferQue->pop_front();
if (pWave == NULL)
{
Sleep(1);
continue;
}
pHdr = pWave->pWaveHdr;
mRes = waveInUnprepareHeader(m_hWaveIn, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInUnprepareHeader");
assert(FALSE);
continue;
}
m_nWaveInHdrPrepareCount--;
if (waveInHandlerFunc)
waveInHandlerFunc(pHdr, m_pWaveInHandlerData);
delete pWave;
if (m_bWaveInRun)
{
mRes = waveInPrepareHeader(m_hWaveIn, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInPrepareHeader");
assert(FALSE);
continue;
}
mRes = waveInAddBuffer(m_hWaveIn, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInAddBuffer");
assert(FALSE);
continue;
}
m_nWaveInHdrPrepareCount++;
}
else
{
//DXPRINTF("[%s] WaveIn Hdr Count:%d\n", __FUNCTION__, m_nWaveInHdrPrepareCount);
if (m_pWaveBufferQue->count() == 0 && m_nWaveInHdrPrepareCount == 0) {
break;
}
}
}
}
void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
WaveIn *pWaveIn = (WaveIn *)dwInstance;
switch(uMsg)
{
case WIM_OPEN:
DXPRINTF("WIM_OPEN\n");
break;
case WIM_CLOSE:
DXPRINTF("WIM_CLOSE\n");
pWaveIn->processWaveInClose();
break;
case WIM_DATA:
pWaveIn->processWaveInDone((WAVEHDR *)dwParam1);
break;
default:
break;
}
}
void WaveIn::processWaveInDone(WAVEHDR *pHdr)
{
if (m_pWaveBufferQue)
m_pWaveBufferQue->push_back(pHdr);
}
void WaveIn::processWaveInClose()
{
}
int WaveIn::open(WAVEFORMATEX waveInFormat, int buff_count, double duration)
{
MMRESULT mRes;
if (m_bWaveInRun)
return -1;
memset(&m_waveInFormatEx, 0, sizeof(m_waveInFormatEx));
m_waveInFormatEx.wFormatTag = WAVE_FORMAT_PCM;
m_waveInFormatEx.nChannels = waveInFormat.nChannels;
m_waveInFormatEx.nSamplesPerSec = waveInFormat.nSamplesPerSec;
m_waveInFormatEx.wBitsPerSample = waveInFormat.wBitsPerSample;
m_waveInFormatEx.nBlockAlign = m_waveInFormatEx.nChannels*m_waveInFormatEx.wBitsPerSample/8;
m_waveInFormatEx.nAvgBytesPerSec = m_waveInFormatEx.nSamplesPerSec*m_waveInFormatEx.nBlockAlign;
m_waveInFormatEx.cbSize = 0;
mRes = waveInOpen(&m_hWaveIn, WAVE_MAPPER, &m_waveInFormatEx,
(DWORD_PTR)waveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInOpen");
return -2;
}
/* prepare wavein header */
int buff_size = (int)(m_waveInFormatEx.nAvgBytesPerSec*duration);
buff_size *= m_waveInFormatEx.nChannels;
m_nWaveInHdrCount = buff_count;
m_pWaveInHdr = new WAVEHDR[m_nWaveInHdrCount];
for (int i=0; i<m_nWaveInHdrCount; i++)
{
memset(&m_pWaveInHdr[i], 0, sizeof(WAVEHDR));
m_pWaveInHdr[i].lpData = new char[buff_size];
m_pWaveInHdr[i].dwBufferLength = buff_size;
m_pWaveInHdr[i].dwUser = i;
mRes = waveInPrepareHeader(m_hWaveIn, &m_pWaveInHdr[i], sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInPrepareHeader");
assert(FALSE);
continue;
}
mRes = waveInAddBuffer(m_hWaveIn, &m_pWaveInHdr[i], sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes ,"waveInAddBuffer");
assert(FALSE);
continue;
}
}
m_nWaveInHdrPrepareCount = m_nWaveInHdrCount;
m_pWaveBufferQue = new WaveBufferQueue(false);
m_bWaveInRun = TRUE;
m_hWaveInThread = (HANDLE)_beginthreadex(NULL, 0, Thread_WaveIn, this, 0, NULL);
mRes = waveInStart(m_hWaveIn);
if (mRes != MMSYSERR_NOERROR) {
waveInPrintError(mRes, "waveInStart");
return -1;
}
return 0;
}
void WaveIn::close()
{
MMRESULT mRes;
if (!m_bWaveInRun || !m_hWaveIn)
return;
m_bWaveInRun = FALSE;
WaitForSingleObject(m_hWaveInThread, INFINITE);
mRes = waveInStop(m_hWaveIn);
if (mRes != MMSYSERR_NOERROR)
waveInPrintError(mRes, "waveInStop");
for (int i=0; i<m_nWaveInHdrCount; i++)
delete[] m_pWaveInHdr[i].lpData;
delete m_pWaveInHdr;
mRes = waveInClose(m_hWaveIn);
if (mRes != MMSYSERR_NOERROR)
waveInPrintError(mRes ,"waveInClose");
if (m_pWaveBufferQue) {
delete m_pWaveBufferQue;
m_pWaveBufferQue = NULL;
}
CloseHandle(m_hWaveInThread);
}
void WaveIn::waveInPrintError(MMRESULT result, LPCTSTR str)
{
char errmsg[128] = {0};
waveInGetErrorText(result, errmsg, sizeof(errmsg));
DXPRINTF("%s waveInError: %d %s\n", str, result, errmsg);
}
< WaveOut.h >
#ifndef __WAVE_OUT_H__
#define __WAVE_OUT_H__
#include <windows.h>
#include <MMSystem.h>
#include <MMReg.h>
#include <stdio.h>
#include "WaveBuffer.h"
class WaveOut
{
public:
WaveOut();
virtual ~WaveOut();
/* WaveOut API */
int open(WAVEFORMATEX waveOutFormat, DWORD channel_layout);
void close();
int waveOutAdd(unsigned char *buff, int len);
void setAudioVolume(int val);
DWORD getAudioVolume();
void reset();
void setMaxWaveOutBufferCount(int maxCount) { m_nMaxWaveOutBufferCount = maxCount; }
int channels() { return m_waveOutFormatEx.Format.nChannels; }
int sampleRate() { return m_waveOutFormatEx.Format.nSamplesPerSec; }
int channelLayout() { return m_waveOutFormatEx.dwChannelMask; }
public:
void processWaveOutDone(WAVEHDR *pHdr);
void processWaveOutClose();
void running();
protected:
HWAVEOUT m_hWaveOut;
HANDLE m_hWaveOutThread;
BOOL m_bWaveOutRun;
int m_nMaxWaveOutBufferCount;
int m_nWaveOutHdrPrepareCount;
WaveBufferQueue* m_pWaveBufferQue;
DWORD m_nAudioVolume;
WAVEFORMATEXTENSIBLE m_waveOutFormatEx;
WAVEOUTCAPS m_waveOutCaps;
void waveOutPrintError(MMRESULT result, LPCTSTR str);
FILE *m_pFile; // audio dump
};
#endif
< WaveOut.cpp >
#include "WaveOut.h"
#include "GlobalEnv.h"
#include <assert.h>
#include <process.h>
#include <stdio.h>
#pragma comment(lib, "winmm.lib")
WaveOut::WaveOut()
{
m_hWaveOut = NULL;
m_hWaveOutThread = NULL;
m_bWaveOutRun = FALSE;
m_nWaveOutHdrPrepareCount = 0;
m_pWaveBufferQue = new WaveBufferQueue();
m_nAudioVolume = 0xFFFFFFFF;
ZeroMemory(&m_waveOutCaps, sizeof(WAVEOUTCAPS));
ZeroMemory(&m_waveOutFormatEx, sizeof(WAVEFORMATEXTENSIBLE));
m_nMaxWaveOutBufferCount = 3;
m_pFile = NULL;
}
WaveOut::~WaveOut()
{
close();
DX_DELETE_OBJECT(m_pWaveBufferQue);
}
unsigned __stdcall Thread_WaveOut(LPVOID lpParam)
{
WaveOut *pWaveOut = (WaveOut *)lpParam;
pWaveOut->running();
return 0;
}
void WaveOut::running()
{
MMRESULT mRes;
WaveBuffer *pWave;
WAVEHDR *pHdr;
while (1)
{
pWave = m_pWaveBufferQue->pop_front();
if (pWave == NULL) {
Sleep(1);
goto skip;
}
pHdr = pWave->pWaveHdr;
mRes = waveOutUnprepareHeader(m_hWaveOut, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutUnprepareHeader");
assert(FALSE);
continue;
}
delete pWave;
InterlockedDecrement((LPLONG)&m_nWaveOutHdrPrepareCount);
skip:
if (!m_bWaveOutRun)
{
if (m_pWaveBufferQue->count() == 0 && m_nWaveOutHdrPrepareCount == 0) {
break;
}
}
}
}
void WaveOut::processWaveOutDone(WAVEHDR *pHdr)
{
if (m_pWaveBufferQue) {
m_pWaveBufferQue->push_back(pHdr);
}
}
void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
WaveOut *pWaveOut = (WaveOut *)dwInstance;
switch(uMsg)
{
case WOM_OPEN:
DXPRINTF("WOM_OPEN\n");
break;
case WOM_CLOSE:
DXPRINTF("WOM_CLOSE\n");
break;
case WOM_DONE:
pWaveOut->processWaveOutDone((WAVEHDR *)dwParam1);
break;
default:
break;
}
}
int WaveOut::waveOutAdd(unsigned char *buff, int len)
{
MMRESULT mRes;
if (!m_bWaveOutRun)
return -1;
//DXPRINTF("wave out buffer count : %d (%d)\n", m_nWaveOutHdrPrepareCount, len);
if (m_nMaxWaveOutBufferCount > 0) {
if (m_nWaveOutHdrPrepareCount > m_nMaxWaveOutBufferCount) {
DXPRINTF("wave out buffer overflow : %d (%d)\n", m_nWaveOutHdrPrepareCount, len);
return 0;
}
}
WAVEHDR *pHdr = new WAVEHDR;
ZeroMemory(pHdr, sizeof(WAVEHDR));
pHdr->lpData = new char[len];
memcpy(pHdr->lpData, buff, len);
pHdr->dwBufferLength = len;
pHdr->dwFlags = 0;
mRes = waveOutPrepareHeader(m_hWaveOut, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutPrepareHeader");
delete[] pHdr->lpData;
delete pHdr;
assert(FALSE);
return -2;
}
mRes = waveOutWrite(m_hWaveOut, pHdr, sizeof(WAVEHDR));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutWrite");
delete[] pHdr->lpData;
delete pHdr;
assert(FALSE);
return -3;
}
InterlockedIncrement((LPLONG)&m_nWaveOutHdrPrepareCount);
if (m_pFile) fwrite(buff, len, 1, m_pFile);
return 0;
}
int WaveOut::open(WAVEFORMATEX waveOutFormat, DWORD channel_layout)
{
MMRESULT mRes;
if (m_bWaveOutRun)
return -1;
m_waveOutFormatEx;
memset(&m_waveOutFormatEx, 0, sizeof(WAVEFORMATEXTENSIBLE));
m_waveOutFormatEx.Format.wFormatTag = WAVE_FORMAT_PCM;
m_waveOutFormatEx.Format.nChannels = waveOutFormat.nChannels;
m_waveOutFormatEx.Format.nSamplesPerSec = waveOutFormat.nSamplesPerSec;
m_waveOutFormatEx.Format.wBitsPerSample = waveOutFormat.wBitsPerSample;
m_waveOutFormatEx.Format.nBlockAlign = waveOutFormat.nChannels*waveOutFormat.wBitsPerSample/8;
m_waveOutFormatEx.Format.nAvgBytesPerSec = waveOutFormat.nSamplesPerSec*waveOutFormat.nBlockAlign;
if (waveOutFormat.nChannels <= 2) {
m_waveOutFormatEx.Format.cbSize = 0;
} else {
m_waveOutFormatEx.dwChannelMask = channel_layout;
m_waveOutFormatEx.Samples.wValidBitsPerSample = waveOutFormat.wBitsPerSample;
m_waveOutFormatEx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
m_waveOutFormatEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
m_waveOutFormatEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
}
mRes = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, (WAVEFORMATEX *)&m_waveOutFormatEx,
(DWORD_PTR)waveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutOpen");
return -2;
}
mRes = waveOutSetVolume(m_hWaveOut, m_nAudioVolume);
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutWrite");
}
m_bWaveOutRun = TRUE;
m_hWaveOutThread = (HANDLE)_beginthreadex(NULL, 0, Thread_WaveOut, this, 0, NULL);
if (!m_hWaveOutThread) {
DXPRINTF("[%s] waveout thread create error, err:%d\n", __FUNCTION__, GetLastError());
return -3;
}
ZeroMemory(&m_waveOutCaps, sizeof(WAVEOUTCAPS));
mRes = waveOutGetDevCaps((UINT_PTR)m_hWaveOut, &m_waveOutCaps, sizeof(WAVEOUTCAPS));
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutGetDevCaps");
}
#if 0
m_pFile = fopen("audio.wav", "wb");
#endif
return 0;
}
void WaveOut::close()
{
MMRESULT mRes;
if (!m_bWaveOutRun || !m_hWaveOut)
return;
waveOutReset(m_hWaveOut);
m_bWaveOutRun = FALSE;
WaitForSingleObject(m_hWaveOutThread, INFINITE);
mRes = waveOutClose(m_hWaveOut);
if (mRes != MMSYSERR_NOERROR)
waveOutPrintError(mRes, "waveOutClose");
CloseHandle(m_hWaveOutThread);
m_hWaveOutThread = NULL;
if (m_pFile) {
fclose(m_pFile);
m_pFile = NULL;
}
}
void WaveOut::setAudioVolume(int val)
{
m_nAudioVolume = val;
if (m_hWaveOut) {
MMRESULT mRes = waveOutSetVolume(m_hWaveOut, m_nAudioVolume);
if (mRes != MMSYSERR_NOERROR) {
waveOutPrintError(mRes, "waveOutSetVolume");
}
}
}
DWORD WaveOut::getAudioVolume()
{
if (m_hWaveOut) {
MMRESULT mRes = waveOutGetVolume(m_hWaveOut, &m_nAudioVolume);
if (mRes == MMSYSERR_NOERROR) return m_nAudioVolume;
waveOutPrintError(mRes, "waveOutGetVolume");
}
return 0;
}
void WaveOut::reset()
{
if (m_hWaveOut)
waveOutReset(m_hWaveOut);
}
void WaveOut::waveOutPrintError(MMRESULT result, LPCTSTR str)
{
char errmsg[128] = {0};
waveOutGetErrorText(result, errmsg, sizeof(errmsg));
DXPRINTF("%s waveOutError: %d %s\n", str, result, errmsg);
}
< WaveOut 사용법 >
WaveOut* m_pWaveOut = new WaveOut();
...
// waveout 열기
int open(int channel, int sample_rate, int channel_layout)
{
// open waveout
WAVEFORMATEX waveformat;
memset(&waveformat, 0, sizeof(WAVEFORMATEX));
waveformat.wFormatTag = WAVE_FORMAT_PCM;
waveformat.nChannels = channel;
waveformat.nSamplesPerSec = sample_rate;
waveformat.wBitsPerSample = 16;
waveformat.nBlockAlign = waveformat.nChannels*waveformat.wBitsPerSample/8;
waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec*waveformat.nBlockAlign;
waveformat.cbSize = 0;
int ret = m_pWaveOut->open(waveformat, channel_layout);
if (ret < 0) m_pWaveOut->close();
return ret;
}
// waveout 출력 - buff : 오디오 pcm 데이터, size : 오디오 pcm 데이터 사이즈
int waveout(unsigned char *buff, int size)
{
return m_pWaveOut->waveOutAdd(buff, size);
}
// waveout 닫기
void close()
{
m_pWaveOut->close();
}
혹시.. 풀소스 올려 주실수 있을까요??
답글삭제소스 내용 확인해 보면.. #include "WaveBuffer.h" 파일도 없고..
호출 방법은 open 만 하면 될것 같긴 한데..
예제가 없어 확실하지는 않네요..
추가로.. 파일로 저장하는 방법도 있으면 좋겠구요^^
wavein/waveout 어느쪽을 사용하든 오디오 처리에 대한 기본적인 이해는 있어야하구요. 오디오 덤프파일을 들으려면 GoldWave 로 채널/샘플링레이트 등을 맞춰주면 됩니다.
삭제안녕하세요 ~ 글 잘 읽고 많이 배워갑니다.
답글삭제그런데 한가지 추가하고 싶은게 있는데, 어떤식으로 접근해야 할지 몰라서 혹시나 하는 마음에 질문드립니다. 전 지금 mfc랑 api를 이용해서 녹음기 소스를 만들고 있는데, 저장 방법을 저장 버퍼가 차면 파일로 변환하던지 , 그게 힘들다면 버퍼에 있는 샘플을 가지고 오는 식으로 만들려고 합니다.(녹음기 안에 다른 프로그램을 넣을 예정이거든요.) 혹시 가능하시다면 약간의 도움을 주실수 있을까요?