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.
4348 lines
100 KiB
4348 lines
100 KiB
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Module: tswave.c
|
|
//
|
|
// Purpose: User-mode driver for terminal server
|
|
// sound redirection
|
|
//
|
|
// Copyright(C) Microsoft Corporation 2000
|
|
//
|
|
// History: 4-10-2000 vladimis [created]
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
#include "rdpsnd.h"
|
|
#include <winsta.h>
|
|
|
|
#define TSSND_NATIVE_XLATERATE ( TSSND_NATIVE_BLOCKALIGN * TSSND_NATIVE_SAMPLERATE )
|
|
#define MIXER_OBJECTF_TYPEMASK 0xF0000000L // internal
|
|
enum {
|
|
RDP_MXDID_MUTE = 0,
|
|
RDP_MXDID_VOLUME,
|
|
RDP_MXDID_LAST
|
|
};
|
|
|
|
#define ENTER_CRIT EnterCriticalSection(&g_cs);
|
|
#define LEAVE_CRIT LeaveCriticalSection(&g_cs);
|
|
|
|
//
|
|
// Global queue containing all queued wave headers
|
|
// guarded by ENTER_CRIT LEAVE_CRIT macros
|
|
//
|
|
PWAVEOUTCTX g_pAllWaveOut = NULL;
|
|
HANDLE g_hMixerEvent = NULL;
|
|
CRITICAL_SECTION g_cs;
|
|
MIXERCTX g_Mixer;
|
|
|
|
//
|
|
// Stream data
|
|
//
|
|
HANDLE g_hWaitToInitialize = NULL;
|
|
HANDLE g_hDataReadyEvent = NULL;
|
|
HANDLE g_hStreamIsEmptyEvent = NULL;
|
|
HANDLE g_hStreamMutex = NULL;
|
|
HANDLE g_hStream = NULL;
|
|
PSNDSTREAM g_Stream = NULL;
|
|
BOOL g_bMixerRunning = TRUE;
|
|
|
|
DWORD
|
|
waveRestart(
|
|
PWAVEOUTCTX pWaveOut
|
|
);
|
|
|
|
BOOL
|
|
_waveCheckSoundAlive(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
_waveAcquireStream(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
_waveReleaseStream(
|
|
VOID
|
|
);
|
|
|
|
VOID Place8kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place8kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place8kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place8kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
|
|
VOID Place11kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place22kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place44kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place11kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place22kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place44kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place11kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place22kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place44kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place11kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place22kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place44kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
|
|
VOID Place48kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place48kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place48kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
VOID Place48kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize);
|
|
|
|
/*
|
|
* Function:
|
|
* waveCallback
|
|
*
|
|
* Description:
|
|
* Fire a user specified callback
|
|
*
|
|
*/
|
|
VOID
|
|
waveCallback(
|
|
PWAVEOUTCTX pWaveOut,
|
|
DWORD msg,
|
|
DWORD_PTR dwParam1
|
|
)
|
|
{
|
|
if (pWaveOut && pWaveOut->dwCallback)
|
|
DriverCallback(pWaveOut->dwCallback, // user's callback DWORD
|
|
HIWORD(pWaveOut->dwOpenFlags), // callback flags
|
|
(HDRVR)pWaveOut->hWave, // wave device handle
|
|
msg, // the message
|
|
pWaveOut->dwInstance, // user's instance data
|
|
dwParam1, // first DWORD
|
|
0L); // second DWORD
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveOpen
|
|
*
|
|
* Description:
|
|
* The user request a device open
|
|
*
|
|
* Parameters:
|
|
* ppWaveOut - pointer to a context
|
|
* pWaveOpenDesc- requested formats
|
|
* dwFlags - and flags ( see MSDN )
|
|
*
|
|
*/
|
|
DWORD
|
|
waveOpen(
|
|
PWAVEOUTCTX *ppWaveOut,
|
|
LPWAVEOPENDESC pWaveOpenDesc,
|
|
DWORD_PTR dwFlags
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
DWORD dwProbe;
|
|
PWAVEOUTCTX pWaveOut = NULL;
|
|
LPWAVEFORMATEX lpFormat = NULL;
|
|
VOID (*pPlaceFn)(PVOID, PVOID, DWORD);
|
|
DWORD dwBytesPerXLate;
|
|
|
|
// Parameters check
|
|
//
|
|
if (NULL == ppWaveOut || NULL == pWaveOpenDesc)
|
|
{
|
|
TRC(ERR, "waveOpen: either ppWaveOut or pWaveOpenDesc are NULL\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check the requested format
|
|
//
|
|
lpFormat = (LPWAVEFORMATEX)(pWaveOpenDesc->lpFormat);
|
|
if (NULL == lpFormat)
|
|
{
|
|
TRC(ERR, "waveOpen: pWaveOpenDesc->lpFormat is NULL\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (WAVE_FORMAT_PCM != lpFormat->wFormatTag) // PCM format only
|
|
{
|
|
TRC(ALV, "waveOpen: non PCM format required, tag=%d\n",
|
|
lpFormat->wFormatTag);
|
|
rv = WAVERR_BADFORMAT;
|
|
goto exitpt;
|
|
}
|
|
|
|
// 8kHz 8 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
8000 == lpFormat->nSamplesPerSec &&
|
|
8000 == lpFormat->nAvgBytesPerSec &&
|
|
1 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place8kHz8Mono;
|
|
dwBytesPerXLate = 8000;
|
|
} else
|
|
// 8kHz 8 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
8000 == lpFormat->nSamplesPerSec &&
|
|
16000 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place8kHz8Stereo;
|
|
dwBytesPerXLate = 2 * 8000;
|
|
} else
|
|
// 8kHz 16 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
8000 == lpFormat->nSamplesPerSec &&
|
|
16000 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place8kHz16Mono;
|
|
dwBytesPerXLate = 2 * 8000;
|
|
} else
|
|
// 8kHz 16 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
8000 == lpFormat->nSamplesPerSec &&
|
|
32000 == lpFormat->nAvgBytesPerSec &&
|
|
4 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place8kHz16Stereo;
|
|
dwBytesPerXLate = 4 * 8000;
|
|
} else
|
|
// 11kHz 8 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels && // mono
|
|
11025 == lpFormat->nSamplesPerSec &&
|
|
11025 == lpFormat->nAvgBytesPerSec &&
|
|
1 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place11kHz8Mono;
|
|
dwBytesPerXLate = 1 * 11025;
|
|
}
|
|
else
|
|
// 22kHz 8 mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
22050 == lpFormat->nSamplesPerSec &&
|
|
22050 == lpFormat->nAvgBytesPerSec &&
|
|
1 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place22kHz8Mono;
|
|
dwBytesPerXLate = 2 * 11025;
|
|
}
|
|
else
|
|
// 44kHz 8 mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
44100 == lpFormat->nSamplesPerSec &&
|
|
44100 == lpFormat->nAvgBytesPerSec &&
|
|
1 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place44kHz8Mono;
|
|
dwBytesPerXLate = 4 * 11025;
|
|
}
|
|
else
|
|
// 11kHz 8 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
11025 == lpFormat->nSamplesPerSec &&
|
|
22050 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place11kHz8Stereo;
|
|
dwBytesPerXLate = 2 * 11025;
|
|
}
|
|
else
|
|
// 22kHz 8 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
22050 == lpFormat->nSamplesPerSec &&
|
|
44100 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place22kHz8Stereo;
|
|
dwBytesPerXLate = 4 * 11025;
|
|
}
|
|
else
|
|
// 44kHz 8 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
44100 == lpFormat->nSamplesPerSec &&
|
|
88200 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place44kHz8Stereo;
|
|
dwBytesPerXLate = 8 * 11025;
|
|
}
|
|
else
|
|
// 11kHz 16 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
11025 == lpFormat->nSamplesPerSec &&
|
|
22050 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place11kHz16Mono;
|
|
dwBytesPerXLate = 2 * 11025;
|
|
}
|
|
else
|
|
// 22kHz 16 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
22050 == lpFormat->nSamplesPerSec &&
|
|
44100 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place22kHz16Mono;
|
|
dwBytesPerXLate = 4 * 11025;
|
|
}
|
|
else
|
|
// 44kHz 16 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
44100 == lpFormat->nSamplesPerSec &&
|
|
88200 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
16== lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place44kHz16Mono;
|
|
dwBytesPerXLate = 8 * 11025;
|
|
}
|
|
else
|
|
// 11kHz 16 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
11025 == lpFormat->nSamplesPerSec &&
|
|
44100 == lpFormat->nAvgBytesPerSec &&
|
|
4 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place11kHz16Stereo;
|
|
dwBytesPerXLate = 4 * 11025;
|
|
}
|
|
else
|
|
// 22kHz 16 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
22050 == lpFormat->nSamplesPerSec &&
|
|
88200 == lpFormat->nAvgBytesPerSec &&
|
|
4 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place22kHz16Stereo;
|
|
dwBytesPerXLate = 8 * 11025;
|
|
}
|
|
else
|
|
// 44kHz 16 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
44100 == lpFormat->nSamplesPerSec &&
|
|
176400 == lpFormat->nAvgBytesPerSec &&
|
|
4 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place44kHz16Stereo;
|
|
dwBytesPerXLate = 16 * 11025;
|
|
}
|
|
else
|
|
// 48kHz 8 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
48000 == lpFormat->nSamplesPerSec &&
|
|
48000 == lpFormat->nAvgBytesPerSec &&
|
|
1 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place48kHz8Mono;
|
|
dwBytesPerXLate = 48000;
|
|
} else
|
|
// 48kHz 8 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
48000 == lpFormat->nSamplesPerSec &&
|
|
96000 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
8 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place48kHz8Stereo;
|
|
dwBytesPerXLate = 2 * 48000;
|
|
} else
|
|
// 48kHz 16 bit mono
|
|
//
|
|
if (1 == lpFormat->nChannels &&
|
|
48000 == lpFormat->nSamplesPerSec &&
|
|
96000 == lpFormat->nAvgBytesPerSec &&
|
|
2 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place48kHz16Mono;
|
|
dwBytesPerXLate = 2 * 48000;
|
|
} else
|
|
// 48kHz 16 bit stereo
|
|
//
|
|
if (2 == lpFormat->nChannels &&
|
|
48000 == lpFormat->nSamplesPerSec &&
|
|
192000 == lpFormat->nAvgBytesPerSec &&
|
|
4 == lpFormat->nBlockAlign &&
|
|
16 == lpFormat->wBitsPerSample)
|
|
{
|
|
pPlaceFn = Place48kHz16Stereo;
|
|
dwBytesPerXLate = 4 * 48000;
|
|
} else
|
|
{
|
|
// fall in an error
|
|
//
|
|
TRC(ALV, "waveOpen: unsupported format requested\n");
|
|
TRC(ALV, "waveOpen: FormatTag - %d\n", lpFormat->wFormatTag);
|
|
TRC(ALV, "waveOpen: Channels - %d\n", lpFormat->nChannels);
|
|
TRC(ALV, "waveOpen: SamplesPerSec - %d\n", lpFormat->nSamplesPerSec);
|
|
TRC(ALV, "waveOpen: AvgBytesPerSec - %d\n", lpFormat->nAvgBytesPerSec);
|
|
TRC(ALV, "waveOpen: BlockAlign - %d\n", lpFormat->nBlockAlign);
|
|
TRC(ALV, "waveOpen: BitsPerSample - %d\n", lpFormat->wBitsPerSample);
|
|
|
|
rv = WAVERR_BADFORMAT;
|
|
goto exitpt;
|
|
}
|
|
|
|
// validate the flags
|
|
//
|
|
if (0 != (dwFlags & WAVE_FORMAT_QUERY))
|
|
{
|
|
// this was only a query
|
|
TRC(ALV, "waveOpen: WAVE_FORMAT_QUERY\n");
|
|
rv = MMSYSERR_NOERROR;
|
|
goto exitpt;
|
|
}
|
|
dwProbe = ~(WAVE_ALLOWSYNC |
|
|
CALLBACK_EVENT |
|
|
CALLBACK_FUNCTION |
|
|
CALLBACK_WINDOW
|
|
);
|
|
|
|
|
|
if (0 != (dwFlags & dwProbe))
|
|
{
|
|
TRC(ERR, "waveOpen: unsupported flags required: 0x%x\n",
|
|
dwFlags);
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check that the remote side is there
|
|
//
|
|
if ( AudioRedirDisabled() ||
|
|
( !_waveCheckSoundAlive() &&
|
|
g_hWaitToInitialize == NULL ))
|
|
{
|
|
TRC(ALV, "waveOpen: No remote sound\n");
|
|
rv = MMSYSERR_NODRIVER;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Allocate the context structure
|
|
//
|
|
pWaveOut = TSMALLOC(sizeof(*pWaveOut));
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "waveOpen: Can't allocate %d bytes\n", sizeof(*pWaveOut));
|
|
rv = MMSYSERR_NOMEM;
|
|
goto exitpt;
|
|
}
|
|
memset(pWaveOut, 0, sizeof(*pWaveOut));
|
|
|
|
// fill the context
|
|
//
|
|
pWaveOut->hWave = pWaveOpenDesc->hWave;
|
|
pWaveOut->dwOpenFlags = dwFlags;
|
|
pWaveOut->dwCallback = pWaveOpenDesc->dwCallback;
|
|
pWaveOut->dwInstance = pWaveOpenDesc->dwInstance;
|
|
|
|
pWaveOut->lpfnPlace = pPlaceFn;
|
|
pWaveOut->dwXlateRate = dwBytesPerXLate;
|
|
|
|
pWaveOut->Format_nBlockAlign = lpFormat->nBlockAlign;
|
|
pWaveOut->Format_nAvgBytesPerSec = lpFormat->nAvgBytesPerSec;
|
|
pWaveOut->Format_nChannels = lpFormat->nChannels;
|
|
|
|
if ( NULL != g_Stream )
|
|
pWaveOut->cLastStreamPosition = g_Stream->cLastBlockQueued;
|
|
else
|
|
pWaveOut->cLastStreamPosition = 0;
|
|
|
|
pWaveOut->hNoDataEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
|
|
if (NULL == pWaveOut->hNoDataEvent)
|
|
{
|
|
TRC(ERR, "waveOpen: can't create event\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
*ppWaveOut = pWaveOut;
|
|
|
|
if ( NULL != g_hWaitToInitialize )
|
|
{
|
|
pWaveOut->bDelayed = TRUE;
|
|
}
|
|
|
|
// add this context to the global queue
|
|
//
|
|
ENTER_CRIT;
|
|
pWaveOut->lpNext = g_pAllWaveOut;
|
|
g_pAllWaveOut = pWaveOut;
|
|
LEAVE_CRIT;
|
|
|
|
waveCallback(pWaveOut, WOM_OPEN, 0);
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
|
|
if (MMSYSERR_NOERROR != rv)
|
|
{
|
|
if (pWaveOut)
|
|
{
|
|
if (NULL != pWaveOut->hNoDataEvent)
|
|
CloseHandle(pWaveOut->hNoDataEvent);
|
|
|
|
TSFREE(pWaveOut);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveGetWaveOutDeviceCaps
|
|
*
|
|
* Description:
|
|
* The user requests for device capabilities
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - our context
|
|
* pWaveOutCaps- supported capabilites
|
|
* dwWaveOutCapsSize - the sizeof the parameter above
|
|
*
|
|
*/
|
|
DWORD
|
|
waveGetWaveOutDeviceCaps(
|
|
PWAVEOUTCTX pWaveOut,
|
|
LPWAVEOUTCAPS pWaveOutCaps,
|
|
DWORD_PTR dwWaveOutCapsSize
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
// Parameters check
|
|
//
|
|
if (dwWaveOutCapsSize < sizeof(*pWaveOutCaps))
|
|
{
|
|
TRC(ERR, "waveGetWaveOutDeviceCaps: invalid size of WAVEOUTCAPS, expect %d, received %d\n",
|
|
sizeof(*pWaveOutCaps), dwWaveOutCapsSize);
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
pWaveOutCaps->wMid = MM_MICROSOFT;
|
|
pWaveOutCaps->wPid = MM_MSFT_GENERIC_WAVEOUT;
|
|
pWaveOutCaps->vDriverVersion = TSSND_DRIVER_VERSION;
|
|
LoadString( g_hDllInst,
|
|
IDS_DRIVER_NAME,
|
|
pWaveOutCaps->szPname,
|
|
sizeof( pWaveOutCaps->szPname ) / sizeof( pWaveOutCaps->szPname[0] ));
|
|
pWaveOutCaps->dwFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 |
|
|
WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16 |
|
|
WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 |
|
|
WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16 |
|
|
WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 |
|
|
WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16;
|
|
pWaveOutCaps->wChannels = 2;
|
|
|
|
pWaveOutCaps->dwSupport = WAVECAPS_SAMPLEACCURATE;
|
|
if ( NULL != g_Stream )
|
|
{
|
|
pWaveOutCaps->dwSupport |= (g_Stream->dwSoundCaps & TSSNDCAPS_VOLUME)?
|
|
WAVECAPS_VOLUME : 0;
|
|
pWaveOutCaps->dwSupport |= (g_Stream->dwSoundCaps & TSSNDCAPS_PITCH)?
|
|
WAVECAPS_PITCH : 0;
|
|
}
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveSetVolume
|
|
*
|
|
* Description:
|
|
* The user requests a new volume
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - context
|
|
* dwVolume - new volume
|
|
*
|
|
*/
|
|
DWORD
|
|
waveSetVolume(
|
|
PWAVEOUTCTX pWaveOut,
|
|
DWORD dwVolume
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
// Parameters check
|
|
//
|
|
|
|
// in case of mono, adjust the volume to stereo
|
|
//
|
|
if ( NULL != pWaveOut &&
|
|
1 == pWaveOut->Format_nChannels )
|
|
{
|
|
dwVolume = ( dwVolume & 0xffff ) |
|
|
(( dwVolume & 0xffff ) << 16 );
|
|
}
|
|
|
|
// Set the new volume in the sound stream
|
|
//
|
|
if (!_waveAcquireStream())
|
|
{
|
|
TRC(ERR, "waveSetVolume: can't acquire stream mutex\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check that volume control
|
|
// is supported on the remote
|
|
//
|
|
if (0 != (g_Stream->dwSoundCaps & TSSNDCAPS_VOLUME))
|
|
{
|
|
g_Stream->dwVolume = dwVolume;
|
|
g_Stream->bNewVolume = TRUE;
|
|
}
|
|
else
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
|
|
_waveReleaseStream();
|
|
|
|
if (MMSYSERR_NOTSUPPORTED == rv)
|
|
{
|
|
TRC(INF, "waveSetVolume: volume control not supported\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
// kick the sndio thread
|
|
//
|
|
if (g_hDataReadyEvent)
|
|
SetEvent(g_hDataReadyEvent);
|
|
else
|
|
TRC(WRN, "waveSetVolume: g_hDataReadyEvent is NULL\n");
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveGetVolume
|
|
*
|
|
* Description:
|
|
* The user queries for the current volume level
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - context
|
|
* pdwVolume - [out] current volume
|
|
*
|
|
*/
|
|
DWORD
|
|
waveGetVolume(
|
|
PWAVEOUTCTX pWaveOut,
|
|
DWORD *pdwVolume
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
// Parameters check
|
|
//
|
|
|
|
if (NULL == pdwVolume)
|
|
{
|
|
TRC(ERR, "pdwVolume is NULL\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!_waveAcquireStream())
|
|
{
|
|
TRC(ERR, "waveGetVolume: can't acquire stream mutex\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check that volume control
|
|
// is supported on the remote
|
|
//
|
|
if (0 != (g_Stream->dwSoundCaps & TSSNDCAPS_VOLUME))
|
|
*pdwVolume = g_Stream->dwVolume;
|
|
else
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
|
|
_waveReleaseStream();
|
|
|
|
if (MMSYSERR_NOTSUPPORTED == rv)
|
|
{
|
|
TRC(INF, "waveGetVolume: volume control not supported\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
|
|
}
|
|
|
|
DWORD
|
|
waveSetMute(
|
|
PWAVEOUTCTX pWaveOut,
|
|
BOOL fMute
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
if ( NULL == g_Stream)
|
|
{
|
|
TRC(ERR, "waveGetVolume: stream is NULL\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( fMute )
|
|
{
|
|
g_Stream->dwSoundFlags |= TSSNDFLAGS_MUTE;
|
|
} else {
|
|
g_Stream->dwSoundFlags &= ~TSSNDFLAGS_MUTE;
|
|
}
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
waveGetMute(
|
|
PWAVEOUTCTX pWaveOut,
|
|
DWORD *pfdwMute
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
if ( NULL == g_Stream)
|
|
{
|
|
TRC(ERR, "waveGetVolume: stream is NULL\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
*pfdwMute = ( 0 != ( g_Stream->dwSoundFlags & TSSNDFLAGS_MUTE ));
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveSetPitch
|
|
*
|
|
* Description:
|
|
* Sets new pitch level
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - context
|
|
* dwPitch - new pitch level
|
|
*
|
|
*/
|
|
DWORD
|
|
waveSetPitch(
|
|
PWAVEOUTCTX pWaveOut,
|
|
DWORD dwPitch
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
// Parameters check
|
|
//
|
|
|
|
// Set the new volume in the sound stream
|
|
//
|
|
if (!_waveAcquireStream())
|
|
{
|
|
TRC(ERR, "waveSetPitch: can't acquire stream mutex\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check that pitch control
|
|
// is supported on the remote
|
|
//
|
|
if (0 != (g_Stream->dwSoundCaps & TSSNDCAPS_PITCH))
|
|
{
|
|
g_Stream->dwPitch = dwPitch;
|
|
g_Stream->bNewPitch = TRUE;
|
|
}
|
|
else
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
|
|
_waveReleaseStream();
|
|
|
|
if (MMSYSERR_NOTSUPPORTED == rv)
|
|
{
|
|
TRC(INF, "waveSetPitch: pitch control not supported\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
// kick the sndio thread
|
|
//
|
|
if (g_hDataReadyEvent)
|
|
SetEvent(g_hDataReadyEvent);
|
|
else
|
|
TRC(WRN, "waveSetPitch: g_hDataReadyEvent is NULL\n");
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveGetPitch
|
|
*
|
|
* Description:
|
|
* Queries for the current pitch level
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - context
|
|
* pdwPitch - [out] current pitch level
|
|
*
|
|
*/
|
|
DWORD
|
|
waveGetPitch(
|
|
PWAVEOUTCTX pWaveOut,
|
|
DWORD *pdwPitch
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
// Parameters check
|
|
//
|
|
|
|
if (NULL == pdwPitch)
|
|
{
|
|
TRC(ERR, "pdwPitch is NULL\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!_waveAcquireStream())
|
|
{
|
|
TRC(ERR, "waveGetPitch: can't acquire stream mutex\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
// Check that pitch control
|
|
// is supported on the remote
|
|
//
|
|
if (0 != (g_Stream->dwSoundCaps & TSSNDCAPS_PITCH))
|
|
*pdwPitch = g_Stream->dwPitch;
|
|
else
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
|
|
_waveReleaseStream();
|
|
|
|
if (MMSYSERR_NOTSUPPORTED == rv)
|
|
{
|
|
TRC(INF, "waveGetPitch: pitch control not supported\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveGetNumDevs
|
|
*
|
|
* Description:
|
|
* we have only one device
|
|
*
|
|
*/
|
|
DWORD
|
|
waveGetNumDevs(
|
|
VOID
|
|
)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveClose
|
|
*
|
|
* Description:
|
|
* Wait for all blocks to complete and then close
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - context
|
|
*
|
|
*/
|
|
DWORD
|
|
waveClose(
|
|
PWAVEOUTCTX pWaveOut
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
DWORD syserr;
|
|
PWAVEOUTCTX pPrevWaveOut;
|
|
PWAVEOUTCTX pWaveOutIter;
|
|
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "waveClose: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// test if we are still playing
|
|
//
|
|
if ( pWaveOut->bPaused && 0 != pWaveOut->lNumberOfBlocksPlaying )
|
|
{
|
|
TRC(INF, "waveClose: WAVERR_STILLPLAYING\n");
|
|
rv = WAVERR_STILLPLAYING;
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( NULL != pWaveOut->hNoDataEvent)
|
|
{
|
|
DWORD dwTimeout;
|
|
|
|
if ( pWaveOut->bDelayed )
|
|
{
|
|
TRC( INF, "waveClose: delaying 15 seconds\n" );
|
|
dwTimeout = 15000;
|
|
} else {
|
|
dwTimeout = 0;
|
|
}
|
|
|
|
syserr = WaitForSingleObject(pWaveOut->hNoDataEvent, dwTimeout);
|
|
|
|
if ( WAIT_TIMEOUT == syserr )
|
|
{
|
|
TRC(INF, "waveClose: WAVERR_STILLPLAYING\n");
|
|
rv = WAVERR_STILLPLAYING;
|
|
goto exitpt;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we may end with some data in the last block in the stream
|
|
// if the "queued" mark hasn't change, increment it and kick the io
|
|
// thread to play this block
|
|
// to test this play very-very short files
|
|
// shorter than TSSND_BLOCKSIZE / (TSSND_NATIVE_BLOCKALIGN *
|
|
// TSSND_NATIVE_SAMPLERATE) seconds
|
|
//
|
|
//
|
|
//
|
|
if (_waveAcquireStream())
|
|
{
|
|
if (g_Stream->cLastBlockQueued == pWaveOut->cLastStreamPosition &&
|
|
0 != pWaveOut->dwLastStreamOffset)
|
|
{
|
|
g_Stream->cLastBlockQueued ++;
|
|
//
|
|
// kick the io thread
|
|
//
|
|
if (g_hDataReadyEvent)
|
|
SetEvent(g_hDataReadyEvent);
|
|
else
|
|
TRC(WRN, "waveClose: g_hDataReadyEvent is NULL\n");
|
|
}
|
|
_waveReleaseStream();
|
|
}
|
|
|
|
|
|
if (NULL != pWaveOut->hNoDataEvent)
|
|
CloseHandle(pWaveOut->hNoDataEvent);
|
|
|
|
// remove this context from the global queue
|
|
//
|
|
ENTER_CRIT;
|
|
|
|
pPrevWaveOut = NULL;
|
|
pWaveOutIter = g_pAllWaveOut;
|
|
|
|
while ( NULL != pWaveOutIter &&
|
|
pWaveOutIter != pWaveOut)
|
|
{
|
|
pPrevWaveOut = pWaveOutIter;
|
|
pWaveOutIter = pWaveOutIter->lpNext;
|
|
}
|
|
|
|
ASSERT(pWaveOut == pWaveOutIter);
|
|
|
|
if (pWaveOut == pWaveOutIter)
|
|
{
|
|
if (pPrevWaveOut)
|
|
pPrevWaveOut->lpNext = pWaveOut->lpNext;
|
|
else
|
|
g_pAllWaveOut = pWaveOut->lpNext;
|
|
}
|
|
LEAVE_CRIT;
|
|
|
|
waveCallback(pWaveOut, WOM_CLOSE, 0);
|
|
|
|
TSFREE(pWaveOut);
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveWrite
|
|
*
|
|
* Description:
|
|
* Play a block of data
|
|
*
|
|
* Parameters:
|
|
* pWaveOut - context
|
|
* pWaveHdr - the block
|
|
* dwWaveHdrSize - size of the above parameter
|
|
*
|
|
*/
|
|
DWORD
|
|
waveWrite(
|
|
PWAVEOUTCTX pWaveOut,
|
|
PWAVEHDR pWaveHdr,
|
|
DWORD_PTR dwWaveHdrSize
|
|
)
|
|
{
|
|
SNDWAVE WaveData;
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
PWAVEHDR pPrevHdr;
|
|
PWAVEHDR pLastHdr;
|
|
|
|
// Parameters check
|
|
//
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "waveWrite: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (sizeof(*pWaveHdr) != dwWaveHdrSize)
|
|
{
|
|
TRC(ERR, "waveWrite: invalid size for dwWaveHdrSize\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// check the buffer size alignment
|
|
//
|
|
if ( 0 != pWaveOut->Format_nBlockAlign &&
|
|
0 != pWaveHdr->dwBufferLength % pWaveOut->Format_nBlockAlign )
|
|
{
|
|
TRC( ERR, "wavePrepare: size unaligned\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (IsBadReadPtr( pWaveHdr->lpData, pWaveHdr->dwBufferLength ))
|
|
{
|
|
TRC( ERR, "wavePrepare: buffer unreadable\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
//
|
|
// check for proper alignment
|
|
//
|
|
if ( 0 != pWaveOut->Format_nBlockAlign &&
|
|
2 == pWaveOut->Format_nBlockAlign / pWaveOut->Format_nChannels &&
|
|
0 != (( (LONG_PTR)pWaveHdr->lpData ) & 1 ))
|
|
{
|
|
TRC( ERR, "wavePrepare: buffer unaligned\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
#endif
|
|
|
|
// pass this header to the mixer thread
|
|
//
|
|
if (NULL == g_hMixerEvent)
|
|
{
|
|
TRC(ERR, "waveWrite: g_hMixerEvent is NULL\n");
|
|
// confirm that the block is done
|
|
//
|
|
waveCallback(pWaveOut, WOM_DONE, (DWORD_PTR)pWaveHdr);
|
|
goto exitpt;
|
|
}
|
|
|
|
// add the header to the queue
|
|
//
|
|
ENTER_CRIT;
|
|
|
|
// find the last header
|
|
//
|
|
pPrevHdr = NULL;
|
|
pLastHdr = pWaveOut->pFirstWaveHdr;
|
|
while (pLastHdr)
|
|
{
|
|
//
|
|
// VERY VERY VERY IMAPORTANT !!!
|
|
// check if the app trys to add a header twice
|
|
// (WINAMP does)
|
|
//
|
|
if (pLastHdr == pWaveHdr)
|
|
{
|
|
TRC(ERR, "waveWrite: equal headers found, aborting\n");
|
|
goto abort_waveWrite;
|
|
}
|
|
|
|
pPrevHdr = pLastHdr;
|
|
pLastHdr = pLastHdr->lpNext;
|
|
}
|
|
|
|
pWaveHdr->lpNext = NULL;
|
|
pWaveHdr->reserved = (DWORD_PTR)pWaveOut;
|
|
pWaveHdr->dwFlags &= ~(WHDR_DONE);
|
|
pWaveHdr->dwFlags |= WHDR_INQUEUE;
|
|
|
|
// add the new header
|
|
//
|
|
if (NULL == pPrevHdr)
|
|
{
|
|
pWaveOut->pFirstWaveHdr = pWaveHdr;
|
|
} else {
|
|
pPrevHdr->lpNext = pWaveHdr;
|
|
}
|
|
|
|
InterlockedIncrement(&pWaveOut->lNumberOfBlocksPlaying);
|
|
|
|
ResetEvent(pWaveOut->hNoDataEvent);
|
|
|
|
//
|
|
// kick the mixer thread
|
|
//
|
|
SetEvent(g_hMixerEvent);
|
|
|
|
abort_waveWrite:
|
|
|
|
LEAVE_CRIT;
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* _waveAcquireStream
|
|
*
|
|
* Description:
|
|
* Locks down the stream using mutex
|
|
*
|
|
*/
|
|
BOOL
|
|
_waveAcquireStream(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
DWORD dwres;
|
|
|
|
if (NULL == g_hStream ||
|
|
NULL == g_Stream)
|
|
{
|
|
TRC(ALV, "_waveAcquireStream: the stream handle is NULL\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
if (NULL == g_hStreamMutex)
|
|
{
|
|
TRC(FATAL, "_waveAcquireStreamMutex: the stream mutex is NULL\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
dwres = WaitForSingleObject(g_hStreamMutex, DEFAULT_VC_TIMEOUT);
|
|
if (WAIT_TIMEOUT == dwres ||
|
|
WAIT_ABANDONED == dwres )
|
|
{
|
|
TRC(ERR, "_waveAcquireStreamMutex: "
|
|
"timed out waiting for the stream mutex or owner crashed=%d\n", dwres );
|
|
//
|
|
// possible app crash
|
|
//
|
|
ASSERT(0);
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
_waveReleaseStream(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL rv = TRUE;
|
|
|
|
ASSERT(NULL != g_hStream);
|
|
ASSERT(NULL != g_Stream);
|
|
ASSERT(NULL != g_hStreamMutex);
|
|
|
|
if (!ReleaseMutex(g_hStreamMutex))
|
|
rv = FALSE;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* _waveCheckSoundAlive
|
|
*
|
|
* Description:
|
|
* Chacks if the client can play audio
|
|
*
|
|
*/
|
|
BOOL
|
|
_waveCheckSoundAlive(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
|
|
rv = ( NULL != g_Stream && 0 != (g_Stream->dwSoundCaps & TSSNDCAPS_ALIVE));
|
|
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
AudioRedirDisabled(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
static BOOL s_fChecked = FALSE;
|
|
static HANDLE s_hRDPEvent = NULL;
|
|
static WINSTATIONCONFIG s_config;
|
|
ULONG returnedLength;
|
|
|
|
if ( s_fChecked )
|
|
{
|
|
return s_config.User.fDisableCam;
|
|
}
|
|
|
|
//
|
|
// we need special case for session 0
|
|
// because winlogon never exits there
|
|
//
|
|
if ( 0 == NtCurrentPeb()->SessionId )
|
|
{
|
|
DWORD dw;
|
|
if ( NULL == s_hRDPEvent )
|
|
{
|
|
|
|
s_hRDPEvent = OpenEvent( SYNCHRONIZE, FALSE, L"Global\\RDPAudioDisabledEvent" );
|
|
if ( NULL == s_hRDPEvent )
|
|
{
|
|
TRC( ERR, "failed to open Global\\RDPAudioDisabledEvent [%d]\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
dw = WaitForSingleObject( s_hRDPEvent, 0 );
|
|
return ( WAIT_OBJECT_0 == dw );
|
|
}
|
|
|
|
//
|
|
// check if the loader lock is held
|
|
// if true, we'll fail to do the RPC call
|
|
//
|
|
if ( NtCurrentTeb()->ClientId.UniqueThread !=
|
|
((PRTL_CRITICAL_SECTION)(NtCurrentPeb()->LoaderLock))->OwningThread )
|
|
{
|
|
fSuccess = WinStationQueryInformation(NULL, LOGONID_CURRENT,
|
|
WinStationConfiguration, &s_config,
|
|
sizeof(s_config), &returnedLength);
|
|
if ( fSuccess )
|
|
{
|
|
s_fChecked = TRUE;
|
|
}
|
|
}
|
|
|
|
return ( !fSuccess || s_config.User.fDisableCam );
|
|
}
|
|
|
|
|
|
/*
|
|
* Create the mixer thread
|
|
*
|
|
*
|
|
*/
|
|
BOOL
|
|
_EnableMixerThread(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwThreadId;
|
|
|
|
ENTER_CRIT;
|
|
|
|
if ( AudioRedirDisabled() )
|
|
{
|
|
TRC( ALV, "TS Audio redirection is disabled\n" );
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( !g_bPersonalTS && 0 != NtCurrentPeb()->SessionId )
|
|
{
|
|
if ( NULL == g_pAllWaveOut )
|
|
{
|
|
//
|
|
// not yet
|
|
//
|
|
goto exitpt;
|
|
}
|
|
}
|
|
|
|
if ( NULL != g_hMixerThread )
|
|
{
|
|
DWORD dw = WaitForSingleObject( g_hMixerThread, 0 );
|
|
if ( WAIT_OBJECT_0 == dw )
|
|
{
|
|
CloseHandle( g_hMixerThread );
|
|
g_hMixerThread = NULL;
|
|
}
|
|
}
|
|
|
|
if ( NULL == g_hMixerThread )
|
|
{
|
|
g_bMixerRunning = TRUE;
|
|
|
|
g_hMixerThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
waveMixerThread,
|
|
NULL,
|
|
0,
|
|
&dwThreadId
|
|
);
|
|
}
|
|
|
|
if (NULL == g_hMixerThread)
|
|
{
|
|
TRC(FATAL, "DriverProc: can't start mixer thread\n");
|
|
}
|
|
|
|
exitpt:
|
|
LEAVE_CRIT;
|
|
|
|
return ( NULL != g_hMixerThread );
|
|
}
|
|
|
|
/*
|
|
* called on waveOutClose, if there are no more workers to play, close the mixer thread
|
|
*
|
|
*/
|
|
VOID
|
|
_DerefMixerThread(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE hMixerThread;
|
|
|
|
//
|
|
// don't close the mixer thread on Pro and on session 0 on servers
|
|
//
|
|
if (g_bPersonalTS || 0 == NtCurrentPeb()->SessionId)
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( NULL == g_hMixerEvent )
|
|
{
|
|
TRC( ERR, "_DerefMixerThread: no mixer event\n" );
|
|
goto exitpt;
|
|
}
|
|
|
|
ENTER_CRIT;
|
|
if ( NULL != g_pAllWaveOut )
|
|
{
|
|
//
|
|
// not yet
|
|
//
|
|
LEAVE_CRIT;
|
|
goto exitpt;
|
|
}
|
|
|
|
hMixerThread = g_hMixerThread;
|
|
g_hMixerThread = NULL;
|
|
g_bMixerRunning = FALSE;
|
|
LEAVE_CRIT;
|
|
|
|
SetEvent( g_hMixerEvent );
|
|
WaitForSingleObject(hMixerThread, INFINITE);
|
|
CloseHandle( hMixerThread );
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* _waveMixerWriteData
|
|
*
|
|
* Description:
|
|
* Mixes blocks of data to the stream
|
|
*
|
|
*/
|
|
VOID
|
|
_waveMixerWriteData(
|
|
VOID
|
|
)
|
|
{
|
|
// this call is made within the mixer
|
|
// thread context
|
|
//
|
|
UINT uiEmptyBlocks;
|
|
PWAVEHDR pWaveHdr;
|
|
PWAVEHDR pPrevHdr;
|
|
PWAVEOUTCTX pWaveOut;
|
|
DWORD dwStartPos;
|
|
DWORD dwSize1;
|
|
DWORD dwSize2;
|
|
DWORD dwFitBufferLength;
|
|
DWORD dwFitDest;
|
|
DWORD dwBuffDisp;
|
|
BOOL bKickStream = FALSE;
|
|
|
|
ENTER_CRIT;
|
|
|
|
if (NULL == g_pAllWaveOut)
|
|
{
|
|
TRC(ALV, "_waveWriteData: WaveOut queue is empty\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
if (NULL == g_hDataReadyEvent)
|
|
{
|
|
TRC(ERR, "_waveWriteData: g_hStreamDataReady is NULL\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!_waveAcquireStream())
|
|
{
|
|
TRC(ERR, "_waveWriteData: can't acquire the stream mutex\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( 0 == (g_Stream->dwSoundCaps & TSSNDCAPS_ALIVE) ||
|
|
0 != ( g_Stream->dwSoundFlags & TSSNDFLAGS_MUTE ))
|
|
{
|
|
// no play here
|
|
_waveReleaseStream();
|
|
goto exitpt;
|
|
}
|
|
|
|
for (
|
|
pWaveOut = g_pAllWaveOut;
|
|
NULL != pWaveOut;
|
|
pWaveOut = pWaveOut->lpNext
|
|
)
|
|
{
|
|
|
|
for(
|
|
pPrevHdr = NULL, pWaveHdr = pWaveOut->pFirstWaveHdr;
|
|
NULL != pWaveHdr;
|
|
/* nothing */
|
|
)
|
|
{
|
|
|
|
// if this stream is paused advance to the next one
|
|
//
|
|
if (pWaveOut->bPaused)
|
|
break;
|
|
|
|
// check if we have to append data to a buffer
|
|
// from previous call
|
|
//
|
|
if ((BYTE)(pWaveOut->cLastStreamPosition - g_Stream->cLastBlockSent) >
|
|
TSSND_MAX_BLOCKS ||
|
|
(BYTE)(g_Stream->cLastBlockQueued -
|
|
pWaveOut->cLastStreamPosition) >
|
|
TSSND_MAX_BLOCKS)
|
|
{
|
|
pWaveOut->cLastStreamPosition = g_Stream->cLastBlockSent;
|
|
pWaveOut->dwLastStreamOffset = 0;
|
|
|
|
TRC(ALV, "_waveWriteData: reseting the stream position\n");
|
|
}
|
|
|
|
// the empty blocks are from "LastStreamPosition" to "Confirmed"
|
|
// ( "Confirmed" to "LastStreamPosition" are preserved )
|
|
//
|
|
uiEmptyBlocks = (BYTE)
|
|
(g_Stream->cLastBlockSent + TSSND_MAX_BLOCKS -
|
|
pWaveOut->cLastStreamPosition);
|
|
|
|
if (uiEmptyBlocks > TSSND_MAX_BLOCKS)
|
|
{
|
|
TRC(FATAL, "too many empty blocks:\n");
|
|
TRC(FATAL, "cLastBlockQueued=%d\n", g_Stream->cLastBlockQueued);
|
|
TRC(FATAL, "cLastBlockSent =%d\n", g_Stream->cLastBlockSent);
|
|
TRC(FATAL, "cLastBlockConfirmed%d\n", g_Stream->cLastBlockConfirmed);
|
|
TRC(FATAL, "cLastStreamPosition=%d\n", pWaveOut->cLastStreamPosition);
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
// if everything is full, go to bed
|
|
//
|
|
if (0 == uiEmptyBlocks)
|
|
{
|
|
TRC(ALV, "_waveMixerWriteData: stream is full\n");
|
|
break;
|
|
}
|
|
|
|
// WHAT IF THE EMPTY SPACE IS SMALLER THAN THE DATA IN THE HEADER
|
|
//
|
|
dwFitBufferLength = (uiEmptyBlocks * TSSND_BLOCKSIZE -
|
|
pWaveOut->dwLastStreamOffset);
|
|
|
|
dwFitDest = MulDiv(
|
|
pWaveHdr->dwBufferLength -
|
|
pWaveOut->dwLastHeaderOffset,
|
|
TSSND_NATIVE_XLATERATE,
|
|
pWaveOut->dwXlateRate
|
|
);
|
|
|
|
dwFitDest &= ~( TSSND_NATIVE_BLOCKALIGN - 1 );
|
|
|
|
if ( dwFitBufferLength < dwFitDest )
|
|
{
|
|
dwFitDest = MulDiv(
|
|
dwFitBufferLength,
|
|
pWaveOut->dwXlateRate,
|
|
TSSND_NATIVE_XLATERATE
|
|
);
|
|
|
|
dwFitDest &= ~( pWaveOut->Format_nBlockAlign - 1 );
|
|
} else {
|
|
dwFitBufferLength = dwFitDest;
|
|
|
|
dwFitDest = pWaveHdr->dwBufferLength - pWaveOut->dwLastHeaderOffset;
|
|
}
|
|
|
|
// place the data, because of the round buffer,
|
|
// this could be a two step process
|
|
//
|
|
// TRC( INF, "Filling block # %d, offset=0x%x\n", pWaveOut->cLastStreamPosition, pWaveOut->dwLastStreamOffset );
|
|
dwStartPos = (pWaveOut->cLastStreamPosition
|
|
% TSSND_MAX_BLOCKS) *
|
|
TSSND_BLOCKSIZE +
|
|
pWaveOut->dwLastStreamOffset;
|
|
|
|
if ( dwStartPos + dwFitBufferLength >
|
|
TSSND_TOTALSTREAMSIZE)
|
|
{
|
|
dwSize1 = (TSSND_TOTALSTREAMSIZE - dwStartPos) /
|
|
TSSND_NATIVE_BLOCKALIGN;
|
|
dwSize2 = dwFitBufferLength / TSSND_NATIVE_BLOCKALIGN -
|
|
dwSize1;
|
|
} else {
|
|
dwSize1 = dwFitBufferLength / TSSND_NATIVE_BLOCKALIGN;
|
|
dwSize2 = 0;
|
|
}
|
|
|
|
pWaveOut->lpfnPlace(g_Stream->pSndData +
|
|
dwStartPos,
|
|
((LPSTR)pWaveHdr->lpData) + pWaveOut->dwLastHeaderOffset,
|
|
dwSize1);
|
|
|
|
dwBuffDisp = MulDiv(
|
|
dwSize1 * TSSND_NATIVE_BLOCKALIGN,
|
|
pWaveOut->dwXlateRate,
|
|
TSSND_NATIVE_XLATERATE
|
|
);
|
|
|
|
dwBuffDisp &= ~( pWaveOut->Format_nBlockAlign - 1 );
|
|
|
|
pWaveOut->lpfnPlace(g_Stream->pSndData,
|
|
((LPSTR)pWaveHdr->lpData) + pWaveOut->dwLastHeaderOffset +
|
|
dwBuffDisp,
|
|
dwSize2);
|
|
|
|
// Calculate the new position
|
|
//
|
|
pWaveOut->dwLastStreamOffset += dwFitBufferLength;
|
|
pWaveOut->cLastStreamPosition += (BYTE)(pWaveOut->dwLastStreamOffset /
|
|
TSSND_BLOCKSIZE);
|
|
pWaveOut->dwLastStreamOffset %= TSSND_BLOCKSIZE;
|
|
|
|
pWaveOut->dwLastHeaderOffset += dwFitDest;
|
|
|
|
ASSERT(pWaveOut->dwLastHeaderOffset <= pWaveHdr->dwBufferLength);
|
|
|
|
//
|
|
// check if the buffer is completed
|
|
//
|
|
if ( 0 == MulDiv(
|
|
( pWaveHdr->dwBufferLength - pWaveOut->dwLastHeaderOffset ),
|
|
TSSND_NATIVE_XLATERATE,
|
|
pWaveOut->dwXlateRate))
|
|
{
|
|
|
|
pWaveOut->dwLastHeaderOffset = 0;
|
|
|
|
// remove this header from the queue
|
|
//
|
|
if (NULL == pPrevHdr)
|
|
pWaveOut->pFirstWaveHdr = pWaveHdr->lpNext;
|
|
else
|
|
pPrevHdr->lpNext = pWaveHdr->lpNext;
|
|
|
|
pWaveHdr->lpNext = NULL;
|
|
//
|
|
// save the current stream mark
|
|
//
|
|
pWaveHdr->reserved = g_Stream->cLastBlockQueued;
|
|
|
|
// add it to the ready queue
|
|
//
|
|
if (NULL == pWaveOut->pLastReadyHdr)
|
|
{
|
|
pWaveOut->pFirstReadyHdr = pWaveHdr;
|
|
pWaveOut->pLastReadyHdr = pWaveHdr;
|
|
} else {
|
|
pWaveOut->pLastReadyHdr->lpNext = pWaveHdr;
|
|
pWaveOut->pLastReadyHdr = pWaveHdr;
|
|
}
|
|
|
|
// reset pPrevHdr and pWaveHdr
|
|
//
|
|
pPrevHdr = NULL;
|
|
pWaveHdr = pWaveOut->pFirstWaveHdr;
|
|
} else {
|
|
//
|
|
// Advance to the next header
|
|
//
|
|
pPrevHdr = pWaveHdr;
|
|
pWaveHdr = pWaveHdr->lpNext;
|
|
}
|
|
|
|
//
|
|
// kick the stream thread
|
|
//
|
|
if ((BYTE)(pWaveOut->cLastStreamPosition - g_Stream->cLastBlockQueued) <
|
|
_NEG_IDX)
|
|
{
|
|
|
|
bKickStream = TRUE;
|
|
//
|
|
// move the "queued" mark
|
|
//
|
|
g_Stream->cLastBlockQueued = pWaveOut->cLastStreamPosition;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (bKickStream)
|
|
{
|
|
//
|
|
// kick the io
|
|
//
|
|
SetEvent(g_hDataReadyEvent);
|
|
}
|
|
|
|
_waveReleaseStream();
|
|
|
|
exitpt:
|
|
|
|
// Now for all "done" buffers, send the callback
|
|
//
|
|
|
|
for (pWaveOut = g_pAllWaveOut;
|
|
NULL != pWaveOut;
|
|
pWaveOut = pWaveOut->lpNext)
|
|
{
|
|
for (pPrevHdr = NULL, pWaveHdr = pWaveOut->pFirstReadyHdr;
|
|
NULL != pWaveHdr;
|
|
/* nothing */ )
|
|
{
|
|
|
|
if ( (INT)((CHAR)(g_Stream->cLastBlockQueued -
|
|
PtrToLong((PVOID)pWaveHdr->reserved))) >= 0)
|
|
{
|
|
// this block was confirmed, proceed with
|
|
// extracting it and notification
|
|
//
|
|
if (NULL != pPrevHdr)
|
|
pPrevHdr->lpNext = pWaveHdr->lpNext;
|
|
else
|
|
pWaveOut->pFirstReadyHdr = pWaveHdr->lpNext;
|
|
|
|
if (pWaveHdr == pWaveOut->pLastReadyHdr)
|
|
pWaveOut->pLastReadyHdr = pPrevHdr;
|
|
|
|
//
|
|
// advance the number of samples.
|
|
// also, remember a time stamp and this block size
|
|
// in samples, for sample accuracy
|
|
//
|
|
pWaveOut->dwSamples += pWaveHdr->dwBufferLength /
|
|
pWaveOut->Format_nBlockAlign;
|
|
|
|
//
|
|
// clear this buffer from the pending list
|
|
//
|
|
if (0 == InterlockedDecrement(&pWaveOut->lNumberOfBlocksPlaying))
|
|
{
|
|
SetEvent(pWaveOut->hNoDataEvent);
|
|
}
|
|
|
|
// notify the app
|
|
//
|
|
|
|
// mark the buffer as ready
|
|
//
|
|
pWaveHdr->dwFlags |= WHDR_DONE;
|
|
pWaveHdr->dwFlags &= ~(WHDR_INQUEUE);
|
|
pWaveHdr->lpNext = NULL;
|
|
pWaveHdr->reserved = 0;
|
|
|
|
// confirm that the block is done
|
|
//
|
|
waveCallback(pWaveOut, WOM_DONE, (DWORD_PTR)pWaveHdr);
|
|
|
|
|
|
// reinitialize the iterators
|
|
//
|
|
if ( NULL == g_pAllWaveOut )
|
|
goto leave_crit;
|
|
|
|
pWaveOut = g_pAllWaveOut;
|
|
|
|
pPrevHdr = NULL, pWaveHdr = pWaveOut->pFirstReadyHdr;
|
|
|
|
if ( NULL == pWaveHdr )
|
|
goto leave_crit;
|
|
} else {
|
|
|
|
// advance the iterators
|
|
//
|
|
pPrevHdr = pWaveHdr;
|
|
pWaveHdr = pWaveHdr->lpNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
leave_crit:
|
|
LEAVE_CRIT;
|
|
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* _waveMixerPlaySilence
|
|
*
|
|
* Description:
|
|
* Simulates play by using sleep
|
|
*
|
|
*/
|
|
BOOL
|
|
_waveMixerPlaySilence(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL rv = FALSE;
|
|
DWORD dwMinTime;
|
|
DWORD dwTime;
|
|
DWORD dwLength;
|
|
PWAVEHDR pWaveHdr;
|
|
PWAVEOUTCTX pWaveOut;
|
|
|
|
//
|
|
// simulate silent play
|
|
//
|
|
dwMinTime = (DWORD)-1;
|
|
//
|
|
// find the smallest block waiting and sleep
|
|
// for the time it has to play
|
|
//
|
|
ENTER_CRIT;
|
|
for (
|
|
pWaveOut = g_pAllWaveOut;
|
|
NULL != pWaveOut;
|
|
pWaveOut = pWaveOut->lpNext
|
|
)
|
|
{
|
|
pWaveHdr = pWaveOut->pFirstWaveHdr;
|
|
|
|
if ( NULL == pWaveHdr )
|
|
continue;
|
|
|
|
dwLength = pWaveHdr->dwBufferLength - pWaveOut->dwLastHeaderOffset;
|
|
//
|
|
// time is in miliseconds
|
|
//
|
|
dwTime = dwLength * 1000 /
|
|
pWaveOut->Format_nAvgBytesPerSec;
|
|
|
|
if ( dwMinTime > dwTime )
|
|
dwMinTime = dwTime;
|
|
}
|
|
LEAVE_CRIT;
|
|
|
|
//
|
|
// exit if no block is found
|
|
//
|
|
if ( (DWORD)-1 == dwMinTime )
|
|
goto exitpt;
|
|
|
|
if ( 0 == dwMinTime )
|
|
dwMinTime = 1;
|
|
|
|
Sleep( dwMinTime );
|
|
|
|
//
|
|
// start confirming
|
|
//
|
|
ENTER_CRIT;
|
|
for (
|
|
pWaveOut = g_pAllWaveOut;
|
|
NULL != pWaveOut;
|
|
pWaveOut = pWaveOut->lpNext
|
|
)
|
|
{
|
|
pWaveHdr = pWaveOut->pFirstWaveHdr;
|
|
|
|
if ( NULL == pWaveHdr )
|
|
continue;
|
|
|
|
dwLength = pWaveOut->dwLastHeaderOffset;
|
|
dwLength += dwMinTime * pWaveOut->Format_nAvgBytesPerSec / 1000;
|
|
//
|
|
// align to a block
|
|
//
|
|
dwLength += pWaveOut->Format_nBlockAlign - 1;
|
|
dwLength /= pWaveOut->Format_nBlockAlign;
|
|
dwLength *= pWaveOut->Format_nBlockAlign;
|
|
|
|
pWaveOut->dwLastHeaderOffset = dwLength;
|
|
if ( dwLength >= pWaveHdr->dwBufferLength )
|
|
{
|
|
pWaveOut->dwLastHeaderOffset = 0;
|
|
pWaveOut->pFirstWaveHdr = pWaveHdr->lpNext;
|
|
//
|
|
// this block is "done"
|
|
// mark the buffer as ready
|
|
//
|
|
pWaveHdr->dwFlags |= WHDR_DONE;
|
|
pWaveHdr->dwFlags &= ~(WHDR_INQUEUE);
|
|
pWaveHdr->lpNext = NULL;
|
|
pWaveHdr->reserved = 0;
|
|
|
|
// increment the position
|
|
//
|
|
pWaveOut->dwSamples += pWaveHdr->dwBufferLength /
|
|
pWaveOut->Format_nBlockAlign;
|
|
|
|
if (0 == InterlockedDecrement(&pWaveOut->lNumberOfBlocksPlaying))
|
|
{
|
|
SetEvent(pWaveOut->hNoDataEvent);
|
|
}
|
|
|
|
// confirm that the block is done
|
|
//
|
|
waveCallback(pWaveOut, WOM_DONE, (DWORD_PTR)pWaveHdr);
|
|
}
|
|
}
|
|
LEAVE_CRIT;
|
|
|
|
rv = TRUE;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveMixerThread
|
|
*
|
|
* Description:
|
|
* Mixer thread main entry point
|
|
*
|
|
* Parameters:
|
|
* pParam - unused
|
|
*
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
waveMixerThread(
|
|
PVOID pParam
|
|
)
|
|
{
|
|
HANDLE ahEvents[3];
|
|
PWAVEOUTCTX pWaveOut;
|
|
HANDLE hCleanupEvent = NULL;
|
|
DWORD numEvents;
|
|
|
|
//
|
|
// wait for the sound process to initialize
|
|
//
|
|
if (( NULL == g_Stream ||
|
|
0 == ( g_Stream->dwSoundCaps & TSSNDCAPS_INITIALIZED)) &&
|
|
NULL != g_hWaitToInitialize )
|
|
{
|
|
DWORD dw = WaitForSingleObject( g_hWaitToInitialize,
|
|
10 * DEFAULT_VC_TIMEOUT );
|
|
if ( WAIT_OBJECT_0 != dw )
|
|
TRC( ERR, "WaitToInitialize failed\n" );
|
|
else
|
|
TRC( INF, "WaitToInitialize succeeded\n" );
|
|
|
|
hCleanupEvent = g_hWaitToInitialize;
|
|
g_hWaitToInitialize = NULL;
|
|
|
|
drvEnable();
|
|
} else {
|
|
hCleanupEvent = _CreateInitEvent();
|
|
}
|
|
|
|
if (NULL == g_hMixerEvent ||
|
|
NULL == g_hDataReadyEvent)
|
|
{
|
|
TRC(FATAL, "waveMixerThread: no events\n");
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( NULL != hCleanupEvent )
|
|
{
|
|
ahEvents[0] = hCleanupEvent;
|
|
ahEvents[1] = g_hMixerEvent;
|
|
ahEvents[2] = g_hStreamIsEmptyEvent;
|
|
numEvents = 3;
|
|
} else {
|
|
ahEvents[0] = g_hMixerEvent;
|
|
ahEvents[1] = g_hStreamIsEmptyEvent;
|
|
numEvents = 2;
|
|
}
|
|
|
|
while (g_bMixerRunning)
|
|
{
|
|
DWORD dwres;
|
|
DWORD bHdrsPending = FALSE;
|
|
|
|
// check if there are headers pending
|
|
//
|
|
ENTER_CRIT;
|
|
for (pWaveOut = g_pAllWaveOut;
|
|
NULL != pWaveOut && !bHdrsPending;
|
|
pWaveOut = pWaveOut->lpNext
|
|
)
|
|
bHdrsPending = (NULL != pWaveOut->pFirstWaveHdr ||
|
|
NULL != pWaveOut->pFirstReadyHdr) &&
|
|
!pWaveOut->bPaused;
|
|
|
|
LEAVE_CRIT;
|
|
|
|
if ( bHdrsPending &&
|
|
NULL != g_Stream &&
|
|
( 0 == (g_Stream->dwSoundCaps & TSSNDCAPS_ALIVE) ||
|
|
( 0 != (g_Stream->dwSoundCaps & TSSNDCAPS_VOLUME) &&
|
|
0 == g_Stream->dwVolume
|
|
) ||
|
|
( 0 != ( g_Stream->dwSoundFlags & TSSNDFLAGS_MUTE ))
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// play silence in case of disconnected on "mute" mode
|
|
//
|
|
while( ( 0 == (g_Stream->dwSoundCaps & TSSNDCAPS_ALIVE) ||
|
|
( 0 != (g_Stream->dwSoundCaps & TSSNDCAPS_VOLUME) &&
|
|
0 == g_Stream->dwVolume
|
|
) ||
|
|
( 0 != ( g_Stream->dwSoundFlags & TSSNDFLAGS_MUTE ))
|
|
) &&
|
|
_waveMixerPlaySilence() )
|
|
;
|
|
} else {
|
|
Sleep( 30 ); // give some time to the DSound emulator thread to wake up
|
|
//
|
|
|
|
dwres = WaitForMultipleObjects(
|
|
(!bHdrsPending) ? numEvents - 1 : numEvents,
|
|
ahEvents,
|
|
FALSE,
|
|
INFINITE
|
|
);
|
|
|
|
//
|
|
// check for termination
|
|
//
|
|
if ( WAIT_OBJECT_0 == dwres && NULL != hCleanupEvent )
|
|
{
|
|
TRC( INF, "Cleanup detected (rdpclip disappeared ?!)\n" );
|
|
// check for termination
|
|
if ( _waveAcquireStream() )
|
|
{
|
|
if ( TSSNDCAPS_TERMINATED == g_Stream->dwSoundCaps )
|
|
{
|
|
TRC( INF, "Cleaning up global data\n" );
|
|
CloseHandle( g_hMixerThread );
|
|
g_hMixerThread = NULL;
|
|
_waveReleaseStream();
|
|
drvDisable();
|
|
goto exitpt;
|
|
}
|
|
|
|
_waveReleaseStream();
|
|
}
|
|
}
|
|
|
|
_waveMixerWriteData();
|
|
}
|
|
|
|
}
|
|
|
|
exitpt:
|
|
TRC(INF, "waveMixerThread exited\n");
|
|
|
|
if ( NULL != hCleanupEvent )
|
|
CloseHandle( hCleanupEvent );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* wavePrepare
|
|
*
|
|
* Description:
|
|
* Prepares a block, i.e. only sets it's flags
|
|
*
|
|
*/
|
|
DWORD
|
|
wavePrepare(
|
|
PVOID pWaveCtx,
|
|
PWAVEHDR pWaveHdr,
|
|
DWORD_PTR dwWaveHdrSize,
|
|
BOOL bPrepare)
|
|
{
|
|
PWAVEOUTCTX pWaveOut = pWaveCtx;
|
|
BOOL rv = MMSYSERR_NOTSUPPORTED;
|
|
|
|
// Parameters check
|
|
//
|
|
if (NULL == pWaveCtx)
|
|
{
|
|
TRC(ERR, "wavePrepare: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (sizeof(*pWaveHdr) != dwWaveHdrSize)
|
|
{
|
|
TRC(ERR, "wavePrepare: invalid size for dwWaveHdrSize\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// check the buffer size alignment
|
|
//
|
|
if ( 0 != pWaveOut->Format_nBlockAlign &&
|
|
0 != pWaveHdr->dwBufferLength % pWaveOut->Format_nBlockAlign )
|
|
{
|
|
TRC( ERR, "wavePrepare: size unaligned\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (IsBadReadPtr( pWaveHdr->lpData, pWaveHdr->dwBufferLength ))
|
|
{
|
|
TRC( ERR, "wavePrepare: buffer unreadable\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
//
|
|
// check for proper alignment
|
|
//
|
|
if ( 0 != pWaveOut->Format_nChannels &&
|
|
2 == pWaveOut->Format_nBlockAlign / pWaveOut->Format_nChannels &&
|
|
0 != (( (LONG_PTR)pWaveHdr->lpData ) & 1 ))
|
|
{
|
|
TRC( ERR, "wavePrepare: buffer unaligned\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
#endif
|
|
|
|
if (bPrepare)
|
|
pWaveHdr->dwFlags |= WHDR_PREPARED;
|
|
else
|
|
pWaveHdr->dwFlags &= ~WHDR_PREPARED;
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveReset
|
|
*
|
|
* Description:
|
|
* Resets all current queued blocks
|
|
*
|
|
*/
|
|
DWORD
|
|
waveReset(
|
|
PWAVEOUTCTX pWaveOut
|
|
)
|
|
{
|
|
BOOL rv = MMSYSERR_NOTSUPPORTED;
|
|
LPWAVEHDR pWaveHdr;
|
|
LPWAVEHDR pFoundPrevHdr;
|
|
|
|
ENTER_CRIT;
|
|
|
|
// Parameters check
|
|
//
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "waveReset: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
// dismiss all headers pending confirmation
|
|
//
|
|
while ( NULL != pWaveOut->pFirstReadyHdr )
|
|
{
|
|
pWaveHdr = pWaveOut->pFirstReadyHdr;
|
|
pWaveOut->pFirstReadyHdr = pWaveOut->pFirstReadyHdr->lpNext;
|
|
|
|
if (NULL == pWaveOut->pFirstReadyHdr)
|
|
pWaveOut->pLastReadyHdr = NULL;
|
|
|
|
pWaveHdr->reserved = 0;
|
|
pWaveHdr->lpNext = NULL;
|
|
pWaveHdr->dwFlags |= WHDR_DONE;
|
|
pWaveHdr->dwFlags &= ~(WHDR_INQUEUE);
|
|
|
|
// confirm that the block is done
|
|
//
|
|
LEAVE_CRIT;
|
|
|
|
waveCallback(pWaveOut, WOM_DONE, (DWORD_PTR)pWaveHdr);
|
|
|
|
ENTER_CRIT;
|
|
|
|
if (0 == InterlockedDecrement(&pWaveOut->lNumberOfBlocksPlaying))
|
|
{
|
|
SetEvent(pWaveOut->hNoDataEvent);
|
|
}
|
|
}
|
|
|
|
// Clean all headers in the queue
|
|
//
|
|
while(NULL != pWaveOut->pFirstWaveHdr)
|
|
{
|
|
|
|
pWaveHdr = pWaveOut->pFirstWaveHdr;
|
|
pWaveOut->pFirstWaveHdr = pWaveHdr->lpNext;
|
|
|
|
pWaveHdr->reserved = 0;
|
|
pWaveHdr->lpNext = NULL;
|
|
pWaveHdr->dwFlags |= WHDR_DONE;
|
|
pWaveHdr->dwFlags &= ~(WHDR_INQUEUE);
|
|
|
|
// confirm that the block is done
|
|
//
|
|
LEAVE_CRIT;
|
|
|
|
waveCallback(pWaveOut, WOM_DONE, (DWORD_PTR)pWaveHdr);
|
|
|
|
ENTER_CRIT;
|
|
|
|
if (0 == InterlockedDecrement(&pWaveOut->lNumberOfBlocksPlaying))
|
|
{
|
|
SetEvent(pWaveOut->hNoDataEvent);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// we may end with some data in the last block in the stream
|
|
// if the "queued" mark hasn't change, increment it and kick the io
|
|
// thread to play this block
|
|
// to test this play very-very short files
|
|
// shorter than TSSND_BLOCKSIZE / (TSSND_NATIVE_BLOCKALIGN *
|
|
// TSSND_NATIVE_SAMPLERATE) seconds
|
|
//
|
|
//
|
|
//
|
|
if (_waveAcquireStream())
|
|
{
|
|
if (g_Stream->cLastBlockQueued == pWaveOut->cLastStreamPosition &&
|
|
0 != pWaveOut->dwLastStreamOffset)
|
|
{
|
|
g_Stream->cLastBlockQueued ++;
|
|
//
|
|
// kick the io thread
|
|
//
|
|
if (g_hDataReadyEvent)
|
|
SetEvent(g_hDataReadyEvent);
|
|
else
|
|
TRC(WRN, "waveClose: g_hDataReadyEvent is NULL\n");
|
|
}
|
|
_waveReleaseStream();
|
|
}
|
|
|
|
pWaveOut->dwLastHeaderOffset = 0;
|
|
pWaveOut->dwSamples = 0;
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
LEAVE_CRIT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveGetPos
|
|
*
|
|
* Description:
|
|
* Gets current position in the current stream
|
|
*
|
|
*/
|
|
DWORD
|
|
waveGetPos(
|
|
PWAVEOUTCTX pWaveOut,
|
|
MMTIME *pMMTime,
|
|
DWORD_PTR dwMMTimeSize
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
DWORD dwSamples;
|
|
DWORD ms;
|
|
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "waveGetPos: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (NULL == pMMTime || sizeof(*pMMTime) != dwMMTimeSize)
|
|
{
|
|
TRC(ERR, "waveGetPos: pMMTime is invalid\n");
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
//
|
|
// update the played position
|
|
//
|
|
|
|
dwSamples = pWaveOut->dwSamples;
|
|
|
|
switch ( pMMTime->wType )
|
|
{
|
|
case TIME_SAMPLES:
|
|
pMMTime->u.sample = dwSamples;
|
|
break;
|
|
case TIME_BYTES:
|
|
pMMTime->u.cb = dwSamples * pWaveOut->Format_nBlockAlign;
|
|
break;
|
|
case TIME_MS:
|
|
pMMTime->u.ms = MulDiv( dwSamples, pWaveOut->Format_nBlockAlign * 1000,
|
|
pWaveOut->Format_nAvgBytesPerSec );
|
|
|
|
break;
|
|
case TIME_SMPTE:
|
|
ms = MulDiv( dwSamples, pWaveOut->Format_nBlockAlign * 1000,
|
|
pWaveOut->Format_nAvgBytesPerSec );
|
|
|
|
pMMTime->u.smpte.frame = (BYTE)((ms % 1000) * 24 / 1000);
|
|
ms /= 1000;
|
|
pMMTime->u.smpte.sec = (BYTE)(ms % 60);
|
|
ms /= 60;
|
|
pMMTime->u.smpte.min = (BYTE)(ms % 60);
|
|
ms /= 60;
|
|
pMMTime->u.smpte.hour = (BYTE)ms;
|
|
pMMTime->u.smpte.fps = 24;
|
|
break;
|
|
default:
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
}
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* wavePause
|
|
*
|
|
* Description:
|
|
* Pauses the current play
|
|
*
|
|
*/
|
|
DWORD
|
|
wavePause(
|
|
PWAVEOUTCTX pWaveOut
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "wavePause: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
pWaveOut->bPaused = TRUE;
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* waveRestart
|
|
*
|
|
* Description:
|
|
* Restarts a paused play
|
|
*
|
|
*/
|
|
DWORD
|
|
waveRestart(
|
|
PWAVEOUTCTX pWaveOut
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
if (NULL == pWaveOut)
|
|
{
|
|
TRC(ERR, "waveRestart: invalid device handle\n");
|
|
rv = MMSYSERR_INVALHANDLE;
|
|
goto exitpt;
|
|
}
|
|
|
|
pWaveOut->bPaused = FALSE;
|
|
|
|
//
|
|
// Kick the mixer thread
|
|
//
|
|
if (NULL == g_hMixerEvent)
|
|
{
|
|
TRC(WRN, "waveRestart: g_hMixerEvent is NULL\n");
|
|
} else
|
|
SetEvent(g_hMixerEvent);
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* wodMessage
|
|
*
|
|
* Description:
|
|
* Main entry point for WaveOut device
|
|
*
|
|
* Parameters:
|
|
*
|
|
*
|
|
*/
|
|
DWORD
|
|
APIENTRY
|
|
wodMessage(
|
|
UINT uDeviceID,
|
|
UINT uMessage,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
PWAVEOUTCTX pWaveOut = (PWAVEOUTCTX)dwUser;
|
|
|
|
switch ( uMessage )
|
|
{
|
|
case WODM_GETNUMDEVS:
|
|
TRC(ALV, "WODM_GETNUMDEVS\n");
|
|
rv = waveGetNumDevs();
|
|
break;
|
|
|
|
case WODM_GETDEVCAPS:
|
|
drvEnable();
|
|
|
|
TRC( ALV, "WODM_GETDEVCAPS\n");
|
|
rv = waveGetWaveOutDeviceCaps(
|
|
pWaveOut,
|
|
(LPWAVEOUTCAPS)dwParam1,
|
|
dwParam2
|
|
);
|
|
break;
|
|
|
|
case WODM_OPEN:
|
|
drvEnable();
|
|
|
|
TRC(ALV, "WODM_OPEN\n");
|
|
rv = waveOpen( (PWAVEOUTCTX *)dwUser,
|
|
(LPWAVEOPENDESC)dwParam1,
|
|
dwParam2);
|
|
|
|
_EnableMixerThread();
|
|
break;
|
|
|
|
case WODM_CLOSE:
|
|
TRC(ALV, "WODM_CLOSE\n");
|
|
rv = waveClose(pWaveOut);
|
|
_DerefMixerThread();
|
|
break;
|
|
|
|
case WODM_WRITE:
|
|
TRC(ALV, "WODM_WRITE\n");
|
|
rv = waveWrite(pWaveOut, (PWAVEHDR)dwParam1, dwParam2);
|
|
break;
|
|
|
|
case WODM_PAUSE:
|
|
TRC(ALV, "WODM_PAUSE\n");
|
|
rv = wavePause(pWaveOut);
|
|
break;
|
|
|
|
case WODM_RESTART:
|
|
TRC(ALV, "WODM_RESTART\n");
|
|
rv = waveRestart(pWaveOut);
|
|
break;
|
|
|
|
case WODM_RESET:
|
|
TRC(ALV, "WODM_RESET\n");
|
|
rv = waveReset(pWaveOut);
|
|
break;
|
|
|
|
case WODM_BREAKLOOP:
|
|
TRC(ALV, "WODM_BREAKLOOP\n");
|
|
// rv = waveBreakLoop(pWaveOut);
|
|
rv = MMSYSERR_NOERROR;
|
|
break;
|
|
|
|
case WODM_GETPOS:
|
|
TRC(ALV, "WODM_GETPOS\n");
|
|
rv = waveGetPos(pWaveOut, (MMTIME *)dwParam1, dwParam2);
|
|
break;
|
|
|
|
case WODM_SETPITCH:
|
|
TRC(ALV, "WODM_SETPITCH\n");
|
|
rv = waveSetPitch(pWaveOut, PtrToLong((PVOID)dwParam1));
|
|
break;
|
|
|
|
case WODM_SETVOLUME:
|
|
TRC(ALV, "WODM_SETVOLUME\n");
|
|
rv = waveSetVolume(pWaveOut, PtrToLong((PVOID)dwParam1));
|
|
break;
|
|
|
|
case WODM_SETPLAYBACKRATE:
|
|
TRC(ALV, "WODM_SETPLAYBACKRATE\n");
|
|
// rv = waveSetPlaybackRate(pWaveOut, dwParam1);
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
break;
|
|
|
|
case WODM_GETPITCH:
|
|
TRC(ALV, "WODM_GETVOLUME\n");
|
|
rv = waveGetPitch(pWaveOut, (DWORD *)dwParam1);
|
|
break;
|
|
|
|
case WODM_GETVOLUME:
|
|
TRC(ALV, "WODM_GETVOLUME\n");
|
|
rv = waveGetVolume(pWaveOut, (DWORD *)dwParam1);
|
|
break;
|
|
|
|
case WODM_GETPLAYBACKRATE:
|
|
TRC(ALV, "WODM_GETPLAYBACKRATE\n");
|
|
// rv = waveGetPlaybackRate(pWaveOut, (DWORD *)dwParam1);
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
break;
|
|
|
|
case WODM_PREPARE:
|
|
TRC(ALV, "WODM_PREPARE\n");
|
|
rv = wavePrepare(pWaveOut, (PWAVEHDR)dwParam1, dwParam2, TRUE);
|
|
break;
|
|
|
|
case WODM_UNPREPARE:
|
|
TRC(ALV, "WODM_UNPREPARE\n");
|
|
rv = wavePrepare(pWaveOut, (PWAVEHDR)dwParam1, dwParam2, FALSE);
|
|
break;
|
|
|
|
default:
|
|
TRC(ERR, "Unsupported message: 0x%x\n", uMessage);
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Function:
|
|
* widMessage
|
|
*
|
|
* Description:
|
|
* Main entry point for WaveIn device ( unsupported
|
|
*/
|
|
DWORD
|
|
APIENTRY
|
|
widMessage(
|
|
UINT uDeviceID,
|
|
UINT uMessage,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
if ( WIDM_GETNUMDEVS == uMessage )
|
|
return 0;
|
|
|
|
return MMSYSERR_NODRIVER;
|
|
}
|
|
|
|
//
|
|
// Common PCM format -> 22 kHz 16 bit stereo
|
|
//
|
|
// THE SIZE IS IN NUMBER OF SAMPLES IN NATIVE FORMAT
|
|
//
|
|
#define PLACE_DATA(_pdst_, _srcv_) \
|
|
sum = _pdst_[0] + _srcv_; \
|
|
\
|
|
if (sum > 0x7FFF) \
|
|
sum = 0x7FFF; \
|
|
if (sum < -0x8000) \
|
|
sum = -0x8000; \
|
|
\
|
|
_pdst_[0] = (INT16)sum; \
|
|
_pdst_ ++;
|
|
|
|
VOID
|
|
Place8kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 8000;
|
|
|
|
psrc += ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place8kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = ((INT)(psrc[1] - 0x80)) << 8;
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 8000;
|
|
|
|
psrc += 2 * ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place8kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 8000;
|
|
|
|
psrc += (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place8kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = psrc[1];
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 8000;
|
|
|
|
psrc += 2 * (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place11kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT src;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA(pdst, src);
|
|
PLACE_DATA(pdst, src);
|
|
|
|
psrc += (dwSize & 1); // advance on every odd step
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
|
|
VOID
|
|
Place22kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT src;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA(pdst, src);
|
|
PLACE_DATA(pdst, src);
|
|
|
|
psrc ++;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place44kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT src;
|
|
|
|
src = (((INT)(psrc[0] + psrc[1] - 2 * 0x80)) / 2) << 8;
|
|
|
|
PLACE_DATA(pdst, src);
|
|
PLACE_DATA(pdst, src);
|
|
|
|
psrc += 2;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place11kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize --)
|
|
{
|
|
INT sum;
|
|
INT src;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA(pdst, src);
|
|
PLACE_DATA(pdst, src);
|
|
|
|
psrc += (dwSize & 1); // advance on every odd step
|
|
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place22kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
INT16 *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT src;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA(pdst, src);
|
|
PLACE_DATA(pdst, src);
|
|
|
|
psrc ++;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place44kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
INT16 *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT src;
|
|
|
|
src = (psrc[0] + psrc[1]) / 2;
|
|
|
|
PLACE_DATA(pdst, src);
|
|
PLACE_DATA(pdst, src);
|
|
|
|
psrc += 2;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place11kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
BYTE *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize --)
|
|
{
|
|
INT sum;
|
|
INT srcl, srcr;
|
|
|
|
srcl = (((INT)(psrc[0] - 0x80)) << 8);
|
|
srcr = (((INT)(psrc[1] - 0x80)) << 8);
|
|
|
|
PLACE_DATA(pdst, srcl);
|
|
PLACE_DATA(pdst, srcr);
|
|
|
|
psrc += 2 * (dwSize & 1); // advance on every odd step
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place22kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
BYTE *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT srcl, srcr;
|
|
|
|
srcl = (((INT)(psrc[0] - 0x80)) << 8);
|
|
srcr = (((INT)(psrc[1] - 0x80)) << 8);
|
|
|
|
PLACE_DATA(pdst, srcl);
|
|
PLACE_DATA(pdst, srcr);
|
|
|
|
psrc += 2;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place44kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
BYTE *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT srcl, srcr;
|
|
|
|
srcl = (((INT)(psrc[0] + psrc[2] - 2 * 0x80) / 2) << 8);
|
|
srcr = (((INT)(psrc[1] + psrc[3] - 2 * 0x80) / 2) << 8);
|
|
|
|
PLACE_DATA(pdst, srcl);
|
|
PLACE_DATA(pdst, srcr);
|
|
|
|
psrc += 4;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place11kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
INT16 *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize --)
|
|
{
|
|
INT sum;
|
|
INT srcl, srcr;
|
|
|
|
srcl = (INT)psrc[0];
|
|
srcr = (INT)psrc[1];
|
|
|
|
PLACE_DATA(pdst, srcl);
|
|
PLACE_DATA(pdst, srcr);
|
|
|
|
psrc += 2 * (dwSize & 1); // advance on every odd step
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place22kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
INT16 *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT srcl, srcr;
|
|
|
|
srcl = (INT)psrc[0];
|
|
srcr = (INT)psrc[1];
|
|
|
|
PLACE_DATA(pdst, srcl);
|
|
PLACE_DATA(pdst, srcr);
|
|
|
|
psrc += 2;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place44kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *pdst;
|
|
INT16 *psrc;
|
|
|
|
if (NULL == pDest || NULL == pSrc)
|
|
goto exitpt;
|
|
|
|
for (pdst = pDest, psrc = pSrc;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT sum;
|
|
INT srcl, srcr;
|
|
|
|
srcl = (INT)(psrc[0] + psrc[2]) / 2;
|
|
srcr = (INT)(psrc[1] + psrc[3]) / 2;
|
|
|
|
PLACE_DATA(pdst, srcl);
|
|
PLACE_DATA(pdst, srcr);
|
|
|
|
psrc += 4;
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place12kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 12000;
|
|
|
|
psrc += ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place12kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = ((INT)(psrc[1] - 0x80)) << 8;
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 12000;
|
|
|
|
psrc += 2 * ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place12kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 12000;
|
|
|
|
psrc += (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place12kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = psrc[1];
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 12000;
|
|
|
|
psrc += 2 * (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
|
|
VOID
|
|
Place16kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 16000;
|
|
|
|
psrc += ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place16kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = ((INT)(psrc[1] - 0x80)) << 8;
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 16000;
|
|
|
|
psrc += 2 * ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place16kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 16000;
|
|
|
|
psrc += (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place16kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = psrc[1];
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 16000;
|
|
|
|
psrc += 2 * (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
|
|
VOID
|
|
Place24kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 24000;
|
|
|
|
psrc += ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place24kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = ((INT)(psrc[1] - 0x80)) << 8;
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 24000;
|
|
|
|
psrc += 2 * ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place24kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 24000;
|
|
|
|
psrc += (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place24kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = psrc[1];
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 24000;
|
|
|
|
psrc += 2 * (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
|
|
VOID
|
|
Place32kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 32000;
|
|
|
|
psrc += ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place32kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = ((INT)(psrc[1] - 0x80)) << 8;
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 32000;
|
|
|
|
psrc += 2 * ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place32kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 32000;
|
|
|
|
psrc += (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place32kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = psrc[1];
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 32000;
|
|
|
|
psrc += 2 * (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
|
|
VOID
|
|
Place48kHz8Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 48000;
|
|
|
|
psrc += ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place48kHz8Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
BYTE *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = ((INT)(psrc[0] - 0x80)) << 8;
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = ((INT)(psrc[1] - 0x80)) << 8;
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 48000;
|
|
|
|
psrc += 2 * ( dwLeap / TSSND_NATIVE_SAMPLERATE );
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place48kHz16Mono(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 48000;
|
|
|
|
psrc += (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
VOID
|
|
Place48kHz16Stereo(PVOID pDest, PVOID pSrc, DWORD dwSize)
|
|
{
|
|
INT16 *psrc;
|
|
INT16 *pdst;
|
|
DWORD dwLeap;
|
|
|
|
if ( NULL == pDest || NULL == pSrc )
|
|
goto exitpt;
|
|
|
|
for (psrc = pSrc,
|
|
pdst = pDest,
|
|
dwLeap = 0;
|
|
dwSize;
|
|
dwSize--)
|
|
{
|
|
INT src;
|
|
INT sum;
|
|
|
|
src = psrc[0];
|
|
|
|
PLACE_DATA( pdst, src );
|
|
|
|
src = psrc[1];
|
|
PLACE_DATA( pdst, src );
|
|
|
|
dwLeap += 48000;
|
|
|
|
psrc += 2 * (dwLeap / TSSND_NATIVE_SAMPLERATE);
|
|
dwLeap %= TSSND_NATIVE_SAMPLERATE;
|
|
}
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Unsupported entries
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
APIENTRY
|
|
modMessage(
|
|
UINT uDeviceID,
|
|
UINT uMessage,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
if ( MODM_GETNUMDEVS == uMessage )
|
|
return 0;
|
|
|
|
return MMSYSERR_NODRIVER;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
midMessage(
|
|
UINT uDeviceID,
|
|
UINT uMessage,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
if ( MIDM_GETNUMDEVS == uMessage )
|
|
return 0;
|
|
|
|
return MMSYSERR_NODRIVER;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
auxMessage(
|
|
UINT uDeviceID,
|
|
UINT uMessage,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
if ( AUXDM_GETNUMDEVS == uMessage )
|
|
return 0;
|
|
|
|
return MMSYSERR_NODRIVER;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Mixer implementation
|
|
//
|
|
DWORD
|
|
RDPMixerOpen(
|
|
PMIXERCTX *ppMixer,
|
|
PMIXEROPENDESC pMixerDesc,
|
|
DWORD_PTR dwFlags
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
PMIXERCTX pMix = NULL;
|
|
|
|
ASSERT( CALLBACK_FUNCTION == dwFlags );
|
|
|
|
pMix = &g_Mixer;
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
RDPMixerClose(
|
|
PMIXERCTX pMixer
|
|
)
|
|
{
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
DWORD
|
|
RDPMixerGetDevCaps(
|
|
PMIXERCTX pMixer,
|
|
PMIXERCAPS pCaps,
|
|
DWORD_PTR dwCapsSize
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
// Parameters check
|
|
//
|
|
if (dwCapsSize < sizeof(*pCaps))
|
|
{
|
|
TRC(ERR, "RDPMixerGetDevCaps: invalid size of MIXERCAPS, expect %d, received %d\n",
|
|
sizeof(*pCaps), dwCapsSize);
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
pCaps->wMid = MM_MICROSOFT;
|
|
pCaps->wPid = MM_MSFT_GENERIC_WAVEOUT;
|
|
pCaps->vDriverVersion = TSSND_DRIVER_VERSION;
|
|
LoadString( g_hDllInst,
|
|
IDS_DRIVER_NAME,
|
|
pCaps->szPname,
|
|
sizeof( pCaps->szPname ) / sizeof( pCaps->szPname[0] ));
|
|
pCaps->fdwSupport = 0; // no flags defined
|
|
pCaps->cDestinations = 1;
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
_FillMixerLineInfo( PMIXERLINE pLine )
|
|
{
|
|
DWORD dw;
|
|
|
|
pLine->dwDestination = 0; // just one destination
|
|
pLine->dwLineID = 0; // just one line
|
|
pLine->fdwLine = MIXERLINE_LINEF_ACTIVE;
|
|
pLine->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
|
|
pLine->cChannels = 2;
|
|
pLine->cConnections = 0;
|
|
pLine->cControls = 2;
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_VOLUME_NAME,
|
|
pLine->szShortName,
|
|
RTL_NUMBER_OF( pLine->szShortName ));
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_VOLUME_NAME,
|
|
pLine->szName,
|
|
RTL_NUMBER_OF( pLine->szName ));
|
|
|
|
pLine->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
|
|
|
|
pLine->Target.dwDeviceID = WAVE_MAPPER;
|
|
|
|
pLine->Target.wMid = MM_MICROSOFT;
|
|
pLine->Target.wPid = MM_MSFT_GENERIC_WAVEOUT;
|
|
pLine->Target.vDriverVersion = TSSND_DRIVER_VERSION;
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_DRIVER_NAME,
|
|
pLine->Target.szPname,
|
|
RTL_NUMBER_OF( pLine->Target.szPname ));
|
|
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
DWORD
|
|
RDPMixerGetLineInfo(
|
|
PMIXERCTX pMixer,
|
|
PMIXERLINE pLine,
|
|
DWORD_PTR dwFlags
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
|
|
if ( pLine->cbStruct < sizeof( *pLine ))
|
|
{
|
|
TRC(ERR, "MixerGetLineInfo: invalid lineinfo size: %d\n", pLine->cbStruct );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
switch( dwFlags & MIXER_GETLINEINFOF_QUERYMASK )
|
|
{
|
|
|
|
case MIXER_GETLINEINFOF_DESTINATION:
|
|
TRC( ALV, "MixerGetLineInfo: MIXER_GETLINEINFOF_DESTINATION\n" );
|
|
if ( 0 != pLine->dwDestination )
|
|
{
|
|
//
|
|
// there's just one destination
|
|
//
|
|
TRC( ERR, "MixerGetLineInfo: invalid destination: %d\n", pLine->dwDestination );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillMixerLineInfo( pLine );
|
|
break;
|
|
|
|
case MIXER_GETLINEINFOF_TARGETTYPE:
|
|
TRC( ALV, "MixerGetLineInfo: MIXER_GETLINEINFOF_TARGETTYPE\n" );
|
|
if ( MIXERLINE_TARGETTYPE_WAVEOUT != pLine->Target.dwType )
|
|
{
|
|
TRC( ERR, "MIXER_GETLINEINFOF_TARGETTYPE for unsupported type=0x%x\n", pLine->Target.dwType );
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillMixerLineInfo( pLine );
|
|
break;
|
|
|
|
case MIXER_GETLINEINFOF_COMPONENTTYPE:
|
|
TRC( ALV, "MixerGetLineInfo: MIXER_GETLINEINFOF_COMPONENTTYPE\n" );
|
|
if ( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS != pLine->dwComponentType )
|
|
{
|
|
TRC( ERR, "MIXER_GETLINEINFOF_COMPONENTTYPE for unsupported type=0x%x\n", pLine->dwComponentType );
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillMixerLineInfo( pLine );
|
|
break;
|
|
|
|
case MIXER_GETLINEINFOF_LINEID:
|
|
TRC( ALV, "MIXER_GETLINEINFOF_LINEID\n" );
|
|
if ( 0 != pLine->dwLineID )
|
|
{
|
|
TRC( ERR, "MIXER_GETLINEINFOF_LINEID for invalid line ID: %d\n", pLine->dwLineID );
|
|
rv = MIXERR_INVALLINE;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillMixerLineInfo( pLine );
|
|
break;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
_FillLineControl(
|
|
PMIXERLINECONTROLS pc,
|
|
DWORD dwControlId
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
PMIXERCONTROL pmc;
|
|
DWORD dwMax = (DWORD)-1;
|
|
|
|
if ( pc->cbmxctrl < sizeof( *(pc->pamxctrl )))
|
|
{
|
|
TRC( ERR, "_FillLineControl: no enough space\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
pc->dwLineID = 0;
|
|
pmc = pc->pamxctrl;
|
|
|
|
pmc->cbStruct = sizeof( *pmc );
|
|
pmc->dwControlID = dwControlId;
|
|
switch( dwControlId )
|
|
{
|
|
// case RDP_MXDID_MIXER: pmc->dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; break;
|
|
case RDP_MXDID_VOLUME: pmc->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; dwMax = (WORD)-1; break;
|
|
case RDP_MXDID_MUTE: pmc->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; dwMax = 1; break;
|
|
}
|
|
pmc->fdwControl = 0;
|
|
pmc->cMultipleItems = 0;
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_VOLUME_NAME,
|
|
pmc->szShortName,
|
|
RTL_NUMBER_OF( pmc->szShortName ));
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_VOLUME_NAME,
|
|
pmc->szName,
|
|
RTL_NUMBER_OF( pmc->szName ));
|
|
|
|
pmc->Bounds.dwMinimum = 0;
|
|
pmc->Bounds.dwMaximum = dwMax;
|
|
pmc->Metrics.cSteps = 1;
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
_FillLineControlAll(
|
|
PMIXERLINECONTROLS pc
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
PMIXERCONTROL pmc;
|
|
PMIXERCONTROL pnextmc;
|
|
|
|
if ( pc->cbmxctrl < sizeof( *(pc->pamxctrl )))
|
|
{
|
|
TRC( ERR, "_FillLineControl: no enough space\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( 2 != pc->cControls )
|
|
{
|
|
TRC( ERR, "_FillLineControl: invalid number of lines\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
pc->dwLineID = 0;
|
|
pmc = pc->pamxctrl;
|
|
|
|
pmc->cbStruct = sizeof( *pmc );
|
|
pmc->dwControlID = RDP_MXDID_MUTE;
|
|
pmc->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
|
|
pmc->fdwControl = 0;
|
|
pmc->cMultipleItems = 0;
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_VOLUME_NAME,
|
|
pmc->szShortName,
|
|
RTL_NUMBER_OF( pmc->szShortName ));
|
|
|
|
LoadString( g_hDllInst,
|
|
IDS_VOLUME_NAME,
|
|
pmc->szName,
|
|
RTL_NUMBER_OF( pmc->szName ));
|
|
|
|
pmc->Bounds.dwMinimum = 0;
|
|
pmc->Bounds.dwMaximum = 1;
|
|
pmc->Metrics.cSteps = 1;
|
|
|
|
//
|
|
// copy the volume struct
|
|
//
|
|
pnextmc = (PMIXERCONTROL)(((PBYTE)pmc) + pc->cbmxctrl);
|
|
RtlCopyMemory( pnextmc, pmc, sizeof( *pmc ));
|
|
pmc = pnextmc;
|
|
pmc->dwControlID = RDP_MXDID_VOLUME;
|
|
pmc->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
|
|
pmc->Bounds.dwMinimum = 0;
|
|
pmc->Bounds.dwMaximum = (WORD)(-1);
|
|
|
|
rv = MMSYSERR_NOERROR;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
RDPMixerGetLineControls(
|
|
PMIXERCTX pMixer,
|
|
PMIXERLINECONTROLS pControls,
|
|
DWORD_PTR fdwControls
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
DWORD dwControlId;
|
|
|
|
if ( pControls->cbStruct < sizeof( *pControls ))
|
|
{
|
|
TRC(ERR, "MixerGetLineControls: invalid linecontrols size: %d\n", pControls->cbStruct );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
switch( fdwControls )
|
|
{
|
|
case MIXER_GETLINECONTROLSF_ONEBYTYPE:
|
|
TRC( ALV, "MixerGetLineControls: MIXER_GETLINECONTROLSF_ONEBYTYPE\n",
|
|
pControls->dwControlType );
|
|
if ( 0 != pControls->dwLineID )
|
|
{
|
|
rv = MIXERR_INVALLINE;
|
|
goto exitpt;
|
|
}
|
|
switch( pControls->dwControlType )
|
|
{
|
|
// case MIXERCONTROL_CONTROLTYPE_MIXER: dwControlId = RDP_MXDID_MIXER; break;
|
|
case MIXERCONTROL_CONTROLTYPE_VOLUME: dwControlId = RDP_MXDID_VOLUME; break;
|
|
case MIXERCONTROL_CONTROLTYPE_MUTE: dwControlId = RDP_MXDID_MUTE; break;
|
|
default:
|
|
rv = MMSYSERR_NOTSUPPORTED;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillLineControl( pControls, dwControlId );
|
|
break;
|
|
case MIXER_GETLINECONTROLSF_ONEBYID:
|
|
TRC( ALV, "MixerGetLineControls: MIXER_GETLINECONTROLSF_ONEBYID\n" );
|
|
if ( RDP_MXDID_LAST <= pControls->dwControlID )
|
|
{
|
|
TRC( ERR, "MixerGetLineControls: invalid line id: %d\n", pControls->dwControlID );
|
|
rv = MIXERR_INVALCONTROL;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillLineControl( pControls, pControls->dwControlID );
|
|
break;
|
|
case MIXER_GETLINECONTROLSF_ALL:
|
|
TRC( ALV, "MixerGetLineControls: MIXER_GETLINECONTROLSF_ALL\n" );
|
|
if ( 0 != pControls->dwLineID )
|
|
{
|
|
rv = MIXERR_INVALLINE;
|
|
goto exitpt;
|
|
}
|
|
if ( 2 > pControls->cControls )
|
|
{
|
|
TRC( ERR, "MixerGetLineControls: invalid cControls=%d\n", pControls->cControls );
|
|
rv = MIXERR_INVALCONTROL;
|
|
goto exitpt;
|
|
}
|
|
rv = _FillLineControlAll( pControls );
|
|
break;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
RDPMixerGetSetControlDetails(
|
|
PMIXERCTX pMixer,
|
|
PMIXERCONTROLDETAILS pDetails,
|
|
DWORD_PTR fdwDetails,
|
|
BOOL bGet
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
DWORD fdw;
|
|
|
|
if ( pDetails->cbStruct < sizeof( *pDetails ))
|
|
{
|
|
TRC( ERR, "Mixer%sControlDetails: invalid details size\n", (bGet)?"Get":"Set" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( 0 != pDetails->cMultipleItems &&
|
|
1 != pDetails->cMultipleItems )
|
|
{
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
|
|
if ( RDP_MXDID_LAST <= pDetails->dwControlID )
|
|
{
|
|
TRC( ERR, "Mixer%sControlDetails: invalid control id: %d\n", (bGet)?"Get":"Set", pDetails->dwControlID );
|
|
rv = MIXERR_INVALCONTROL;
|
|
goto exitpt;
|
|
}
|
|
|
|
fdw = PtrToLong( (PVOID)(MIXER_GETCONTROLDETAILSF_QUERYMASK & fdwDetails));
|
|
|
|
if ( MIXER_GETCONTROLDETAILSF_VALUE == fdw )
|
|
{
|
|
TRC( ALV, "Mixer%sControlDetails: read VALUE, cbDetail=%d, cChannels=%d, controlId=%d\n",
|
|
(bGet)?"Get":"Set",
|
|
pDetails->cbDetails, pDetails->cChannels,
|
|
pDetails->dwControlID
|
|
);
|
|
ASSERT( pDetails->cbDetails == sizeof( DWORD ));
|
|
if ( pDetails->cbDetails == sizeof( DWORD ))
|
|
{
|
|
DWORD dwVal = *(DWORD *)pDetails->paDetails;
|
|
if ( !bGet )
|
|
{
|
|
if ( 2 != pDetails->cChannels &&
|
|
1 != pDetails->cChannels )
|
|
{
|
|
TRC( ERR, "Unexpected # channels\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
//
|
|
// the mute has different control id
|
|
//
|
|
if ( RDP_MXDID_MUTE == pDetails->dwControlID )
|
|
{
|
|
rv = waveSetMute( NULL, (dwVal != 0) );
|
|
} else {
|
|
//
|
|
// this will set the volume
|
|
// there should be 2 channels for stereo
|
|
//
|
|
if ( pDetails->cChannels == 2 )
|
|
{
|
|
DWORD dwChanLeft, dwChanRight;
|
|
|
|
dwChanRight = ((DWORD *)pDetails->paDetails)[0];
|
|
dwChanLeft = ((DWORD *)pDetails->paDetails)[1];
|
|
dwVal = ((dwChanLeft & 0xffff) << 16) | ( dwChanRight & 0xffff );
|
|
} else {
|
|
dwVal = ((DWORD *)pDetails->paDetails)[0];
|
|
dwVal |= dwVal << 16;
|
|
}
|
|
rv = waveSetVolume( NULL, dwVal );
|
|
}
|
|
|
|
} else {
|
|
if ( 2 != pDetails->cChannels &&
|
|
1 != pDetails->cChannels )
|
|
{
|
|
TRC( ERR, "Unexpected # channels\n" );
|
|
rv = MMSYSERR_INVALPARAM;
|
|
goto exitpt;
|
|
}
|
|
//
|
|
// get the new volume value
|
|
//
|
|
if ( RDP_MXDID_MUTE == pDetails->dwControlID )
|
|
{
|
|
rv = waveGetMute( NULL, &dwVal );
|
|
((DWORD *)(pDetails->paDetails))[0] = dwVal;
|
|
if ( 2 == pDetails->cChannels )
|
|
{
|
|
((DWORD *)(pDetails->paDetails))[1] = dwVal;
|
|
}
|
|
} else {
|
|
//
|
|
// get the volume
|
|
//
|
|
rv = waveGetVolume( NULL, &dwVal );
|
|
TRC( ALV, "GET Volume=0x%x\n", dwVal );
|
|
if ( 2 == pDetails->cChannels )
|
|
{
|
|
((DWORD *)(pDetails->paDetails))[0] = dwVal & 0xffff; // right
|
|
((DWORD *)(pDetails->paDetails))[1] = dwVal >> 16; // left
|
|
} else
|
|
{
|
|
// get an average
|
|
//
|
|
((DWORD *)(pDetails->paDetails))[0] =
|
|
(( dwVal & 0xffff ) + ( dwVal >> 16 )) / 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
TRC( ERR, "Mixer%sControlDetails fdwDetails=0x%x\n",
|
|
(bGet)?"Get":"Set", fdwDetails );
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
mxdMessage(
|
|
UINT uDeviceID,
|
|
UINT uMessage,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwParam1,
|
|
DWORD_PTR dwParam2
|
|
)
|
|
{
|
|
DWORD rv = MMSYSERR_ERROR;
|
|
PMIXERCTX pMixer = (PMIXERCTX)dwUser;
|
|
|
|
switch ( uMessage )
|
|
{
|
|
|
|
case MXDM_GETNUMDEVS:
|
|
TRC( ALV, "WXDM_GETNUMDEVS\n");
|
|
return 1;
|
|
|
|
case MXDM_GETDEVCAPS:
|
|
TRC( ALV, "MXDM_GETDEVCAPS\n" );
|
|
rv = RDPMixerGetDevCaps( pMixer, (PMIXERCAPS)dwParam1, dwParam2 );
|
|
break;
|
|
|
|
case MXDM_OPEN:
|
|
TRC( ALV, "MXDM_OPEN\n" );
|
|
rv = RDPMixerOpen( (PMIXERCTX *)dwUser,
|
|
(PMIXEROPENDESC)dwParam1,
|
|
dwParam2 );
|
|
break;
|
|
|
|
case MXDM_CLOSE:
|
|
TRC( ALV, "MXDM_CLOSE\n" );
|
|
rv = RDPMixerClose( pMixer );
|
|
break;
|
|
|
|
case MXDM_GETLINEINFO:
|
|
TRC( ALV, "MXDM_GETLINEINFO\n" );
|
|
rv = RDPMixerGetLineInfo( pMixer, (PMIXERLINE)dwParam1, dwParam2 );
|
|
break;
|
|
|
|
case MXDM_GETLINECONTROLS:
|
|
TRC( ALV, "MXDM_GETLINECONTROLS\n" );
|
|
rv = RDPMixerGetLineControls( pMixer, (PMIXERLINECONTROLS)dwParam1, dwParam2 );
|
|
break;
|
|
|
|
case MXDM_GETCONTROLDETAILS:
|
|
TRC( ALV, "MXDM_GETCONTROLDETAILS\n" );
|
|
rv = RDPMixerGetSetControlDetails( pMixer, (PMIXERCONTROLDETAILS)dwParam1, dwParam2, TRUE );
|
|
break;
|
|
|
|
case MXDM_SETCONTROLDETAILS:
|
|
TRC( ALV, "MXDM_SETCONTROLDETAILS\n" );
|
|
rv = RDPMixerGetSetControlDetails( pMixer, (PMIXERCONTROLDETAILS)dwParam1, dwParam2, FALSE );
|
|
break;
|
|
|
|
default:
|
|
TRC( ALV, "Message id=%d\n", uMessage );
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|