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

Code before:
    {
      // Requested buffer was too large, try again with a duration reduced by the min device period
      audioSinkBufferDurationHns -= hnsMinimumDevicePeriod;
      needNewClient = false;
    }
    else
    {
      break;
    }
  }

  if (FAILED(hr))
  {
    CLog::LogF(LOGERROR, "Failed to initialize WASAPI in exclusive mode - {}.",
               CWIN32Util::FormatHRESULT(hr));
    CLog::Log(LOGDEBUG, "  Sample Rate     : {}", wfxex.Format.nSamplesPerSec);
    CLog::Log(LOGDEBUG, "  Sample Format   : {}", CAEUtil::DataFormatToStr(format.m_dataFormat));
    CLog::Log(LOGDEBUG, "  Bits Per Sample : {}", wfxex.Format.wBitsPerSample);
    CLog::Log(LOGDEBUG, "  Valid Bits/Samp : {}", wfxex.Samples.wValidBitsPerSample);
    CLog::Log(LOGDEBUG, "  Channel Count   : {}", wfxex.Format.nChannels);
    CLog::Log(LOGDEBUG, "  Block Align     : {}", wfxex.Format.nBlockAlign);
    CLog::Log(LOGDEBUG, "  Avg. Bytes Sec  : {}", wfxex.Format.nAvgBytesPerSec);
    CLog::Log(LOGDEBUG, "  Samples/Block   : {}", wfxex.Samples.wSamplesPerBlock);
    CLog::Log(LOGDEBUG, "  Format cBSize   : {}", wfxex.Format.cbSize);
    CLog::Log(LOGDEBUG, "  Channel Layout  : {}", ((std::string)format.m_channelLayout));
    CLog::Log(LOGDEBUG, "  Enc. Channels   : {}", wfxex_iec61937.dwEncodedChannelCount);
    CLog::Log(LOGDEBUG, "  Enc. Samples/Sec: {}", wfxex_iec61937.dwEncodedSamplesPerSec);
    CLog::Log(LOGDEBUG, "  Channel Mask    : {}", wfxex.dwChannelMask);
    CLog::Log(LOGDEBUG, "  Periodicity (ms): {:.1f}",
              static_cast<float>(audioSinkBufferDurationHns) / 10000.0f);
    return false;
  }

  // Latency of WASAPI buffers in event-driven mode is equal to the returned value
  // of GetStreamLatency converted from 100ns intervals to seconds then multiplied
  // by two as there are two equally-sized buffers and playback starts when the
  // second buffer is filled.
  // m_sinkLatency should match with nominal delay when all is stabilized:
  // e.g: if period is 20ms, delay is 40 ms and latency also 40 ms
  REFERENCE_TIME hnsLatency{};
  hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
  if (FAILED(hr))
  {
    CLog::LogF(LOGERROR, "GetStreamLatency Failed : {}", CWIN32Util::FormatHRESULT(hr));
    return false;
  }

  m_sinkLatency = static_cast<double>(hnsLatency * 2) / 10000000; // 100ns intervals to s

  // Get the buffer size and calculate the frames for AE
  hr = m_pAudioClient->GetBufferSize(&m_uiBufferLen);

Review comment (suggestion):
This removed code was OK.

Wait half of Delay() (one buffer) to WASAPI finish to play current buffer.

For period 20ms --> GetDelay() = 40ms * 500 = 0.02 seg

`KODI::TIME::Sleep` blocks/wait 20ms to assure buffer of 20ms is finished play.

Easy and effective. The new code adds much complication and doesn't solve the pending thigs: playback of pending samples on m_buffer.

Add silence is useless because we want playback real audio samples pending (not silence). If the workarround is for wait the equivalent time only, `KODI::TIME::Sleep` is fine.

Since you're here and adding hundreds of code lines, at least, I think the actual m_buffer should be copied instead of adding silence. Since the samples are at the beginning of m_buffer, one approach would be `memset` 0 the rest of the buffer and send it (only when `m_bufferPtr != 0`).

samples missing on `m_buffer` are always `m_format.m_frames - m_bufferPtr`.

Then is need generate a silence for the missing samples in m_buffer --> wirte zeros of size
`(m_format.m_frames - m_bufferPtr) * m_format.m_frameSize` = bytes of silence to complete m_buffer

