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