24#include <freerdp/config.h>
34#include <winpr/wtsapi.h>
35#include <winpr/cmdline.h>
36#include <winpr/sysinfo.h>
38#include <freerdp/types.h>
39#include <freerdp/channels/log.h>
41#include "rdpsnd_main.h"
46#define WINMM_DEFAULT_PREBUFFER_MS 100
50 rdpsndDevicePlugin device;
63static BOOL rdpsnd_winmm_convert_format(
const AUDIO_FORMAT* in, WAVEFORMATEX* out)
68 ZeroMemory(out,
sizeof(WAVEFORMATEX));
69 out->wFormatTag = WAVE_FORMAT_PCM;
70 out->nChannels = in->nChannels;
71 out->nSamplesPerSec = in->nSamplesPerSec;
73 switch (in->wFormatTag)
76 out->wBitsPerSample = in->wBitsPerSample;
83 out->nBlockAlign = out->nChannels * out->wBitsPerSample / 8;
84 out->nAvgBytesPerSec = out->nSamplesPerSec * out->nBlockAlign;
88static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
91 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
93 winmm->latency = latency;
94 if (!rdpsnd_winmm_convert_format(format, &winmm->format))
100static DWORD WINAPI waveOutProc(LPVOID lpParameter)
103 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)lpParameter;
104 while (GetMessage(&msg,
nullptr, 0, 0))
106 if (msg.message == MM_WOM_CLOSE)
111 else if (msg.message == MM_WOM_DONE)
114 LPWAVEHDR waveHdr = (LPWAVEHDR)msg.lParam;
115 EnterCriticalSection(&winmm->cs);
116 waveOutUnprepareHeader((HWAVEOUT)msg.wParam, waveHdr,
sizeof(WAVEHDR));
117 if (winmm->pending > 0)
119 LeaveCriticalSection(&winmm->cs);
120 free(waveHdr->lpData);
128static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
132 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
137 if (!rdpsnd_winmm_set_format(device, format, latency))
141 winmm->hThread = CreateThread(
nullptr, 0, waveOutProc, winmm, 0, &winmm->threadId);
144 WLog_Print(winmm->log, WLOG_ERROR,
"CreateThread failed: %" PRIu32
"", GetLastError());
148 mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format,
149 (DWORD_PTR)winmm->threadId, 0, CALLBACK_THREAD);
151 if (mmResult != MMSYSERR_NOERROR)
153 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutOpen failed: %" PRIu32
"", mmResult);
157 mmResult = waveOutSetVolume(winmm->hWaveOut, winmm->volume);
159 if (mmResult != MMSYSERR_NOERROR)
161 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutSetVolume failed: %" PRIu32
"", mmResult);
168static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
171 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
175 EnterCriticalSection(&winmm->cs);
177 mmResult = waveOutReset(winmm->hWaveOut);
178 if (mmResult != MMSYSERR_NOERROR)
179 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutReset failure: %" PRIu32
"", mmResult);
181 mmResult = waveOutClose(winmm->hWaveOut);
182 if (mmResult != MMSYSERR_NOERROR)
183 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutClose failure: %" PRIu32
"", mmResult);
185 LeaveCriticalSection(&winmm->cs);
187 winmm->hWaveOut =
nullptr;
192 (void)WaitForSingleObject(winmm->hThread, INFINITE);
193 (void)CloseHandle(winmm->hThread);
194 winmm->hThread =
nullptr;
198static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
200 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
204 rdpsnd_winmm_close(device);
205 DeleteCriticalSection(&winmm->cs);
210static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format)
215 WINPR_UNUSED(device);
216 if (rdpsnd_winmm_convert_format(format, &out))
218 result = waveOutOpen(
nullptr, WAVE_MAPPER, &out, 0, 0, WAVE_FORMAT_QUERY);
220 if (result == MMSYSERR_NOERROR)
227static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
230 DWORD dwVolume = UINT32_MAX;
231 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
233 if (!winmm->hWaveOut)
236 mmResult = waveOutGetVolume(winmm->hWaveOut, &dwVolume);
237 if (mmResult != MMSYSERR_NOERROR)
239 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutGetVolume failure: %" PRIu32
"", mmResult);
240 dwVolume = UINT32_MAX;
245static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
248 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
249 winmm->volume = value;
251 if (!winmm->hWaveOut)
254 mmResult = waveOutSetVolume(winmm->hWaveOut, value);
255 if (mmResult != MMSYSERR_NOERROR)
257 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutGetVolume failure: %" PRIu32
"", mmResult);
264static BOOL rdpsnd_winmm_write(rdpsndWinmmPlugin* winmm,
const BYTE* data,
size_t size)
267 LPWAVEHDR lpWaveHdr = (LPWAVEHDR)calloc(1,
sizeof(WAVEHDR));
271 lpWaveHdr->lpData = malloc(size);
272 if (!lpWaveHdr->lpData)
275 memcpy(lpWaveHdr->lpData, data, size);
277 memset(lpWaveHdr->lpData, (winmm->format.wBitsPerSample == 8) ? 0x80 : 0, size);
278 lpWaveHdr->dwBufferLength = (DWORD)size;
280 mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr,
sizeof(WAVEHDR));
281 if (mmResult != MMSYSERR_NOERROR)
283 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutPrepareHeader failure: %" PRIu32
"", mmResult);
287 mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr,
sizeof(WAVEHDR));
288 if (mmResult != MMSYSERR_NOERROR)
290 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutWrite failure: %" PRIu32
"", mmResult);
291 waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr,
sizeof(WAVEHDR));
298 free(lpWaveHdr->lpData);
303static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
305 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
307 if (!winmm->hWaveOut)
310 if (size > UINT32_MAX)
313 EnterCriticalSection(&winmm->cs);
318 if (winmm->pending == 0)
320 const UINT32 ms = (winmm->latency > 0) ? winmm->latency : WINMM_DEFAULT_PREBUFFER_MS;
321 const size_t align = MAX(winmm->format.nBlockAlign, 1);
322 const size_t silence = (1ull * winmm->format.nAvgBytesPerSec * ms / 1000) / align * align;
324 if ((silence > 0) && !rdpsnd_winmm_write(winmm,
nullptr, silence))
325 WLog_Print(winmm->log, WLOG_WARN,
"could not queue %" PRIu32
" ms of silence", ms);
328 const BOOL rc = rdpsnd_winmm_write(winmm, data, size);
329 LeaveCriticalSection(&winmm->cs);
330 return rc ? winmm->latency : 0;
333static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device,
const ADDIN_ARGV* args)
335 WINPR_UNUSED(device);
344FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_rdpsnd_client_subsystem_entry(
348 rdpsndWinmmPlugin* winmm;
350 if (waveOutGetNumDevs() == 0)
352 WLog_Print(WLog_Get(TAG), WLOG_ERROR,
"No sound playback device available!");
353 return ERROR_DEVICE_NOT_AVAILABLE;
356 winmm = (rdpsndWinmmPlugin*)calloc(1,
sizeof(rdpsndWinmmPlugin));
358 return CHANNEL_RC_NO_MEMORY;
360 winmm->device.Open = rdpsnd_winmm_open;
361 winmm->device.FormatSupported = rdpsnd_winmm_format_supported;
362 winmm->device.GetVolume = rdpsnd_winmm_get_volume;
363 winmm->device.SetVolume = rdpsnd_winmm_set_volume;
364 winmm->device.Play = rdpsnd_winmm_play;
365 winmm->device.Close = rdpsnd_winmm_close;
366 winmm->device.Free = rdpsnd_winmm_free;
367 winmm->log = WLog_Get(TAG);
368 InitializeCriticalSection(&winmm->cs);
370 args = pEntryPoints->args;
371 rdpsnd_winmm_parse_addin_args(&winmm->device, args);
372 winmm->volume = 0xFFFFFFFF;
373 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)winmm);
374 return CHANNEL_RC_OK;