The silence can be write in the same `m_buffer` and then pass to audio driver.

A better approach is use `CAEUtil::GenerateSilence`  to generate silence:

e.g:
https://github.com/xbmc/xbmc/blob/b08ff8dda24b0d05e7e078300428702d1fe8820d/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp#L825

where 

`frames = m_format.m_frames - m_bufferPtr`


(This is only necessary if there are audio samples pending in the m_buffer (but not one full buffer). Otherwise, there's no point in creating artificial silence)




Code after:
    CLog::Log(LOGDEBUG, "  Channel Layout  : {}", ((std::string)format.m_channelLayout));
    CLog::Log(LOGDEBUG, "  Enc. Channels   : {}", wfxex_iec61937.dwEncodedChannelCount);
    CLog::Log(LOGDEBUG, "  Enc. Samples/Sec: {}", wfxex_iec61937.dwEncodedSamplesPerSec);
    CLog::Log(LOGDEBUG, "  Channel Mask    : {}", wfxex.dwChannelMask);
    CLog::Log(LOGDEBUG, "  Periodicity (ms): {:.1f}", static_cast<float>(bufferHns) / 10000.0f);
    return false;
  }

  // Latency of WASAPI buffers in event-driven mode is equal to the returned value
  // of GetStreamLatency converted from 100ns intervals to seconds then multiplied
  // by two as there are two equally-sized buffers and playback starts when the
  // second buffer is filled.
  // m_sinkLatency should match with nominal delay when all is stabilized:
  // e.g: if period is 20ms, delay is 40 ms and latency also 40 ms
  REFERENCE_TIME hnsLatency{};
  hr = m_pAudioClient->GetStreamLatency(&hnsLatency);
  if (FAILED(hr))
  {
    CLog::LogF(LOGERROR, "GetStreamLatency Failed : {}", CWIN32Util::FormatHRESULT(hr));
    return false;
  }

  m_sinkLatency = static_cast<double>(hnsLatency * 2) / 10000000; // 100ns intervals to s

  // Get the buffer size and calculate the frames for AE
  UINT32 numBufferFrames{0};
  hr = m_pAudioClient->GetBufferSize(&numBufferFrames);
  if (FAILED(hr))
  {
    CLog::LogF(LOGERROR, "GetBufferSize Failed : {}", CWIN32Util::FormatHRESULT(hr));
    return false;
  }

  format.m_frames = numBufferFrames;

  CLog::LogF(LOGINFO, "WASAPI Exclusive Mode Sink Initialized using: {}, {}, {}",
             CAEUtil::DataFormatToStr(format.m_dataFormat), wfxex.Format.nSamplesPerSec,
             wfxex.Format.nChannels);

  CLog::LogF(LOGDEBUG, "WASAPI Exclusive Mode Sink Initialized with the following parameters:");
  CLog::Log(LOGDEBUG, "  Audio Device    : {}", m_pDevice->deviceId);
  CLog::Log(LOGDEBUG, "  Sample Rate     : {}", wfxex.Format.nSamplesPerSec);
  CLog::Log(LOGDEBUG, "  Sample Format   : {}", CAEUtil::DataFormatToStr(format.m_dataFormat));
  CLog::Log(LOGDEBUG, "  Bits Per Sample : {}", wfxex.Format.wBitsPerSample);
  CLog::Log(LOGDEBUG, "  Valid Bits/Samp : {}", wfxex.Samples.wValidBitsPerSample);
  CLog::Log(LOGDEBUG, "  Channel Count   : {}", wfxex.Format.nChannels);
  CLog::Log(LOGDEBUG, "  Block Align     : {}", wfxex.Format.nBlockAlign);
  CLog::Log(LOGDEBUG, "  Avg. Bytes Sec  : {}", wfxex.Format.nAvgBytesPerSec);
  CLog::Log(LOGDEBUG, "  Samples/Block   : {}", wfxex.Samples.wSamplesPerBlock);
  CLog::Log(LOGDEBUG, "  Format cBSize   : {}", wfxex.Format.cbSize);
  CLog::Log(LOGDEBUG, "  Channel Layout  : {}", ((std::string)format.m_channelLayout));