24 #include <freerdp/config.h>
33 #include <winpr/crt.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"
45 rdpsndDevicePlugin device;
57 static BOOL rdpsnd_winmm_convert_format(
const AUDIO_FORMAT* in, WAVEFORMATEX* out)
62 ZeroMemory(out,
sizeof(WAVEFORMATEX));
63 out->wFormatTag = WAVE_FORMAT_PCM;
64 out->nChannels = in->nChannels;
65 out->nSamplesPerSec = in->nSamplesPerSec;
67 switch (in->wFormatTag)
70 out->wBitsPerSample = in->wBitsPerSample;
77 out->nBlockAlign = out->nChannels * out->wBitsPerSample / 8;
78 out->nAvgBytesPerSec = out->nSamplesPerSec * out->nBlockAlign;
82 static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
85 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
87 winmm->latency = latency;
88 if (!rdpsnd_winmm_convert_format(format, &winmm->format))
94 static DWORD WINAPI waveOutProc(LPVOID lpParameter)
97 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)lpParameter;
98 while (GetMessage(&msg, NULL, 0, 0))
100 if (msg.message == MM_WOM_CLOSE)
105 else if (msg.message == MM_WOM_DONE)
108 LPWAVEHDR waveHdr = (LPWAVEHDR)msg.lParam;
109 EnterCriticalSection(&winmm->cs);
110 waveOutUnprepareHeader((HWAVEOUT)msg.wParam, waveHdr,
sizeof(WAVEHDR));
111 LeaveCriticalSection(&winmm->cs);
112 free(waveHdr->lpData);
120 static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
124 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
129 if (!rdpsnd_winmm_set_format(device, format, latency))
132 winmm->hThread = CreateThread(NULL, 0, waveOutProc, winmm, 0, &winmm->threadId);
135 WLog_Print(winmm->log, WLOG_ERROR,
"CreateThread failed: %" PRIu32
"", GetLastError());
139 mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format,
140 (DWORD_PTR)winmm->threadId, 0, CALLBACK_THREAD);
142 if (mmResult != MMSYSERR_NOERROR)
144 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutOpen failed: %" PRIu32
"", mmResult);
148 mmResult = waveOutSetVolume(winmm->hWaveOut, winmm->volume);
150 if (mmResult != MMSYSERR_NOERROR)
152 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutSetVolume failed: %" PRIu32
"", mmResult);
159 static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
162 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
166 EnterCriticalSection(&winmm->cs);
168 mmResult = waveOutReset(winmm->hWaveOut);
169 if (mmResult != MMSYSERR_NOERROR)
170 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutReset failure: %" PRIu32
"", mmResult);
172 mmResult = waveOutClose(winmm->hWaveOut);
173 if (mmResult != MMSYSERR_NOERROR)
174 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutClose failure: %" PRIu32
"", mmResult);
176 LeaveCriticalSection(&winmm->cs);
178 winmm->hWaveOut = NULL;
183 (void)WaitForSingleObject(winmm->hThread, INFINITE);
184 (void)CloseHandle(winmm->hThread);
185 winmm->hThread = NULL;
189 static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
191 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
195 rdpsnd_winmm_close(device);
196 DeleteCriticalSection(&winmm->cs);
201 static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format)
206 WINPR_UNUSED(device);
207 if (rdpsnd_winmm_convert_format(format, &out))
209 result = waveOutOpen(NULL, WAVE_MAPPER, &out, 0, 0, WAVE_FORMAT_QUERY);
211 if (result == MMSYSERR_NOERROR)
218 static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
221 DWORD dwVolume = UINT32_MAX;
222 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
224 if (!winmm->hWaveOut)
227 mmResult = waveOutGetVolume(winmm->hWaveOut, &dwVolume);
228 if (mmResult != MMSYSERR_NOERROR)
230 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutGetVolume failure: %" PRIu32
"", mmResult);
231 dwVolume = UINT32_MAX;
236 static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
239 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
240 winmm->volume = value;
242 if (!winmm->hWaveOut)
245 mmResult = waveOutSetVolume(winmm->hWaveOut, value);
246 if (mmResult != MMSYSERR_NOERROR)
248 WLog_Print(winmm->log, WLOG_ERROR,
"waveOutGetVolume failure: %" PRIu32
"", mmResult);
254 static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
258 rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
260 if (!winmm->hWaveOut)
263 if (size > UINT32_MAX)
266 lpWaveHdr = (LPWAVEHDR)calloc(1,
sizeof(WAVEHDR));
270 lpWaveHdr->dwFlags = 0;
271 lpWaveHdr->dwLoops = 0;
272 lpWaveHdr->lpData = malloc(size);
273 if (!lpWaveHdr->lpData)
275 memcpy(lpWaveHdr->lpData, data, size);
276 lpWaveHdr->dwBufferLength = (DWORD)size;
278 EnterCriticalSection(&winmm->cs);
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));
295 LeaveCriticalSection(&winmm->cs);
296 return winmm->latency;
298 LeaveCriticalSection(&winmm->cs);
301 free(lpWaveHdr->lpData);
306 static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device,
const ADDIN_ARGV* args)
308 WINPR_UNUSED(device);
317 FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_rdpsnd_client_subsystem_entry(
321 rdpsndWinmmPlugin* winmm;
323 if (waveOutGetNumDevs() == 0)
325 WLog_Print(WLog_Get(TAG), WLOG_ERROR,
"No sound playback device available!");
326 return ERROR_DEVICE_NOT_AVAILABLE;
329 winmm = (rdpsndWinmmPlugin*)calloc(1,
sizeof(rdpsndWinmmPlugin));
331 return CHANNEL_RC_NO_MEMORY;
333 winmm->device.Open = rdpsnd_winmm_open;
334 winmm->device.FormatSupported = rdpsnd_winmm_format_supported;
335 winmm->device.GetVolume = rdpsnd_winmm_get_volume;
336 winmm->device.SetVolume = rdpsnd_winmm_set_volume;
337 winmm->device.Play = rdpsnd_winmm_play;
338 winmm->device.Close = rdpsnd_winmm_close;
339 winmm->device.Free = rdpsnd_winmm_free;
340 winmm->log = WLog_Get(TAG);
341 InitializeCriticalSection(&winmm->cs);
343 args = pEntryPoints->args;
344 rdpsnd_winmm_parse_addin_args(&winmm->device, args);
345 winmm->volume = 0xFFFFFFFF;
346 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)winmm);
347 return CHANNEL_RC_OK;