Repository: xbmc/xbmc
File: xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp (C++)

Code before:
  if (!m_running) //first time called, pre-fill buffer then start voice
  {
    m_sourceVoice->Stop();
    hr = m_sourceVoice->SubmitSourceBuffer(&xbuffer);
    if (FAILED(hr))
    {
      CLog::LogF(LOGERROR, "voice submit buffer failed due to {}", WASAPIErrToStr(hr));
      delete xbuffer.pContext;
      return 0;
    }
    hr = m_sourceVoice->Start(0, XAUDIO2_COMMIT_NOW);
    if (FAILED(hr))
    {
      CLog::LogF(LOGERROR, "voice start failed due to {}", WASAPIErrToStr(hr));
      m_isDirty = true; //flag new device or re-init needed
      delete xbuffer.pContext;
      return INT_MAX;
    }
    m_sinkFrames += frames;
    m_framesInBuffers += frames;
    m_running = true; //signal that we're processing frames
    return frames;
  }

  /* Get clock time for latency checks */
  QueryPerformanceFrequency(&timerFreq);
  QueryPerformanceCounter(&timerStart);

  /* Wait for Audio Driver to tell us it's got a buffer available */
  //XAUDIO2_VOICE_STATE state;
  //while (m_sourceVoice->GetState(&state), state.BuffersQueued >= XAUDIO_BUFFERS_IN_QUEUE)
  while (m_format.m_frames * XAUDIO_BUFFERS_IN_QUEUE <= m_framesInBuffers.load())
  {
    DWORD eventAudioCallback;
    eventAudioCallback = WaitForSingleObjectEx(m_voiceCallback.mBufferEnd.get(), 1100, TRUE);
    if (eventAudioCallback != WAIT_OBJECT_0)
    {
      CLog::LogF(LOGERROR, "voice buffer timed out");
      delete xbuffer.pContext;
      return INT_MAX;
    }
  }

  if (!m_running)
    return 0;

  QueryPerformanceCounter(&timerStop);
  LONGLONG timerDiff = timerStop.QuadPart - timerStart.QuadPart;
  double timerElapsed = (double) timerDiff * 1000.0 / (double) timerFreq.QuadPart;
  m_avgTimeWaiting += (timerElapsed - m_avgTimeWaiting) * 0.5;


Review comment (performance):
per documentation, "The frequency of the performance counter is fixed at system boot and is consistent across all processors. Therefore, the frequency need only be queried upon application initialization, and the result can be cached." Would you add a commit to make it a class member, or would it be a separate PR?

Code after:
  m_avgTimeWaiting += (timerElapsed - m_avgTimeWaiting) * 0.5;

  if (m_avgTimeWaiting < 3.0)
  {
    CLog::LogF(LOGDEBUG, "Possible AQ Loss: Avg. Time Waiting for Audio Driver callback : {}msec",
               (int)m_avgTimeWaiting);
  }

  hr = m_sourceVoice->SubmitSourceBuffer(&xbuffer);
  if (FAILED(hr))
  {
    CLog::LogF(LOGERROR, "submiting buffer failed due to {}", WASAPIErrToStr(hr));
    delete xbuffer.pContext;
    return INT_MAX;
  }

  m_sinkFrames += frames;
  m_framesInBuffers += frames;

  return frames;
}

void CAESinkXAudio::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force)
{
  HRESULT hr = S_OK;
  CAEDeviceInfo deviceInfo;
  CAEChannelInfo deviceChannels;
  WAVEFORMATEXTENSIBLE wfxex = {};
  UINT32 eflags = 0; // XAUDIO2_DEBUG_ENGINE;
  IXAudio2MasteringVoice* mMasterVoice = nullptr;
  IXAudio2SourceVoice* mSourceVoice = nullptr;
  Microsoft::WRL::ComPtr<IXAudio2> xaudio2;

  hr = XAudio2Create(xaudio2.ReleaseAndGetAddressOf(), eflags);
  if (FAILED(hr))
  {
    CLog::LogF(LOGERROR, "failed to activate XAudio for capability testing ({})",
               WASAPIErrToStr(hr));
    return;
  }

  for(RendererDetail& details : CAESinkFactoryWin::GetRendererDetails())
  {
    deviceInfo.m_channels.Reset();
    deviceInfo.m_dataFormats.clear();
    deviceInfo.m_sampleRates.clear();
    deviceChannels.Reset();

    for (unsigned int c = 0; c < WASAPI_SPEAKER_COUNT; c++)
    {
      if (details.uiChannelMask & WASAPIChannelOrder[c])