FreeRDP
rdpsnd_winmm.c
1 
24 #include <freerdp/config.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include <windows.h>
31 #include <mmsystem.h>
32 
33 #include <winpr/crt.h>
34 #include <winpr/wtsapi.h>
35 #include <winpr/cmdline.h>
36 #include <winpr/sysinfo.h>
37 
38 #include <freerdp/types.h>
39 #include <freerdp/channels/log.h>
40 
41 #include "rdpsnd_main.h"
42 
43 typedef struct
44 {
45  rdpsndDevicePlugin device;
46 
47  HWAVEOUT hWaveOut;
48  WAVEFORMATEX format;
49  UINT32 volume;
50  wLog* log;
51  UINT32 latency;
52  HANDLE hThread;
53  DWORD threadId;
55 } rdpsndWinmmPlugin;
56 
57 static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out)
58 {
59  if (!in || !out)
60  return FALSE;
61 
62  ZeroMemory(out, sizeof(WAVEFORMATEX));
63  out->wFormatTag = WAVE_FORMAT_PCM;
64  out->nChannels = in->nChannels;
65  out->nSamplesPerSec = in->nSamplesPerSec;
66 
67  switch (in->wFormatTag)
68  {
69  case WAVE_FORMAT_PCM:
70  out->wBitsPerSample = in->wBitsPerSample;
71  break;
72 
73  default:
74  return FALSE;
75  }
76 
77  out->nBlockAlign = out->nChannels * out->wBitsPerSample / 8;
78  out->nAvgBytesPerSec = out->nSamplesPerSec * out->nBlockAlign;
79  return TRUE;
80 }
81 
82 static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
83  UINT32 latency)
84 {
85  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
86 
87  winmm->latency = latency;
88  if (!rdpsnd_winmm_convert_format(format, &winmm->format))
89  return FALSE;
90 
91  return TRUE;
92 }
93 
94 static DWORD WINAPI waveOutProc(LPVOID lpParameter)
95 {
96  MSG msg;
97  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)lpParameter;
98  while (GetMessage(&msg, NULL, 0, 0))
99  {
100  if (msg.message == MM_WOM_CLOSE)
101  {
102  /* device was closed - exit thread */
103  break;
104  }
105  else if (msg.message == MM_WOM_DONE)
106  {
107  /* free buffer */
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);
113  free(waveHdr);
114  }
115  }
116 
117  return 0;
118 }
119 
120 static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
121  UINT32 latency)
122 {
123  MMRESULT mmResult;
124  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
125 
126  if (winmm->hWaveOut)
127  return TRUE;
128 
129  if (!rdpsnd_winmm_set_format(device, format, latency))
130  return FALSE;
131 
132  winmm->hThread = CreateThread(NULL, 0, waveOutProc, winmm, 0, &winmm->threadId);
133  if (!winmm->hThread)
134  {
135  WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed: %" PRIu32 "", GetLastError());
136  return FALSE;
137  }
138 
139  mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format,
140  (DWORD_PTR)winmm->threadId, 0, CALLBACK_THREAD);
141 
142  if (mmResult != MMSYSERR_NOERROR)
143  {
144  WLog_Print(winmm->log, WLOG_ERROR, "waveOutOpen failed: %" PRIu32 "", mmResult);
145  return FALSE;
146  }
147 
148  mmResult = waveOutSetVolume(winmm->hWaveOut, winmm->volume);
149 
150  if (mmResult != MMSYSERR_NOERROR)
151  {
152  WLog_Print(winmm->log, WLOG_ERROR, "waveOutSetVolume failed: %" PRIu32 "", mmResult);
153  return FALSE;
154  }
155 
156  return TRUE;
157 }
158 
159 static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
160 {
161  MMRESULT mmResult;
162  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
163 
164  if (winmm->hWaveOut)
165  {
166  EnterCriticalSection(&winmm->cs);
167 
168  mmResult = waveOutReset(winmm->hWaveOut);
169  if (mmResult != MMSYSERR_NOERROR)
170  WLog_Print(winmm->log, WLOG_ERROR, "waveOutReset failure: %" PRIu32 "", mmResult);
171 
172  mmResult = waveOutClose(winmm->hWaveOut);
173  if (mmResult != MMSYSERR_NOERROR)
174  WLog_Print(winmm->log, WLOG_ERROR, "waveOutClose failure: %" PRIu32 "", mmResult);
175 
176  LeaveCriticalSection(&winmm->cs);
177 
178  winmm->hWaveOut = NULL;
179  }
180 
181  if (winmm->hThread)
182  {
183  (void)WaitForSingleObject(winmm->hThread, INFINITE);
184  (void)CloseHandle(winmm->hThread);
185  winmm->hThread = NULL;
186  }
187 }
188 
189 static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
190 {
191  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
192 
193  if (winmm)
194  {
195  rdpsnd_winmm_close(device);
196  DeleteCriticalSection(&winmm->cs);
197  free(winmm);
198  }
199 }
200 
201 static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
202 {
203  MMRESULT result;
204  WAVEFORMATEX out;
205 
206  WINPR_UNUSED(device);
207  if (rdpsnd_winmm_convert_format(format, &out))
208  {
209  result = waveOutOpen(NULL, WAVE_MAPPER, &out, 0, 0, WAVE_FORMAT_QUERY);
210 
211  if (result == MMSYSERR_NOERROR)
212  return TRUE;
213  }
214 
215  return FALSE;
216 }
217 
218 static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
219 {
220  MMRESULT mmResult;
221  DWORD dwVolume = UINT32_MAX;
222  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
223 
224  if (!winmm->hWaveOut)
225  return dwVolume;
226 
227  mmResult = waveOutGetVolume(winmm->hWaveOut, &dwVolume);
228  if (mmResult != MMSYSERR_NOERROR)
229  {
230  WLog_Print(winmm->log, WLOG_ERROR, "waveOutGetVolume failure: %" PRIu32 "", mmResult);
231  dwVolume = UINT32_MAX;
232  }
233  return dwVolume;
234 }
235 
236 static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
237 {
238  MMRESULT mmResult;
239  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
240  winmm->volume = value;
241 
242  if (!winmm->hWaveOut)
243  return TRUE;
244 
245  mmResult = waveOutSetVolume(winmm->hWaveOut, value);
246  if (mmResult != MMSYSERR_NOERROR)
247  {
248  WLog_Print(winmm->log, WLOG_ERROR, "waveOutGetVolume failure: %" PRIu32 "", mmResult);
249  return FALSE;
250  }
251  return TRUE;
252 }
253 
254 static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
255 {
256  MMRESULT mmResult;
257  LPWAVEHDR lpWaveHdr;
258  rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
259 
260  if (!winmm->hWaveOut)
261  return 0;
262 
263  if (size > UINT32_MAX)
264  return 0;
265 
266  lpWaveHdr = (LPWAVEHDR)calloc(1, sizeof(WAVEHDR));
267  if (!lpWaveHdr)
268  return 0;
269 
270  lpWaveHdr->dwFlags = 0;
271  lpWaveHdr->dwLoops = 0;
272  lpWaveHdr->lpData = malloc(size);
273  if (!lpWaveHdr->lpData)
274  goto fail;
275  memcpy(lpWaveHdr->lpData, data, size);
276  lpWaveHdr->dwBufferLength = (DWORD)size;
277 
278  EnterCriticalSection(&winmm->cs);
279 
280  mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
281  if (mmResult != MMSYSERR_NOERROR)
282  {
283  WLog_Print(winmm->log, WLOG_ERROR, "waveOutPrepareHeader failure: %" PRIu32 "", mmResult);
284  goto failCS;
285  }
286 
287  mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
288  if (mmResult != MMSYSERR_NOERROR)
289  {
290  WLog_Print(winmm->log, WLOG_ERROR, "waveOutWrite failure: %" PRIu32 "", mmResult);
291  waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
292  goto failCS;
293  }
294 
295  LeaveCriticalSection(&winmm->cs);
296  return winmm->latency;
297 failCS:
298  LeaveCriticalSection(&winmm->cs);
299 fail:
300  if (lpWaveHdr)
301  free(lpWaveHdr->lpData);
302  free(lpWaveHdr);
303  return 0;
304 }
305 
306 static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, const ADDIN_ARGV* args)
307 {
308  WINPR_UNUSED(device);
309  WINPR_UNUSED(args);
310 }
311 
317 FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_rdpsnd_client_subsystem_entry(
319 {
320  const ADDIN_ARGV* args;
321  rdpsndWinmmPlugin* winmm;
322 
323  if (waveOutGetNumDevs() == 0)
324  {
325  WLog_Print(WLog_Get(TAG), WLOG_ERROR, "No sound playback device available!");
326  return ERROR_DEVICE_NOT_AVAILABLE;
327  }
328 
329  winmm = (rdpsndWinmmPlugin*)calloc(1, sizeof(rdpsndWinmmPlugin));
330  if (!winmm)
331  return CHANNEL_RC_NO_MEMORY;
332 
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);
342 
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;
348 }
Definition: client/rdpsnd.h:76