You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
873 lines
20 KiB
873 lines
20 KiB
//--------------------------------------------------------------------------;
|
|
//
|
|
// File: kegrace.cpp
|
|
//
|
|
// Copyright (c) 1995 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Contents:
|
|
//
|
|
// History:
|
|
// 06/29/96 FrankYe Created
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
#define NODSOUNDSERVICETABLE
|
|
#include "dsoundi.h"
|
|
|
|
// never premix less than this
|
|
#define MIN_PREMIX 45
|
|
|
|
#pragma VxD_LOCKED_CODE_SEG
|
|
#pragma VxD_LOCKED_DATA_SEG
|
|
|
|
extern "C" void KeGrace_GlobalTimeOutProcAsm();
|
|
|
|
LONG lMixerMutex;
|
|
|
|
LONG glNum;
|
|
DWORDLONG gdwlTotalWasted;
|
|
DWORDLONG gdwlTotal;
|
|
DWORDLONG _inline GetPentiumCounter(void)
|
|
{
|
|
_asm _emit 0x0F
|
|
_asm _emit 0x31
|
|
}
|
|
|
|
ULONG VXDINLINE VMM_Get_System_Time(void)
|
|
{
|
|
ULONG Time;
|
|
|
|
Touch_Register(eax);
|
|
VMMCall(Get_System_Time);
|
|
_asm mov Time, eax;
|
|
return Time;
|
|
}
|
|
|
|
VOID _VMCPD_Get_Thread_State(PTCB Thread, PVOID pCPState)
|
|
{
|
|
_asm mov esi, pCPState;
|
|
_asm mov edi, Thread;
|
|
VxDCall(VMCPD_Get_Thread_State);
|
|
}
|
|
|
|
VOID _VMCPD_Set_Thread_State(PTCB Thread, PVOID pCPState)
|
|
{
|
|
_asm mov esi, pCPState;
|
|
_asm mov edi, Thread;
|
|
VxDCall(VMCPD_Set_Thread_State);
|
|
}
|
|
|
|
LONG _InterlockedExchange(PLONG pTarget, LONG Value)
|
|
{
|
|
LONG OldTarget;
|
|
_asm push edi;
|
|
_asm mov eax, Value;
|
|
_asm mov edi, pTarget;
|
|
_asm xchg [edi], eax;
|
|
_asm mov OldTarget, eax;
|
|
_asm pop edi;
|
|
return OldTarget;
|
|
}
|
|
|
|
// Must be in locked code
|
|
LONG _InterlockedExchangeAdd(PLONG pAddend, LONG Increment)
|
|
{
|
|
LONG OldAddend;
|
|
_asm mov esi, pAddend;
|
|
_asm mov ecx, Increment;
|
|
_asm mov eax, [esi]; // Read it (possibly causing a fault in)
|
|
_asm add ecx, eax;
|
|
_asm mov [esi], ecx;
|
|
_asm mov OldAddend, eax;
|
|
return OldAddend;
|
|
}
|
|
|
|
VOID _ZeroMemory(PVOID pDestination, DWORD cbLength)
|
|
{
|
|
_asm mov edi, pDestination ;
|
|
_asm mov esi, cbLength ;
|
|
_asm xor eax, eax ;
|
|
_asm mov ecx, esi ;
|
|
_asm shr ecx, 2 ;
|
|
_asm rep stosd ;
|
|
_asm mov ecx, esi ;
|
|
_asm and ecx, 3 ;
|
|
_asm rep stosb ;
|
|
}
|
|
|
|
// Override the global new and delete operators
|
|
void * ::operator new(size_t size)
|
|
{
|
|
return MemAlloc(size);
|
|
}
|
|
|
|
void ::operator delete(void * pv)
|
|
{
|
|
MemFree(pv);
|
|
}
|
|
|
|
// Implement our own purecall
|
|
int __cdecl _purecall(void)
|
|
{
|
|
ASSERT(FALSE);
|
|
return 0;
|
|
}
|
|
|
|
typedef struct tEVENTPARAMS {
|
|
HTIMEOUT hEvent;
|
|
class CKeGrace *pThis;
|
|
} EVENTPARAMS, *PEVENTPARAMS;
|
|
|
|
class CKeGrace : public CGrace {
|
|
public:
|
|
HRESULT Initialize(CGrDest *pGrDest);
|
|
void Terminate(void);
|
|
void SignalRemix(void);
|
|
int GetMaxRemix(void);
|
|
void GlobalTimeOutProc(int dtimeTardiness);
|
|
|
|
private:
|
|
static const int MIXER_MINPREMIX;
|
|
static const int MIXER_MAXPREMIX;
|
|
LONG m_dtimePremix;
|
|
LONG m_ddtimePremix;
|
|
EVENTPARAMS m_EventParams;
|
|
LONG m_timeBusyWaitForMutex;
|
|
};
|
|
|
|
const int CKeGrace::MIXER_MINPREMIX = 45;
|
|
const int CKeGrace::MIXER_MAXPREMIX = 200;
|
|
|
|
extern "C" void KeGrace_GlobalTimeOutProc(PVOID pKeGrace, int dtimeTardiness)
|
|
{
|
|
((CKeGrace*)pKeGrace)->GlobalTimeOutProc(dtimeTardiness);
|
|
}
|
|
|
|
void CKeGrace::SignalRemix()
|
|
{
|
|
HTIMEOUT hEvent;
|
|
|
|
#if 0
|
|
// If you wanna run without remixing, just enable this bit of code. You
|
|
// might want to lower the MIXER_MAXPREMIX constant as well.
|
|
m_fdwMixerSignal &= DSMIXERSIGNAL_REMIX;
|
|
return;
|
|
#endif
|
|
|
|
if (!(m_fdwMixerSignal & DSMIXERSIGNAL_REMIX))
|
|
{
|
|
m_fdwMixerSignal |= DSMIXERSIGNAL_REMIX;
|
|
|
|
// Set a new time out for 2ms, and then cancel any prior pending timeout.
|
|
//
|
|
// Note that "2ms" is somewhat arbitrary. It just needs to be
|
|
// enough time to hopefully let this thread release the mixer
|
|
// mutex before the event executes.
|
|
|
|
hEvent = Set_Global_Time_Out(KeGrace_GlobalTimeOutProcAsm, 2, (ULONG)&m_EventParams);
|
|
hEvent = _InterlockedExchange((PLONG)&m_EventParams.hEvent, hEvent);
|
|
Cancel_Time_Out(hEvent);
|
|
}
|
|
}
|
|
|
|
void CKeGrace::GlobalTimeOutProc(int dtimeTardiness)
|
|
{
|
|
char CPState[108]; // Size of fp state per Intel prog. ref.
|
|
LONG dtime;
|
|
LONG dtimeSleep;
|
|
LONG dtimeInvalid;
|
|
LONG dtimeNextNotify;
|
|
int cSamplesPremixMax;
|
|
int cSamplesPremixed;
|
|
|
|
// DPF(("CKeGrace::GlobalTimeOutProc"));
|
|
|
|
if (m_dtimePremix/2 < dtimeTardiness) {
|
|
DPF(("CKeGrace_GlobalTimeOutProc : warning: %dms late", dtimeTardiness));
|
|
}
|
|
|
|
//
|
|
// We busy wait on the mutex, each iteration of the wait is longer
|
|
// than the previous.
|
|
//
|
|
if (_InterlockedExchange(&lMixerMutex, TRUE)) {
|
|
HTIMEOUT hEvent;
|
|
LONG timeOut;
|
|
// DPF(("CKeGrace::GlobalTimeOutProc : note: mutex already owned"));
|
|
timeOut = _InterlockedExchangeAdd(&m_timeBusyWaitForMutex, 1);
|
|
hEvent = Set_Global_Time_Out(KeGrace_GlobalTimeOutProcAsm, timeOut,
|
|
(ULONG)&m_EventParams);
|
|
hEvent = _InterlockedExchange((PLONG)&m_EventParams.hEvent, hEvent);
|
|
Cancel_Time_Out(hEvent);
|
|
return;
|
|
}
|
|
m_timeBusyWaitForMutex = 1;
|
|
|
|
// Three cases:
|
|
// 1) mixer is stopped
|
|
// 2) mixer running and a remix is pending
|
|
// 3) mixer running and no remix is pending
|
|
//
|
|
// Around each call to Refresh we need to save and restore the thread's
|
|
// floating point state using the VMCPD Get/Set_Thread_State services.
|
|
//
|
|
|
|
if (MIXERSTATE_STOPPED == m_kMixerState) {
|
|
|
|
dtimeSleep = 1000; // arbitrarily set for 1 second
|
|
|
|
} else {
|
|
|
|
// DWORDLONG dwlStartCycle;
|
|
// DWORDLONG dwlT;
|
|
// dwlStartCycle = dwlT = GetPentiumCounter();
|
|
|
|
dtime = VMM_Get_System_Time();
|
|
|
|
_ZeroMemory(&CPState, sizeof(CPState));
|
|
_VMCPD_Get_Thread_State(Get_Cur_Thread_Handle(), &CPState);
|
|
|
|
// gdwlTotalWasted += GetPentiumCounter() - dwlT;
|
|
// glNum++;
|
|
|
|
if (m_fdwMixerSignal & DSMIXERSIGNAL_REMIX) {
|
|
|
|
m_dtimePremix = MIXER_MINPREMIX; // Initial premix length
|
|
m_ddtimePremix = 2; // increment
|
|
|
|
cSamplesPremixMax = MulDivRD(m_dtimePremix, m_pDest->m_nFrequency, 1000);
|
|
Refresh(TRUE, cSamplesPremixMax, &cSamplesPremixed, &dtimeNextNotify);
|
|
} else {
|
|
|
|
m_dtimePremix += m_ddtimePremix;
|
|
if (m_dtimePremix > MIXER_MAXPREMIX) {
|
|
m_dtimePremix = MIXER_MAXPREMIX;
|
|
} else {
|
|
m_ddtimePremix += 2;
|
|
}
|
|
|
|
cSamplesPremixMax = MulDivRD(m_dtimePremix, m_pDest->m_nFrequency, 1000);
|
|
Refresh(FALSE, cSamplesPremixMax, &cSamplesPremixed, &dtimeNextNotify);
|
|
}
|
|
|
|
// dwlT = GetPentiumCounter();
|
|
|
|
_VMCPD_Set_Thread_State(Get_Cur_Thread_Handle(), &CPState);
|
|
|
|
dtimeInvalid = MulDivRD(cSamplesPremixed, 1000, m_pDest->m_nFrequency);
|
|
dtime = VMM_Get_System_Time() - dtime;
|
|
dtimeInvalid -= 2 * dtime;
|
|
|
|
dtimeSleep = min(dtimeNextNotify, dtimeInvalid/2);
|
|
dtimeSleep = max(dtimeSleep, MIXER_MINPREMIX/2);
|
|
|
|
// gdwlTotalWasted += GetPentiumCounter() - dwlT;
|
|
// gdwlTotal += GetPentiumCounter() - dwlStartCycle;
|
|
|
|
}
|
|
|
|
// DPF(("CKeGrace::GlobalTimeOutProc : note: dtimeSleep=%dms", dtimeSleep));
|
|
ASSERT(!m_EventParams.hEvent);
|
|
m_EventParams.hEvent = Set_Global_Time_Out(KeGrace_GlobalTimeOutProcAsm, dtimeSleep, (ULONG)&m_EventParams);
|
|
|
|
_InterlockedExchange(&lMixerMutex, FALSE);
|
|
}
|
|
|
|
HRESULT CKeGrace::Initialize(CGrDest *pDest)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = CGrace::Initialize(pDest);
|
|
if (S_OK != hr) return hr;
|
|
|
|
DPF(("CKeGrace::Initialize : note: Setting up first GlobalTimeOut"));
|
|
|
|
// If we want to run the timer really fast, do this. So far I haven't seen
|
|
// any empirical evidence of this helping.
|
|
VTD_Begin_Min_Int_Period(5);
|
|
|
|
m_dtimePremix = MIXER_MINPREMIX; // Initial premix length
|
|
m_ddtimePremix = 2; // increment
|
|
|
|
// REMIND do error check
|
|
m_timeBusyWaitForMutex = 1;
|
|
m_EventParams.pThis = this;
|
|
m_EventParams.hEvent = Set_Global_Time_Out(KeGrace_GlobalTimeOutProcAsm, 1, (ULONG)&m_EventParams);
|
|
|
|
gdwlTotal = 0;
|
|
gdwlTotalWasted = 0;
|
|
glNum = 0;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------;
|
|
//
|
|
// Terminate
|
|
//
|
|
// This function is called to terminate the grace mixer thread for the
|
|
// specified ds object. It returns the handle to the thread that is being
|
|
// terminated. After releasing any critical sections that the grace mixer
|
|
// thread may be waiting on, the caller should wait for the thread handle
|
|
// to become signaled. For Win32 beginners: the thread handle is signalled
|
|
// after the thread terminates.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
|
|
void CKeGrace::Terminate()
|
|
{
|
|
HTIMEOUT hEvent;
|
|
|
|
hEvent = _InterlockedExchange((PLONG)&m_EventParams.hEvent, 0);
|
|
Cancel_Time_Out(hEvent);
|
|
|
|
CGrace::Terminate();
|
|
|
|
if (0 != glNum) {
|
|
DPF(("Wasted time = %d cycles", (int)(gdwlTotalWasted / glNum)));
|
|
DPF(("Total time = %d cycles", (int)(gdwlTotal / glNum)));
|
|
}
|
|
}
|
|
|
|
int CKeGrace::GetMaxRemix(void)
|
|
{
|
|
// return max number of samples we might remix
|
|
return (MulDivRU(MIXER_MAXPREMIX, m_pDest->m_nFrequency, 1000));
|
|
}
|
|
|
|
|
|
#pragma VxD_PAGEABLE_CODE_SEG
|
|
#pragma VxD_PAGEABLE_DATA_SEG
|
|
|
|
class CKeGrDest : public CGrDest {
|
|
public:
|
|
CKeGrDest(LPNAGRDESTDATA);
|
|
HRESULT Initialize(void);
|
|
void Terminate(void);
|
|
HRESULT SetFormat(LPWAVEFORMATEX pwfx);
|
|
HRESULT AllocMixer(CMixer **ppMixer);
|
|
void FreeMixer(void);
|
|
HRESULT GetSamplePosition(int *pposPlay, int *pposWrite);
|
|
HRESULT GetSamplePositionNoWin16(int *pposPlay, int *pposWrite);
|
|
HRESULT Lock(PVOID *ppBuffer1, int *pcbBuffer1, PVOID *ppBuffer2, int *pcbBuffer2, int ibWrite, int cbWrite);
|
|
HRESULT Unlock(PVOID pBuffer1, int cbBuffer1, PVOID pBuffer2, int cbBuffer2);
|
|
void Play();
|
|
void Stop();
|
|
|
|
private:
|
|
CKeGrace* m_pKeGrace;
|
|
DWORD m_fdwDriverDesc;
|
|
CBuf* m_pDrvBuf;
|
|
// Let's only send a stop if we are currently playing
|
|
BOOL m_fStopped;
|
|
};
|
|
|
|
CKeGrDest::CKeGrDest(LPNAGRDESTDATA pData)
|
|
{
|
|
m_cbBuffer = pData->cbBuffer;
|
|
m_pBuffer = pData->pBuffer;
|
|
m_pDrvBuf = ((CBuf*)((PIDSDRIVERBUFFER)pData->hBuffer));
|
|
m_fdwDriverDesc = pData->fdwDriverDesc;
|
|
m_fStopped = TRUE;
|
|
}
|
|
|
|
HRESULT CKeGrDest::Initialize(void)
|
|
{
|
|
m_cSamples = m_cbBuffer >> m_nBlockAlignShift;
|
|
return DS_OK;
|
|
}
|
|
|
|
void CKeGrDest::Terminate(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
HRESULT CKeGrDest::AllocMixer(CMixer **ppMixer)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ASSERT(m_pBuffer);
|
|
|
|
*ppMixer = NULL;
|
|
|
|
m_pKeGrace = new CKeGrace;
|
|
if (m_pKeGrace) {
|
|
hr = m_pKeGrace->Initialize(this);
|
|
if (S_OK != hr) {
|
|
delete m_pKeGrace;
|
|
m_pKeGrace = NULL;
|
|
}
|
|
} else {
|
|
hr = DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
if (S_OK == hr) *ppMixer = m_pKeGrace;
|
|
return hr;
|
|
}
|
|
|
|
void CKeGrDest::FreeMixer()
|
|
{
|
|
ASSERT(m_pKeGrace);
|
|
|
|
m_pKeGrace->Terminate();
|
|
delete m_pKeGrace;
|
|
m_pKeGrace = NULL;
|
|
}
|
|
|
|
HRESULT CKeGrDest::SetFormat(LPWAVEFORMATEX pwfx)
|
|
{
|
|
HRESULT hr;
|
|
|
|
SetFormatInfo(pwfx);
|
|
hr = m_pDrvBuf->SetFormat(pwfx);
|
|
return hr;
|
|
}
|
|
|
|
void CKeGrDest::Play()
|
|
{
|
|
HRESULT hr;
|
|
// REMIND we're not propagating errors here!
|
|
hr = m_pDrvBuf->Play(0, 0, DSBPLAY_LOOPING);
|
|
if (SUCCEEDED(hr)) m_fStopped = FALSE;
|
|
}
|
|
|
|
void CKeGrDest::Stop()
|
|
{
|
|
HRESULT hr;
|
|
if (m_fStopped == FALSE)
|
|
{
|
|
hr = m_pDrvBuf->Stop();
|
|
if (SUCCEEDED(hr)) m_fStopped = TRUE;
|
|
}
|
|
}
|
|
|
|
HRESULT CKeGrDest::Lock(PVOID *ppBuffer1, int *pcbBuffer1, PVOID *ppBuffer2, int *pcbBuffer2, int ibWrite, int cbWrite)
|
|
{
|
|
LOCKCIRCULARBUFFER lcb;
|
|
HRESULT hr;
|
|
|
|
lcb.pHwBuffer = m_pDrvBuf;
|
|
lcb.pvBuffer = m_pBuffer;
|
|
lcb.cbBuffer = m_cbBuffer;
|
|
lcb.fPrimary = TRUE;
|
|
lcb.fdwDriverDesc = m_fdwDriverDesc;
|
|
lcb.ibRegion = ibWrite;
|
|
lcb.cbRegion = cbWrite;
|
|
|
|
hr = LockCircularBuffer(&lcb);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
*ppBuffer1 = lcb.pvLock[0];
|
|
*pcbBuffer1 = lcb.cbLock[0];
|
|
|
|
if(ppBuffer2)
|
|
{
|
|
*ppBuffer2 = lcb.pvLock[1];
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!lcb.pvLock[1]);
|
|
}
|
|
|
|
if(pcbBuffer2)
|
|
{
|
|
*pcbBuffer2 = lcb.cbLock[1];
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!lcb.cbLock[1]);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CKeGrDest::Unlock(PVOID pBuffer1, int cbBuffer1, PVOID pBuffer2, int cbBuffer2)
|
|
{
|
|
LOCKCIRCULARBUFFER lcb;
|
|
|
|
lcb.pHwBuffer = m_pDrvBuf;
|
|
lcb.pvBuffer = m_pBuffer;
|
|
lcb.cbBuffer = m_cbBuffer;
|
|
lcb.fPrimary = TRUE;
|
|
lcb.fdwDriverDesc = m_fdwDriverDesc;
|
|
lcb.pvLock[0] = pBuffer1;
|
|
lcb.cbLock[0] = cbBuffer1;
|
|
lcb.pvLock[1] = pBuffer2;
|
|
lcb.cbLock[1] = cbBuffer2;
|
|
|
|
return UnlockCircularBuffer(&lcb);
|
|
}
|
|
|
|
HRESULT CKeGrDest::GetSamplePosition(int *pposPlay, int *pposWrite)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwPlay, dwWrite;
|
|
|
|
ASSERT(pposPlay && pposWrite);
|
|
|
|
hr = m_pDrvBuf->GetPosition(&dwPlay, &dwWrite);
|
|
if (S_OK == hr) {
|
|
|
|
*pposPlay = dwPlay >> m_nBlockAlignShift;
|
|
*pposWrite = dwWrite >> m_nBlockAlignShift;
|
|
|
|
// Until we write code to actually profile the performance, we'll just
|
|
// pad the write position with a hard coded amount
|
|
*pposWrite += m_nFrequency * HW_WRITE_CURSOR_MSEC_PAD / 1024;
|
|
if (*pposWrite >= m_cSamples) *pposWrite -= m_cSamples;
|
|
ASSERT(*pposWrite < m_cSamples);
|
|
|
|
} else {
|
|
*pposPlay = *pposWrite = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT CKeGrDest::GetSamplePositionNoWin16(int *pposPlay, int *pposWrite)
|
|
{
|
|
return GetSamplePosition(pposPlay, pposWrite);
|
|
}
|
|
|
|
int ioctlMixer_Run(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
HRESULT hr;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
|
|
hr = pMixer->Run();
|
|
|
|
IOOUTPUT(hr, HRESULT);
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_Stop(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
BOOL f;
|
|
CMixer *pMixer;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
|
|
f = pMixer->Stop();
|
|
|
|
IOOUTPUT(f, BOOL);
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_PlayWhenIdle(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
|
|
pMixer->PlayWhenIdle();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_StopWhenIdle(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
|
|
pMixer->StopWhenIdle();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_MixListAdd(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
CMixSource *pSource;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
IOINPUT(pSource, CMixSource*);
|
|
|
|
pMixer->MixListAdd(pSource);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_MixListRemove(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
CMixSource *pSource;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
IOINPUT(pSource, CMixSource*);
|
|
|
|
pMixer->MixListRemove(pSource);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_FilterOn(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
CMixSource *pSource;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
IOINPUT(pSource, CMixSource*);
|
|
|
|
pMixer->FilterOn(pSource);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_FilterOff(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
CMixSource *pSource;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
IOINPUT(pSource, CMixSource*);
|
|
|
|
pMixer->FilterOff(pSource);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_GetBytePosition(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
CMixSource *pSource;
|
|
int *pibPlay;
|
|
int *pibWrite;
|
|
|
|
IOSTART(4*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
IOINPUT(pSource, CMixSource*);
|
|
IOINPUT(pibPlay, int*);
|
|
IOINPUT(pibWrite, int*);
|
|
|
|
pMixer->GetBytePosition(pSource, pibPlay, pibWrite);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixer_SignalRemix(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixer *pMixer;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixer, CMixer*);
|
|
|
|
pMixer->SignalRemix();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ioctlKeDest_New(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
LPNAGRDESTDATA pData;
|
|
CKeGrDest *pKeGrDest;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pData, LPNAGRDESTDATA);
|
|
|
|
pKeGrDest = new CKeGrDest(pData);
|
|
|
|
IOOUTPUT(pKeGrDest, CKeGrDest*);
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_Delete(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
delete pMixDest;
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_Initialize(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
HRESULT hr;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
hr = pMixDest->Initialize();
|
|
|
|
IOOUTPUT(hr, HRESULT);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_Terminate(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
pMixDest->Terminate();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_SetFormat(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
LPWAVEFORMATEX pwfx;
|
|
HRESULT hr;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
IOINPUT(pwfx, LPWAVEFORMATEX);
|
|
|
|
hr = pMixDest->SetFormat(pwfx);
|
|
|
|
IOOUTPUT(hr, HRESULT);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_SetFormatInfo(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
LPWAVEFORMATEX pwfx;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
IOINPUT(pwfx, LPWAVEFORMATEX);
|
|
|
|
pMixDest->SetFormatInfo(pwfx);
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_AllocMixer(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
CMixer **ppMixer;
|
|
HRESULT hr;
|
|
|
|
IOSTART(2*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
IOINPUT(ppMixer, CMixer**);
|
|
|
|
hr = pMixDest->AllocMixer(ppMixer);
|
|
|
|
IOOUTPUT(hr, HRESULT);
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_FreeMixer(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
pMixDest->FreeMixer();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_Play(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
pMixDest->Play();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_Stop(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
pMixDest->Stop();
|
|
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlMixDest_GetFrequency(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
CMixDest *pMixDest;
|
|
int nFrequency;
|
|
|
|
IOSTART(1*4);
|
|
|
|
IOINPUT(pMixDest, CMixDest*);
|
|
|
|
nFrequency = pMixDest->GetFrequency();
|
|
|
|
IOOUTPUT(nFrequency, int);
|
|
IORETURN;
|
|
return 0;
|
|
}
|
|
|
|
int ioctlDsvxd_GetMixerMutexPtr(PDIOCPARAMETERS pdiocp)
|
|
{
|
|
IOSTART(0*4);
|
|
IOOUTPUT(&lMixerMutex, PLONG);
|
|
IORETURN;
|
|
return 0;
|
|
}
|