Windows2003-3790/termsrv/rdpsnd/rdpwave.c

4349 lines
100 KiB
C

/////////////////////////////////////////////////////////////////////
//
// 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;
}