22#include <freerdp/config.h>
32#include <winpr/wtsapi.h>
33#include <winpr/cmdline.h>
34#include <freerdp/freerdp.h>
35#include <freerdp/addin.h>
36#include <freerdp/client/audin.h>
38#include "audin_main.h"
41#ifndef WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE
42#define WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE 0x0010
56 PWAVEFORMATEX pwfx_cur;
59 UINT32 frames_per_packet;
60 rdpContext* rdpcontext;
64static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
65 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
67 AudinWinmmDevice* winmm = (AudinWinmmDevice*)dwInstance;
69 UINT error = CHANNEL_RC_OK;
78 pWaveHdr = (WAVEHDR*)dwParam1;
80 if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
82 if (pWaveHdr->dwBytesRecorded &&
83 !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
86 format.cbSize = winmm->pwfx_cur->cbSize;
87 format.nBlockAlign = winmm->pwfx_cur->nBlockAlign;
88 format.nAvgBytesPerSec = winmm->pwfx_cur->nAvgBytesPerSec;
89 format.nChannels = winmm->pwfx_cur->nChannels;
90 format.nSamplesPerSec = winmm->pwfx_cur->nSamplesPerSec;
91 format.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
92 format.wFormatTag = winmm->pwfx_cur->wFormatTag;
94 if ((error = winmm->receive(&format, pWaveHdr->lpData,
95 pWaveHdr->dwBytesRecorded, winmm->user_data)))
98 mmResult = waveInAddBuffer(hWaveIn, pWaveHdr,
sizeof(WAVEHDR));
100 if (mmResult != MMSYSERR_NOERROR)
101 error = ERROR_INTERNAL_ERROR;
114 if (error && winmm->rdpcontext)
115 setChannelError(winmm->rdpcontext, error,
"waveInProc reported an error");
118static BOOL log_mmresult(AudinWinmmDevice* winmm,
const char* what, MMRESULT result)
120 if (result != MMSYSERR_NOERROR)
122 CHAR buffer[8192] = WINPR_C_ARRAY_INIT;
123 CHAR msg[8192] = WINPR_C_ARRAY_INIT;
124 CHAR cmsg[8192] = WINPR_C_ARRAY_INIT;
125 waveInGetErrorTextA(result, buffer,
sizeof(buffer));
127 _snprintf(msg,
sizeof(msg) - 1,
"%s failed. %" PRIu32
" [%s]", what, result, buffer);
128 _snprintf(cmsg,
sizeof(cmsg) - 1,
"audin_winmm_thread_func reported an error '%s'", msg);
129 WLog_Print(winmm->log, WLOG_DEBUG,
"%s", msg);
130 if (winmm->rdpcontext)
131 setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, cmsg);
137static BOOL test_format_supported(
const PWAVEFORMATEX pwfx)
140 WAVEINCAPSA caps = WINPR_C_ARRAY_INIT;
142 rc = waveInGetDevCapsA(WAVE_MAPPER, &caps,
sizeof(caps));
143 if (rc != MMSYSERR_NOERROR)
146 switch (pwfx->nChannels)
149 if ((caps.dwFormats &
150 (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08 |
151 WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_96M16)) == 0)
155 if ((caps.dwFormats &
156 (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08 |
157 WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_96S16)) == 0)
164 rc = waveInOpen(
nullptr, WAVE_MAPPER, pwfx, 0, 0,
165 WAVE_FORMAT_QUERY | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
166 return (rc == MMSYSERR_NOERROR);
169static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
171 AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
172 char* buffer =
nullptr;
174 WAVEHDR waveHdr[4] = WINPR_C_ARRAY_INIT;
180 rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
182 CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
183 if (!log_mmresult(winmm,
"waveInOpen", rc))
184 return ERROR_INTERNAL_ERROR;
188 (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
192 for (
int i = 0; i < 4; i++)
194 buffer = (
char*)malloc(size);
197 return CHANNEL_RC_NO_MEMORY;
199 waveHdr[i].dwBufferLength = size;
200 waveHdr[i].dwFlags = 0;
201 waveHdr[i].lpData = buffer;
202 rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i],
sizeof(waveHdr[i]));
204 if (!log_mmresult(winmm,
"waveInPrepareHeader", rc))
208 rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i],
sizeof(waveHdr[i]));
210 if (!log_mmresult(winmm,
"waveInAddBuffer", rc))
215 rc = waveInStart(winmm->hWaveIn);
217 if (!log_mmresult(winmm,
"waveInStart", rc))
221 status = WaitForSingleObject(winmm->stopEvent, INFINITE);
223 if (status == WAIT_FAILED)
225 WLog_Print(winmm->log, WLOG_DEBUG,
"WaitForSingleObject failed.");
227 if (winmm->rdpcontext)
228 setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
229 "audin_winmm_thread_func reported an error");
232 rc = waveInReset(winmm->hWaveIn);
234 if (!log_mmresult(winmm,
"waveInReset", rc))
238 for (
int i = 0; i < 4; i++)
240 rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i],
sizeof(waveHdr[i]));
242 if (!log_mmresult(winmm,
"waveInUnprepareHeader", rc))
246 free(waveHdr[i].lpData);
249 rc = waveInClose(winmm->hWaveIn);
251 if (!log_mmresult(winmm,
"waveInClose", rc))
255 winmm->hWaveIn =
nullptr;
264static UINT audin_winmm_free(IAudinDevice* device)
266 AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
269 return ERROR_INVALID_PARAMETER;
271 for (UINT32 i = 0; i < winmm->cFormats; i++)
273 free(winmm->ppwfx[i]);
277 free(winmm->device_name);
279 return CHANNEL_RC_OK;
287static UINT audin_winmm_close(IAudinDevice* device)
290 UINT error = CHANNEL_RC_OK;
291 AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
294 return ERROR_INVALID_PARAMETER;
296 (void)SetEvent(winmm->stopEvent);
297 status = WaitForSingleObject(winmm->thread, INFINITE);
299 if (status == WAIT_FAILED)
301 error = GetLastError();
302 WLog_Print(winmm->log, WLOG_ERROR,
"WaitForSingleObject failed with error %" PRIu32
"!",
307 (void)CloseHandle(winmm->thread);
308 (void)CloseHandle(winmm->stopEvent);
309 winmm->thread =
nullptr;
310 winmm->stopEvent =
nullptr;
311 winmm->receive =
nullptr;
312 winmm->user_data =
nullptr;
321static UINT audin_winmm_set_format(IAudinDevice* device,
const AUDIO_FORMAT* format,
322 UINT32 FramesPerPacket)
324 AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
326 if (!winmm || !format)
327 return ERROR_INVALID_PARAMETER;
329 winmm->frames_per_packet = FramesPerPacket;
331 for (UINT32 i = 0; i < winmm->cFormats; i++)
333 const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
334 if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
335 (ppwfx->wBitsPerSample == format->wBitsPerSample) &&
336 (ppwfx->nSamplesPerSec == format->nSamplesPerSec))
340 if (ppwfx->nChannels > 1)
342 ppwfx->nChannels = 1;
345 if (ppwfx->nBlockAlign != 2)
347 ppwfx->nBlockAlign = 2;
348 ppwfx->nAvgBytesPerSec = ppwfx->nSamplesPerSec * ppwfx->nBlockAlign;
351 if (!test_format_supported(ppwfx))
352 return ERROR_INVALID_PARAMETER;
353 winmm->pwfx_cur = ppwfx;
354 return CHANNEL_RC_OK;
358 return ERROR_INVALID_PARAMETER;
361static BOOL audin_winmm_format_supported(IAudinDevice* device,
const AUDIO_FORMAT* format)
363 AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
367 if (!winmm || !format)
370 if (format->wFormatTag != WAVE_FORMAT_PCM)
373 if (format->nChannels != 1)
376 pwfx = (PWAVEFORMATEX)malloc(
sizeof(WAVEFORMATEX) + format->cbSize);
381 pwfx->cbSize = format->cbSize;
382 pwfx->wFormatTag = format->wFormatTag;
383 pwfx->nChannels = format->nChannels;
384 pwfx->nSamplesPerSec = format->nSamplesPerSec;
385 pwfx->nBlockAlign = format->nBlockAlign;
386 pwfx->wBitsPerSample = format->wBitsPerSample;
387 data = (BYTE*)pwfx +
sizeof(WAVEFORMATEX);
388 memcpy(data, format->data, format->cbSize);
390 pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
392 if (!test_format_supported(pwfx))
395 if (winmm->cFormats >= winmm->ppwfx_size)
397 PWAVEFORMATEX* tmp_ppwfx;
398 tmp_ppwfx = realloc(winmm->ppwfx,
sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
403 winmm->ppwfx_size *= 2;
404 winmm->ppwfx = tmp_ppwfx;
407 winmm->ppwfx[winmm->cFormats++] = pwfx;
420static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
422 AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
424 if (!winmm || !receive || !user_data)
425 return ERROR_INVALID_PARAMETER;
427 winmm->receive = receive;
428 winmm->user_data = user_data;
430 if (!(winmm->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
432 WLog_Print(winmm->log, WLOG_ERROR,
"CreateEvent failed!");
433 return ERROR_INTERNAL_ERROR;
436 if (!(winmm->thread = CreateThread(
nullptr, 0, audin_winmm_thread_func, winmm, 0,
nullptr)))
438 WLog_Print(winmm->log, WLOG_ERROR,
"CreateThread failed!");
439 (void)CloseHandle(winmm->stopEvent);
440 winmm->stopEvent =
nullptr;
441 return ERROR_INTERNAL_ERROR;
444 return CHANNEL_RC_OK;
452static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device,
const ADDIN_ARGV* args)
457 AudinWinmmDevice* winmm = (AudinWinmmDevice*)device;
459 {
"dev", COMMAND_LINE_VALUE_REQUIRED,
"<device>",
nullptr,
nullptr, -1,
nullptr,
460 "audio device name" },
461 {
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr }
465 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
466 status = CommandLineParseArgumentsA(args->argc, args->argv, audin_winmm_args, flags, winmm,
468 arg = audin_winmm_args;
472 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
475 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"dev")
477 winmm->device_name = _strdup(arg->Value);
479 if (!winmm->device_name)
481 WLog_Print(winmm->log, WLOG_ERROR,
"_strdup failed!");
482 return CHANNEL_RC_NO_MEMORY;
485 CommandLineSwitchEnd(arg)
486 }
while ((arg = CommandLineFindNextArgumentA(arg)) !=
nullptr);
488 return CHANNEL_RC_OK;
496FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_audin_client_subsystem_entry(
500 AudinWinmmDevice* winmm;
503 if (waveInGetNumDevs() == 0)
505 WLog_Print(WLog_Get(TAG), WLOG_ERROR,
"No microphone available!");
506 return ERROR_DEVICE_NOT_AVAILABLE;
509 winmm = (AudinWinmmDevice*)calloc(1,
sizeof(AudinWinmmDevice));
513 WLog_ERR(TAG,
"calloc failed!");
514 return CHANNEL_RC_NO_MEMORY;
517 winmm->log = WLog_Get(TAG);
518 winmm->iface.Open = audin_winmm_open;
519 winmm->iface.FormatSupported = audin_winmm_format_supported;
520 winmm->iface.SetFormat = audin_winmm_set_format;
521 winmm->iface.Close = audin_winmm_close;
522 winmm->iface.Free = audin_winmm_free;
523 winmm->rdpcontext = pEntryPoints->rdpcontext;
524 args = pEntryPoints->args;
526 if ((error = audin_winmm_parse_addin_args(winmm, args)))
528 WLog_Print(winmm->log, WLOG_ERROR,
529 "audin_winmm_parse_addin_args failed with error %" PRIu32
"!", error);
533 if (!winmm->device_name)
535 winmm->device_name = _strdup(
"default");
537 if (!winmm->device_name)
539 WLog_Print(winmm->log, WLOG_ERROR,
"_strdup failed!");
540 error = CHANNEL_RC_NO_MEMORY;
545 winmm->ppwfx_size = 10;
546 winmm->ppwfx = calloc(winmm->ppwfx_size,
sizeof(PWAVEFORMATEX));
550 WLog_Print(winmm->log, WLOG_ERROR,
"malloc failed!");
551 error = CHANNEL_RC_NO_MEMORY;
555 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
557 WLog_Print(winmm->log, WLOG_ERROR,
"RegisterAudinDevice failed with error %" PRIu32
"!",
562 return CHANNEL_RC_OK;
565 free(winmm->device_name);