Пытаюсь освоить функцию CreateTimerQueueTimer. Замечательная функция, даёт достаточно жёсткую задержку, но непонятные глюки преследуют меня если я начинаю её использовать. Вот например, программа ввода вывода звука.
Вызов моей функции InitTimer() проводит к запуску таймера с периодичностью 10 милисекунд. В таймере забирается звук из звуковой карты с помощью и помещается в цикличекий буффер.
Microphone.Run();
Затем отдаётся на воспроизведение из этого буффера
Speaker.Run();
Глючность не зависит от производительности компьютера. Например на 150 Мгц ноутбуке Compaq Pressario замечательно работает. На настольном Celeron 350 похрюкивает, на других машинах 2 ГГЦ может поиграть 10 секунд и вообще зависнуть.
С помощью Soft-Ice нажимая cntr-D можно посмотреть, что в состоянии зависания всё время поподаешь в одно и то же место — какой то вечный цикл из 5 комманд.
Друзья, попробуйте вставьте в приложение Hellow World! InitTimer() и послушайте пошёл ли звук микрофона в динамик с небольшой задержкой. И не забудьте втавить winmm.lib . Помогите заставить работать без глюков, хрюков и зависаний замечательную функцию CreateTimerQueueTimer на любых компьютерах.
Вот файл QTIMER.CPP
#include "stdafx.h"
#include "devices.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define WT_EXECUTEDEFAULT 0x00000000
#define WT_EXECUTEINIOTHREAD 0x00000001
#define WT_EXECUTEINUITHREAD 0x00000002
#define WT_EXECUTEINWAITTHREAD 0x00000004
#define WT_EXECUTEONLYONCE 0x00000008
#define WT_EXECUTEINTIMERTHREAD 0x00000020
#define WT_EXECUTELONGFUNCTION 0x00000010
#define WT_EXECUTEINPERSISTENTIOTHREAD 0x00000040
#define WT_EXECUTEINPERSISTENTTHREAD 0x00000080
typedef VOID (NTAPI * WAITORTIMERCALLBACK) (PVOID, BOOLEAN );
typedef WINBASEAPI HANDLE WINAPI
pCreateTimerQueueFunc();
typedef WINBASEAPI BOOL WINAPI
pCreateTimerQueueTimerFunc(
PHANDLE phNewTimer,
HANDLE TimerQueue,
WAITORTIMERCALLBACK Callback,
PVOID Parameter,
DWORD DueTime,
DWORD Period,
ULONG Flags
);
pCreateTimerQueueFunc *CreateTimerQueueFunc;
pCreateTimerQueueTimerFunc *CreateTimerQueueTimerFunc;
HINSTANCE Lib;
HANDLE TimerQueue;
HANDLE m_timerHandle;
waveInDevice<short int> Microphone;
waveOutDevice<short int> Speaker;
SinGenerator<short int> SinGen;
HANDLE hThread;
void CALLBACK TimerProc(void* lpParametar, BOOLEAN TimerOrWaitFired)
{
static start=1;
Microphone.Run();
//SinGen.Run();
if (start!=3)
{
if (start==1)
{
SetThreadPriority( GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL);
start=2;
goto end;
return;
}
if (start==2)
{ Speaker.Connect(&Microphone.Output);
//Speaker.Connect(&SinGen.Output);
start=3;
goto end;
return;
}
}
else
Speaker.Run();
end:
;
};
void InitTimer()
{
Lib=LoadLibrary("kernel32.dll");
CreateTimerQueueFunc=(pCreateTimerQueueFunc*)GetProcAddress(Lib,"CreateTimerQueue");
TimerQueue=(*CreateTimerQueueFunc)();
CreateTimerQueueTimerFunc=(pCreateTimerQueueTimerFunc*)GetProcAddress(Lib,"CreateTimerQueueTimer");
BOOL success = (*CreateTimerQueueTimerFunc)(
&m_timerHandle,
TimerQueue,
TimerProc,
NULL,//this
50,
//1000,
10, //10 msec - period
WT_EXECUTEINTIMERTHREAD
);
};
А вот файл devices.h . MSVC 5.0 может давть ошибку не везде стоит return. Но шестая студия глотает.
#ifndef devices_h
#define devices_h
class Device
{ virtual bool Run(){return true;};
};
template <class T>
struct CircBufOut
{ public:
T * Start;
T * End;
T * Cursor;
};
template <class T>
class InputDevice :public Device
{ public:
CircBufOut <T> Output;
};
template <class T>
class OutputDevice :public Device
{ public:
virtual void Connect(CircBufOut <T> *input){};
};
template <class Tin, class Tout>
class InOutDevice : public InputDevice<Tin> , public OutputDevice<Tout>
{
};
#include <windows.h>
#include <mmsystem.h>
template <class Tdata>
class
waveInDevice : public InputDevice <Tdata>
{
private:
CHAR outbuf[65536];
HWAVEIN WaveInHnd;
WAVEFORMATEX fmt;
MMRESULT Res;
DWORD EndPos;
DWORD nblock;
public:
char ERR[200];
waveInDevice()
{ UINT NumDev=waveInGetNumDevs();
{ fmt.wFormatTag=WAVE_FORMAT_PCM;
fmt.nChannels=1;
fmt.nSamplesPerSec=8000;
fmt.nAvgBytesPerSec=sizeof(Tdata)*8000;
fmt.nBlockAlign=2;
fmt.wBitsPerSample=sizeof(Tdata)*8;
fmt.cbSize=0;
Res=waveInOpen(&WaveInHnd,
WAVE_MAPPER,
&fmt,
0,
CALLBACK_NULL,
0
);
if (Res==0)
{
Output.Start=(Tdata*)outbuf;
Output.Cursor=Output.Start;
Output.End=(Tdata*)(outbuf+65536);
EndPos=0;
nblock=0;
waveInStart(WaveInHnd);
}
else
{ waveInGetErrorText(Res,ERR,200);
}
}
};
~waveInDevice()
{ if (WaveInHnd!=NULL)
waveInClose(WaveInHnd);
}
WAVEHDR hdr[4];
bool Run()
{ MMTIME mmt;
mmt.wType=TIME_BYTES;
MMRESULT result=waveInGetPosition(WaveInHnd,&mmt,sizeof(mmt));
DWORD pos=mmt.u.cb;
Output.Cursor=(Tdata*)((DWORD)Output.Start+(DWORD)(pos & 0xFFFF));
if ((EndPos-pos)<16384)
{ MMRESULT R;
LPSTR pdata=outbuf+nblock*16384;
hdr[nblock].lpData=pdata;
hdr[nblock].dwBufferLength=16384;
hdr[nblock].dwBytesRecorded=0;
hdr[nblock].dwUser=0;
hdr[nblock].dwFlags=0;
hdr[nblock].dwLoops=0;
hdr[nblock].lpNext=0;
hdr[nblock].reserved=0;
R=waveInPrepareHeader(WaveInHnd,&hdr[nblock],sizeof(WAVEHDR) );
R=waveInAddBuffer(WaveInHnd,&hdr[nblock], sizeof(WAVEHDR) );
nblock++;
EndPos+=16384;
if (nblock>=4) nblock-=4;
}
return Res;//get Res from waveInOpen
};
};
extern UINT WaveOutBuzyNumDev;
template <class Tdata>
class waveOutDevice : public OutputDevice < Tdata >
{
private:
HWAVEOUT WaveOutHnd;
WAVEFORMATEX fmt;
MMRESULT Res;
CircBufOut <Tdata> * Input;
CircBufOut <Tdata> LastInput;
public:
char ERR[200];
char buf[65536];
waveOutDevice()
{ UINT NumDev=waveOutGetNumDevs();
{ fmt.wFormatTag=WAVE_FORMAT_PCM;
fmt.nChannels=1;
fmt.nSamplesPerSec=8000;
fmt.nAvgBytesPerSec=sizeof(Tdata)*8000;
fmt.nBlockAlign=sizeof(Tdata);
fmt.wBitsPerSample=sizeof(Tdata)*8;
fmt.cbSize=0;
Res=waveOutOpen(&WaveOutHnd,
WAVE_MAPPER,
&fmt,
0,
0,
0
);
if (Res==0)
{Input=NULL;
nh=0;
}
else
{ waveInGetErrorText(Res,ERR,200);
}
}
};
~waveOutDevice()
{ if (WaveOutHnd!=NULL)
{waveOutClose(WaveOutHnd);
}
}
void Connect(CircBufOut <Tdata> * pd)
{ Input=pd;
LastInput=*Input;
// waveOutReset(WaveOutHnd);
};
WAVEHDR hdr[20];
int nh;
bool Run()
{ int nd;
Tdata *NP;
NP=Input->Cursor;
MMRESULT R;
if (NP>LastInput.Cursor)
{
hdr[nh].lpData=(LPSTR)LastInput.Cursor;
hdr[nh].dwBufferLength=((DWORD)NP-(DWORD)LastInput.Cursor);
hdr[nh].dwBytesRecorded=0;
hdr[nh].dwUser=0;
hdr[nh].dwFlags=0;
hdr[nh].dwLoops=0;
hdr[nh].lpNext=0;
hdr[nh].reserved=0;
R=waveOutPrepareHeader(WaveOutHnd,&hdr[nh],sizeof(WAVEHDR));
waveOutWrite(WaveOutHnd,&hdr[nh], sizeof(WAVEHDR) );
LastInput.Cursor=NP;
nh++;
if (nh>=20) nh-=20;
return true;
}
if (NP<LastInput.Cursor)
{ // waveOutReset(WaveOutHnd);
memcpy(buf,NP,(Input->End-NP));
memcpy(buf+(Input->End-NP),Input->Start,(NP-Input->Start));
hdr[nh].lpData=(LPSTR)buf;
hdr[nh].dwBufferLength=((DWORD)LastInput.End-(DWORD)LastInput.Cursor)+
((DWORD)NP-(DWORD)LastInput.Start);
hdr[nh].dwBytesRecorded=0;
hdr[nh].dwUser=0;
hdr[nh].dwFlags=0;
hdr[nh].dwLoops=0;
hdr[nh].lpNext=0;
hdr[nh].reserved=0;
R=waveOutPrepareHeader(WaveOutHnd,&hdr[nh],sizeof(WAVEHDR));
waveOutWrite(WaveOutHnd,&hdr[nh], sizeof(WAVEHDR) );
nh++;
if (nh>=20) nh-=20;
LastInput.Cursor=NP;
return true;
}
};
};
#include <math.h>
#define M_PI (3.141592653589793238462)
template <class Tdata>
class SinGenerator : public InputDevice < Tdata >
{ Tdata outbuf[64000];
int step;
public:
SinGenerator()
{ int i;
for(i=0;i<64000;i++)
{ if (sizeof(Tdata)==2)
{ outbuf[i]=32767.*sin(2*M_PI*i*1000./8000.0);//1000 Hz
}
}
step=1;
Output.Start=outbuf;
Output.End=outbuf+64000;
Output.Cursor=outbuf;
}
bool Run()
{ Output.Cursor+=1000; //0.125 sec = 1000/8000
if (Output.Cursor>=Output.End)
Output.Cursor-=64000;
return true;
}
};
#endif