I want to record microphone audio input and, with some small time delay, play the recorded sound immediately. This will be done continuously using a queue of buffers.
I got the code running to the point that it almost continuously plays the microphone audio input, but there are very short but still noticeable repeated pauses throughout the whole audio output using waveOut. What is causing these annoying pauses? and how to remove them?
Another question is, I'm not using any thing like mutex, I'm relying on the fact that waveIn and waveOut have the same sampling rate and the same amount of data, so hopefully waveOut always follows waveIn and waveIn will not write to buffer being played. Would this be a problem?
Here's the code, it should compile and run. I only made the code run, and it's far from being well written. Any comment on improving the code is highly welcome.
#include "stdafx.h"
#include <Windows.h>
#pragma comment(lib, "winmm.lib")
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
HANDLE hEvent_BufferReady;
HANDLE hEvent_FinishedPlaying;
#define Samplerate 44100
#define nSec 1
int _iBuf;
int _iplaying;
unsigned long result;
HWAVEIN hWaveIn;
HWAVEOUT hWaveOut;
WAVEFORMATEX pFormat;
enum { NUM_BUF = 3 };
WAVEHDR _header [NUM_BUF];
DWORD WINAPI RecordingWaitingThread(LPVOID ivalue)
{
while(1)
{
WaitForSingleObject(hEvent_BufferReady,INFINITE);
result = waveInUnprepareHeader (hWaveIn, &_header[_iBuf], sizeof (WAVEHDR));
_iplaying = _iBuf;
result = waveOutPrepareHeader(hWaveOut, &_header[_iBuf], sizeof(WAVEHDR));
result = waveOutWrite(hWaveOut, &_header[_iBuf], sizeof(WAVEHDR)); // play audio
++_iBuf;
if (_iBuf == NUM_BUF) _iBuf = 0;
result = waveInPrepareHeader(hWaveIn, & _header[_iBuf], sizeof(WAVEHDR));
result = waveInAddBuffer (hWaveIn, & _header[_iBuf], sizeof (WAVEHDR));
}
return 0;
}
DWORD WINAPI PlayingWaitingThread(LPVOID ivalue)
{
while(1){
WaitForSingleObject(hEvent_FinishedPlaying,INFINITE);
waveOutUnprepareHeader(hWaveOut, &_header[_iplaying], sizeof(WAVEHDR));
}
}
static void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1,DWORD dwParam2 )
{
if(uMsg != WOM_DONE)
return;
SetEvent(hEvent_FinishedPlaying);
}
void CALLBACK myWaveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
if(uMsg != WIM_DATA)
return;
SetEvent(hEvent_BufferReady);
}
int main(int argc, _TCHAR* argv[])
{
hEvent_BufferReady=CreateEvent(NULL,FALSE, FALSE, NULL);
hEvent_FinishedPlaying = CreateEvent(NULL,FALSE, FALSE, NULL);
pFormat.wFormatTag = WAVE_FORMAT_PCM; // simple, uncompressed format
pFormat.nChannels = 1; // 1=mono, 2=stereo
pFormat.nSamplesPerSec = Samplerate;
pFormat.wBitsPerSample = 16; // 16 for high quality, 8 for telephone-grade
pFormat.nBlockAlign = pFormat.nChannels*pFormat.wBitsPerSample/8;
pFormat.nAvgBytesPerSec = (pFormat.nSamplesPerSec)*(pFormat.nChannels)*(pFormat.wBitsPerSample)/8;
pFormat.cbSize=0;
short int *_pBuf;
size_t bpbuff =4000;//= (pFormat.nSamplesPerSec) * (pFormat.nChannels) * (pFormat.wBitsPerSample)/8;
_pBuf = new short int [bpbuff * NUM_BUF];
result = waveInOpen(&hWaveIn, WAVE_MAPPER,&pFormat, (DWORD)myWaveInProc, 0L, CALLBACK_FUNCTION);
result = waveOutOpen(&hWaveOut, WAVE_MAPPER, &pFormat, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION);
// initialize all headers in the queue
for ( int i = 0; i < NUM_BUF; i++ )
{
_header[i].lpData = (LPSTR)&_pBuf [i * bpbuff];
_header[i].dwBufferLength = bpbuff*sizeof(*_pBuf);
_header[i].dwFlags = 0L;
_header[i].dwLoops = 0L;
}
DWORD myThreadID;
DWORD myThreadIDPlay;
HANDLE hThread;
HANDLE hThreadPlay;
hThread = CreateThread(NULL, 0, RecordingWaitingThread,NULL,0,&myThreadID);
hThreadPlay = CreateThread(NULL, 0, PlayingWaitingThread,NULL,0,&myThreadIDPlay);
_iBuf = 0;
waveInPrepareHeader(hWaveIn, & _header[_iBuf], sizeof(WAVEHDR));
waveInAddBuffer (hWaveIn, & _header[_iBuf], sizeof (WAVEHDR));
waveInStart(hWaveIn);
getchar();
waveInClose(hWaveIn);
waveOutClose(hWaveOut);
CloseHandle(hThread);
CloseHandle(hThreadPlay);
CloseHandle(hEvent_BufferReady);
CloseHandle(hEvent_FinishedPlaying);
return 0;
}