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] = { 0 };
 
  123    CHAR msg[8192] = { 0 };
 
  124    CHAR cmsg[8192] = { 0 };
 
  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 = { 0 };
 
  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(NULL, 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;
 
  174  WAVEHDR waveHdr[4] = { 0 };
 
  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 = NULL;
 
  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 = NULL;
 
  310  winmm->stopEvent = NULL;
 
  311  winmm->receive = NULL;
 
  312  winmm->user_data = NULL;
 
  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(NULL, TRUE, FALSE, NULL)))
 
  432    WLog_Print(winmm->log, WLOG_ERROR, 
"CreateEvent failed!");
 
  433    return ERROR_INTERNAL_ERROR;
 
  436  if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
 
  438    WLog_Print(winmm->log, WLOG_ERROR, 
"CreateThread failed!");
 
  439    (void)CloseHandle(winmm->stopEvent);
 
  440    winmm->stopEvent = NULL;
 
  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                                                 NULL, NULL, -1, NULL, 
"audio device name" },
 
  460                                               { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
 
  463      COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
 
  464  status = CommandLineParseArgumentsA(args->argc, args->argv, audin_winmm_args, flags, winmm,
 
  466  arg = audin_winmm_args;
 
  470    if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
 
  473    CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, 
"dev")
 
  475      winmm->device_name = _strdup(arg->Value);
 
  477      if (!winmm->device_name)
 
  479        WLog_Print(winmm->log, WLOG_ERROR, 
"_strdup failed!");
 
  480        return CHANNEL_RC_NO_MEMORY;
 
  483    CommandLineSwitchEnd(arg)
 
  484  } 
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
 
  486  return CHANNEL_RC_OK;
 
  494FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_audin_client_subsystem_entry(
 
  498  AudinWinmmDevice* winmm;
 
  501  if (waveInGetNumDevs() == 0)
 
  503    WLog_Print(WLog_Get(TAG), WLOG_ERROR, 
"No microphone available!");
 
  504    return ERROR_DEVICE_NOT_AVAILABLE;
 
  507  winmm = (AudinWinmmDevice*)calloc(1, 
sizeof(AudinWinmmDevice));
 
  511    WLog_ERR(TAG, 
"calloc failed!");
 
  512    return CHANNEL_RC_NO_MEMORY;
 
  515  winmm->log = WLog_Get(TAG);
 
  516  winmm->iface.Open = audin_winmm_open;
 
  517  winmm->iface.FormatSupported = audin_winmm_format_supported;
 
  518  winmm->iface.SetFormat = audin_winmm_set_format;
 
  519  winmm->iface.Close = audin_winmm_close;
 
  520  winmm->iface.Free = audin_winmm_free;
 
  521  winmm->rdpcontext = pEntryPoints->rdpcontext;
 
  522  args = pEntryPoints->args;
 
  524  if ((error = audin_winmm_parse_addin_args(winmm, args)))
 
  526    WLog_Print(winmm->log, WLOG_ERROR,
 
  527               "audin_winmm_parse_addin_args failed with error %" PRIu32 
"!", error);
 
  531  if (!winmm->device_name)
 
  533    winmm->device_name = _strdup(
"default");
 
  535    if (!winmm->device_name)
 
  537      WLog_Print(winmm->log, WLOG_ERROR, 
"_strdup failed!");
 
  538      error = CHANNEL_RC_NO_MEMORY;
 
  543  winmm->ppwfx_size = 10;
 
  544  winmm->ppwfx = calloc(winmm->ppwfx_size, 
sizeof(PWAVEFORMATEX));
 
  548    WLog_Print(winmm->log, WLOG_ERROR, 
"malloc failed!");
 
  549    error = CHANNEL_RC_NO_MEMORY;
 
  553  if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
 
  555    WLog_Print(winmm->log, WLOG_ERROR, 
"RegisterAudinDevice failed with error %" PRIu32 
"!",
 
  560  return CHANNEL_RC_OK;
 
  563  free(winmm->device_name);