// AlexfSnd.cpp: implementation of the CAlexfSyncPlay class.
// Part of "Morse" project. (C) Alexander Fedorov, 2000. lamer2000@hotmail.com
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "AlexfSnd.h"
#include <math.h>
#include <stdio.h>
//define ALEXF_DEBUG_TRACE(buf) {FILE * f = fopen ("c:\\_trace.txt", "a"); fprintf (f, buf); fclose (f);}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CAlexfSyncPlay::CAlexfSyncPlay(BOOL bSoundOn)
{
SPS = 44100; // Samples Per Sec
dDuration = 0; // duration, sec
dFreq = 0; // frequency
wfme.wFormatTag = WAVE_FORMAT_PCM; // general format
wfme.nChannels = 1; // 1 channel
wfme.nSamplesPerSec = SPS; //
wfme.nAvgBytesPerSec = SPS; //
wfme.nBlockAlign = 1; //
wfme.wBitsPerSample = 8; //
wfme.cbSize = 0; // additional info
}
CAlexfSyncPlay::~CAlexfSyncPlay()
{
}
void CALLBACK CAlexfSyncPlay::WaveOut_waveOutProc(
HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
}
void CAlexfSyncPlay::FillSinus08(
unsigned char * pBuf,
int iBufLen,
int iSPS,
double fFreq)
{
double dKoef = 2 * 3.1416 / iSPS * fFreq;
for (int i=0; i<iBufLen; i++)
pBuf[i] = (unsigned char) (cos(i * dKoef) * 125 + 127);
}
BOOL CAlexfSyncPlay::Beep()
{
mmres = waveOutOpen(&hwout, WAVE_MAPPER, &wfme,
(DWORD) WaveOut_waveOutProc, 0x12345, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR)
{
delete [] WaveBuf;
return FALSE;
}
whdr.lpData = WaveBuf;
whdr.dwBufferLength = (DWORD) (dDuration * SPS);
// whdr.dwFlags = WHDR_BEGINLOOP;
mmres = waveOutPrepareHeader(hwout, & whdr, sizeof(whdr));
if (mmres != MMSYSERR_NOERROR)
{
mmres = waveOutClose(hwout);
delete [] WaveBuf;
return FALSE;
}
whdr.dwLoops = 0;
mmres = waveOutWrite(hwout, & whdr, sizeof(WAVEHDR));
if (mmres == MMSYSERR_NOERROR)
{
while (!(whdr.dwFlags & WHDR_DONE))
Sleep(1);
}
mmres = waveOutUnprepareHeader(hwout, & whdr, sizeof(WAVEHDR));
mmres = waveOutClose(hwout);
delete [] WaveBuf;
return TRUE;
}
BOOL CAlexfSyncPlay::Beep08(double Freq, double Delay)
{
dDuration = Delay;
dFreq = Freq;
WaveBuf = new char[(int)((double) SPS * dDuration)];
if (WaveBuf == NULL) return FALSE;
wfme.nAvgBytesPerSec = SPS;
wfme.nBlockAlign = 1;
wfme.wBitsPerSample = 8;
ZeroMemory(&whdr, sizeof(whdr));
FillSinus08((unsigned char *) WaveBuf,
(int) (dDuration * SPS), SPS, dFreq);
return Beep();
}
//////////////////////////////////////////////
//////////////////////////////////////////////
CAlexfAsyncPlay2 * CAlexfAsyncPlay2::ziz = NULL;
CAlexfAsyncPlay2::CAlexfAsyncPlay2()
{
bOK = FALSE;
ziz = this;
pcm = new CAlexfPCM[ALEXF_PLAY_BUF];
if (!pcm) return;
SPS = ALEXF_PLAY_SPS;
wfme.wFormatTag = WAVE_FORMAT_PCM;
wfme.nChannels = 1;
wfme.nSamplesPerSec = SPS;
wfme.nAvgBytesPerSec = SPS;
wfme.nBlockAlign = 1;
wfme.wBitsPerSample = 8;
wfme.cbSize = 0;
hwout = NULL;
mmres = waveOutOpen(&hwout, WAVE_MAPPER, &wfme,
(DWORD) CallbackWaveOutProc, 0x12345, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR) return;
bOK = TRUE;
}
CAlexfAsyncPlay2::~CAlexfAsyncPlay2()
{
while (GetBusyBufferNumber() > 0)
{
Sleep(1);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if (hwout != NULL) mmres = waveOutClose(hwout);
if (pcm) delete [] pcm;
}
BOOL CAlexfAsyncPlay2::AddSingleSound(long Freq, double Delay)
{
BOOL bRC = FALSE;
if (!bOK) return FALSE;
if (Delay > 1) return FALSE;
int i = GetFreeBuf();
if (i < 0) return FALSE;
bRC = TRUE;
pcm[i].lFreq = Freq;
pcm[i].dDelay = Delay;
pcm[i].whdr.dwFlags = 0;
pcm[i].CreateSinus08Sample();
mmres = waveOutPrepareHeader(hwout, & pcm[i].whdr, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); bOK = FALSE;}
mmres = waveOutWrite(hwout, & pcm[i].whdr, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); bOK = FALSE;}
return bRC;
}
void CALLBACK CAlexfAsyncPlay2::CallbackWaveOutProc(HWAVEOUT hwo,
UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
if (uMsg != WOM_DONE) return;
MMRESULT mmres;
mmres = waveOutUnprepareHeader(hwo, (WAVEHDR *) dwParam1, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); ziz->bOK = FALSE;}
ziz->ReleaseBuf((WAVEHDR *) dwParam1);
}
void CAlexfAsyncPlay2::ReleaseBuf(WAVEHDR * lpwhdr)
{
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
{
if (pcm[i].iStage == PCM_STAGE_PLAY_NOW)
if (lpwhdr == & pcm[i].whdr)
{
pcm[i].iStage = PCM_STAGE_FREE;
return;
}
}
}
int CAlexfAsyncPlay2::GetFreeBuf()
{
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
if (pcm[i].iStage == PCM_STAGE_FREE) return i;
return -1;
}
int CAlexfAsyncPlay2::GetBusyBufferNumber()
{
int iRC = 0;
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
if (pcm[i].iStage == PCM_STAGE_PLAY_NOW) iRC++;
return iRC;
}
////////////////////////////////////////////////////////////
CAlexfAsyncPlay* CAlexfAsyncPlay::ziz = NULL;
CAlexfAsyncPlay::CAlexfAsyncPlay()
{
bOK = FALSE;
ziz = this;
bStopThreadNow = FALSE;
pcm = new CAlexfPCM[ALEXF_PLAY_BUF];
if (!pcm) return;
lLastPCM = -1;
InitializeCriticalSection(&csCriticalSection);
// thread init
DWORD dwThreadID = 0;
hThread = ::CreateThread(NULL, 0,
CAlexfAsyncPlay::ThreadFunc, (LPVOID) this, CREATE_SUSPENDED, &dwThreadID);
if (hThread == NULL) {ASSERT(0); return;}
ResumeThread(hThread);
// wave init
SPS = ALEXF_PLAY_SPS;
wfme.wFormatTag = WAVE_FORMAT_PCM;
wfme.nChannels = 1;
wfme.nSamplesPerSec = SPS;
wfme.nAvgBytesPerSec = SPS;
wfme.nBlockAlign = 1;
wfme.wBitsPerSample = 8;
wfme.cbSize = 0;
hwout = NULL;
mmres = waveOutOpen(&hwout, WAVE_MAPPER, &wfme,
(DWORD) CallbackWaveOutProc, 0x12345, CALLBACK_FUNCTION);
if (mmres != MMSYSERR_NOERROR) return;
bOK = TRUE;
}
CAlexfAsyncPlay::~CAlexfAsyncPlay()
{
StopThread();
if (hwout != NULL) mmres = waveOutClose(hwout);
DeleteCriticalSection(&csCriticalSection);
if (pcm) delete [] pcm;
}
BOOL CAlexfAsyncPlay::StopThread()
{
if (!hThread) return TRUE;
bStopThreadNow = TRUE;
DWORD dw = WaitForSingleObject(hThread, 1000 * 60);
if (dw != WAIT_OBJECT_0) return FALSE;
return TRUE;
}
BOOL CAlexfAsyncPlay::AddSingleSound(long Freq, double Delay)
{
if (!bOK) return FALSE;
BOOL bRC = FALSE;
EnterCriticalSection(&csCriticalSection);
// ALEXF_DEBUG_TRACE("AddSingleSound\n");
if (lLastPCM != -1)
{
int i = lLastPCM;
if (pcm[i].dDelay + Delay <= 1)
{
bRC = TRUE;
pcm[i].FillSinus08(pcm[i].WaveBuf + (int)(pcm[i].dDelay * ALEXF_PLAY_SPS), (int)(Delay * ALEXF_PLAY_SPS),
ALEXF_PLAY_SPS, Freq);
pcm[i].dDelay += Delay;
pcm[i].whdr.dwBufferLength = (DWORD) (pcm[i].dDelay * ALEXF_PLAY_SPS);
if (pcm[i].dDelay > 0.1)
{
pcm[i].iStage = PCM_STAGE_MARK_TO_FILL;
lLastPCM = -1;
}
LeaveCriticalSection(&csCriticalSection);
return bRC;
}
}
int i = GetFreeBuf();
if ((i >= 0) && (Delay <= 1))
{
bRC = TRUE;
pcm[i].lFreq = Freq;
pcm[i].dDelay = Delay;
pcm[i].whdr.dwFlags = 0;
pcm[i].FillSinus08(pcm[i].WaveBuf, (int)(pcm[i].dDelay * ALEXF_PLAY_SPS),
ALEXF_PLAY_SPS, pcm[i].lFreq);
pcm[i].whdr.lpData = pcm[i].WaveBuf;
pcm[i].whdr.dwBufferLength = (DWORD) (pcm[i].dDelay * ALEXF_PLAY_SPS);
if (pcm[i].dDelay > 0.1)
{
pcm[i].iStage = PCM_STAGE_MARK_TO_FILL;
}
else
{
lLastPCM = i;
}
}
else
{
// ALEXF_DEBUG_TRACE("AddSingleSound - fail\n");
}
LeaveCriticalSection(&csCriticalSection);
if (!bRC) Sleep(1);
return bRC;
}
int CAlexfAsyncPlay::GetBusyBufferNumber()
{
int iRC = 0;
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
if (pcm[i].iStage == PCM_STAGE_PLAY_NOW) iRC++;
return iRC;
}
DWORD WINAPI CAlexfAsyncPlay::ThreadFunc(LPVOID lpThreadParameter)
{
BOOL bFirtTime = TRUE;
MMRESULT mmres;
ziz = (CAlexfAsyncPlay *) lpThreadParameter;
while (!ziz->bStopThreadNow)
{
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
if (ziz->pcm[i].iStage == PCM_STAGE_MARK_TO_FILL)
{
// char ccc[256]; sprintf(ccc, "Delay [%ld] %.02f\n", i, ziz->pcm[i].dDelay); ALEXF_DEBUG_TRACE(ccc);
// add buffer
EnterCriticalSection(&ziz->csCriticalSection);
mmres = waveOutPrepareHeader(ziz->hwout, & ziz->pcm[i].whdr, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); ziz->bOK = FALSE;}
mmres = waveOutWrite(ziz->hwout, & ziz->pcm[i].whdr, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); ziz->bOK = FALSE;}
ziz->pcm[i].iStage = PCM_STAGE_PLAY_NOW;
bFirtTime = FALSE;
LeaveCriticalSection(&ziz->csCriticalSection);
}
while (ziz->GetBusyBufferNumber() >= ALEXF_PLAY_BUF)
{
Sleep(1);
}
EnterCriticalSection(&ziz->csCriticalSection);
if (ziz->GetBusyBufferNumber() == 0)
if (ziz->lLastPCM != -1)
if (!bFirtTime)
{
int i = ziz->lLastPCM;
ziz->lLastPCM = -1;
mmres = waveOutPrepareHeader(ziz->hwout, & ziz->pcm[i].whdr, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); ziz->bOK = FALSE;}
mmres = waveOutWrite(ziz->hwout, & ziz->pcm[i].whdr, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); ziz->bOK = FALSE;}
ziz->pcm[i].iStage = PCM_STAGE_PLAY_NOW;
// char ccc[256]; sprintf(ccc, "Delay2 [%ld] %.02f\n", i, ziz->pcm[i].dDelay); ALEXF_DEBUG_TRACE(ccc);
}
LeaveCriticalSection(&ziz->csCriticalSection);
}
// exit
while (ziz->GetBusyBufferNumber() > 0)
{
Sleep(1);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
void CALLBACK CAlexfAsyncPlay::CallbackWaveOutProc(HWAVEOUT hwo,
UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
if (uMsg != WOM_DONE) return;
EnterCriticalSection(&ziz->csCriticalSection);
MMRESULT mmres;
mmres = waveOutUnprepareHeader(hwo, (WAVEHDR *) dwParam1, sizeof(WAVEHDR));
if (mmres != MMSYSERR_NOERROR) {ASSERT(0); ziz->bOK = FALSE;}
ziz->ReleaseBuf((WAVEHDR *) dwParam1);
// ALEXF_DEBUG_TRACE("ReleaseBuf\n");
LeaveCriticalSection(&ziz->csCriticalSection);
}
void CAlexfAsyncPlay::ReleaseBuf(WAVEHDR * lpwhdr)
{
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
{
if (pcm[i].iStage == PCM_STAGE_PLAY_NOW)
if (lpwhdr == & pcm[i].whdr)
{
pcm[i].iStage = PCM_STAGE_FREE;
}
}
}
int CAlexfAsyncPlay::GetFreeBuf()
{
for (int i = 0; i < ALEXF_PLAY_BUF; i++)
if (pcm[i].iStage == PCM_STAGE_FREE) return i;
return -1;
}
/////////////////////////////////////////
CAlexfPCM::CAlexfPCM()
{
WaveBuf = new char[ALEXF_PLAY_SPS];
if (!WaveBuf) return;
ZeroMemory(WaveBuf, sizeof(WaveBuf));
ZeroMemory(&whdr, sizeof(whdr));
iStage = PCM_STAGE_FREE;
lFreq = 0;
dDelay = 0;
lNext = -1;
}
CAlexfPCM::~CAlexfPCM()
{
if (WaveBuf) delete [] WaveBuf;
}
BOOL CAlexfPCM::ResizeBuf(long lNewSize)
{
BOOL bRC = TRUE;
if (WaveBuf) delete [] WaveBuf;
lFreq = 0;
dDelay = 0;
WaveBuf = new char[lNewSize];
if (!WaveBuf) bRC = FALSE;
else ZeroMemory(WaveBuf, lNewSize);
return bRC;
}
void CAlexfPCM::FillSinus08(
char * pBuf,
int iBufLen,
int iSPS,
double fFreq)
{
double dKoef = 2 * 3.1416 / iSPS * fFreq;
for (int i=0; i<iBufLen; i++)
pBuf[i] = (char) (cos(i * dKoef) * 125 + 127);
}
BOOL CAlexfPCM::CreateSinus08Sample()
{
FillSinus08(WaveBuf, (int)(dDelay * ALEXF_PLAY_SPS),
ALEXF_PLAY_SPS, lFreq);
whdr.lpData = WaveBuf;
whdr.dwBufferLength = (DWORD) (dDelay * ALEXF_PLAY_SPS);
iStage = PCM_STAGE_PLAY_NOW;
return TRUE;
}