Exemplo n.º 1
0
bool CWin32WASAPI::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, bool bAudioPassthrough)
{
    CLog::Log(LOGDEBUG, __FUNCTION__": endpoint device %s", device.c_str());

    //First check if the version of Windows we are running on even supports WASAPI.
    if (!g_sysinfo.IsVistaOrHigher())
    {
        CLog::Log(LOGERROR, __FUNCTION__": WASAPI output requires Vista or higher.");
        return false;
    }

    //Only one exclusive stream may be initialized at one time.
    if(m_bIsAllocated)
    {
        CLog::Log(LOGERROR, __FUNCTION__": Cannot create more then one WASAPI stream at one time.");
        return false;
    }

    int layoutChannels = 0;

    if(!bAudioPassthrough)
    {
        //If no channel map is specified, use the default.
        if(!channelMap)
            channelMap = (PCMChannels *)wasapi_default_channel_layout[iChannels - 1];

        PCMChannels *outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8);

        for(PCMChannels *channel = outLayout; *channel != PCM_INVALID; channel++)
            ++layoutChannels;

        //Expand monural to stereo as most devices don't seem to like 1 channel PCM streams.
        //Stereo sources should be sent explicitly as two channels so that the external hardware
        //can apply ProLogic/5CH Stereo/etc processing on it.
        if(iChannels <= 2)
        {
            BuildChannelMapping(2, (PCMChannels *)wasapi_default_channel_layout[1]);

            layoutChannels = 2;
            m_remap.SetOutputFormat(2, m_SpeakerOrder, false);
        }
        else //Do the standard remapping.
        {
            BuildChannelMapping(layoutChannels, outLayout);
            m_remap.SetOutputFormat(layoutChannels, m_SpeakerOrder, false);
        }
    }

    m_bPlaying = false;
    m_bPause = false;
    m_bMuting = false;
    m_uiChannels = iChannels;
    m_uiBitsPerSample = uiBitsPerSample;
    m_bPassthrough = bAudioPassthrough;

    m_nCurrentVolume = g_settings.m_nVolumeLevel;
    m_pcmAmplifier.SetVolume(m_nCurrentVolume);

    WAVEFORMATEXTENSIBLE wfxex = {0};

    //fill waveformatex
    ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
    wfxex.Format.cbSize          =  sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
    wfxex.Format.nChannels       = layoutChannels;
    wfxex.Format.nSamplesPerSec  = uiSamplesPerSec;
    if (bAudioPassthrough == true)
    {
        wfxex.dwChannelMask          = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
        wfxex.Format.wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF;
        wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
        wfxex.Format.wBitsPerSample  = 16;
        wfxex.Format.nChannels       = 2;
    }
    else
    {
        wfxex.dwChannelMask          = m_uiSpeakerMask;
        wfxex.Format.wFormatTag      = WAVE_FORMAT_EXTENSIBLE;
        wfxex.SubFormat              = KSDATAFORMAT_SUBTYPE_PCM;
        wfxex.Format.wBitsPerSample  = uiBitsPerSample;
    }

    wfxex.Samples.wValidBitsPerSample = uiBitsPerSample == 32 ? 24 : uiBitsPerSample;
    wfxex.Format.nBlockAlign       = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
    wfxex.Format.nAvgBytesPerSec   = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;

    m_uiAvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;

    m_uiBytesPerFrame = wfxex.Format.nBlockAlign;
    m_uiBytesPerSrcFrame = bAudioPassthrough ? m_uiBytesPerFrame : iChannels * wfxex.Format.wBitsPerSample >> 3;

    IMMDeviceEnumerator* pEnumerator = NULL;
    IMMDeviceCollection* pEnumDevices = NULL;

    //Shut down Directsound.
    g_audioContext.SetActiveDevice(CAudioContext::NONE);

    HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
    EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %i", hr)

    //Get our device.
    //First try to find the named device.
    UINT uiCount = 0;

    hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices);
    EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.")

    hr = pEnumDevices->GetCount(&uiCount);
    EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.")

    for(UINT i = 0; i < uiCount; i++)
    {
        IPropertyStore *pProperty = NULL;
        PROPVARIANT varName;

        hr = pEnumDevices->Item(i, &m_pDevice);
        EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint failed.")

        hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty);
        EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.")

        hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName);
        if(FAILED(hr))
        {
            CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed.");
            SAFE_RELEASE(pProperty);
            goto failed;
        }

        CStdStringW strRawDevName(varName.pwszVal);
        CStdString strDevName;
        g_charsetConverter.wToUTF8(strRawDevName, strDevName);

        if(device == strDevName)
            i = uiCount;
        else
            SAFE_RELEASE(m_pDevice);

        PropVariantClear(&varName);
        SAFE_RELEASE(pProperty);
    }

    SAFE_RELEASE(pEnumDevices);

    if(!m_pDevice)
    {
        CLog::Log(LOGDEBUG, __FUNCTION__": Could not locate the device named \"%s\" in the list of WASAPI endpoint devices.  Trying the default device...", device.c_str());
        hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_pDevice);
        EXIT_ON_FAILURE(hr, __FUNCTION__": Could not retrieve the default WASAPI audio endpoint.")
    }
Exemplo n.º 2
0
bool CWin32DirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels* channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded bAudioPassthrough)
{
  m_uiDataChannels = iChannels;

  if(!bAudioPassthrough)
  {
    //If no channel map is specified, use the default.
    if(!channelMap)
      channelMap = (PCMChannels *)dsound_default_channel_layout[iChannels - 1];

    PCMChannels *outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec);

    for(iChannels = 0; outLayout[iChannels] != PCM_INVALID;) ++iChannels;

    BuildChannelMapping(iChannels, outLayout);
    m_remap.SetOutputFormat(iChannels, m_SpeakerOrder, false);
  }

  bool bAudioOnAllSpeakers(false);
  g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic);
  if(bAudioPassthrough)
    g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE_DIGITAL);
  else
    g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE);
  m_pDSound=g_audioContext.GetDirectSoundDevice();

  m_bPause = false;
  m_bIsAllocated = false;
  m_pBuffer = NULL;
  m_uiChannels = iChannels;
  m_uiSamplesPerSec = uiSamplesPerSec;
  m_uiBitsPerSample = uiBitsPerSample;
  m_Passthrough = bAudioPassthrough;

  m_nCurrentVolume = g_settings.m_nVolumeLevel;
  m_drc = 0;

  WAVEFORMATEXTENSIBLE wfxex = {0};

  //fill waveformatex
  ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE));
  wfxex.Format.cbSize          =  sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
  wfxex.Format.nChannels       = iChannels;
  wfxex.Format.nSamplesPerSec  = uiSamplesPerSec;
  if (bAudioPassthrough == true)
  {
    wfxex.dwChannelMask          = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
    wfxex.Format.wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF;
    wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
    wfxex.Format.wBitsPerSample  = 16;
    wfxex.Format.nChannels       = 2;
  }
  else
  {
    wfxex.dwChannelMask          = m_uiSpeakerMask;

    if (iChannels > 2)
      wfxex.Format.wFormatTag    = WAVE_FORMAT_EXTENSIBLE;
    else
      wfxex.Format.wFormatTag    = WAVE_FORMAT_PCM;

    wfxex.SubFormat              = _KSDATAFORMAT_SUBTYPE_PCM;
    wfxex.Format.wBitsPerSample  = uiBitsPerSample;
  }

  wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample;
  wfxex.Format.nBlockAlign       = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3);
  wfxex.Format.nAvgBytesPerSec   = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign;

  m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec;

  m_uiBytesPerFrame     = wfxex.Format.nBlockAlign;
  m_uiDataBytesPerFrame = (wfxex.Format.nBlockAlign / iChannels) * m_uiDataChannels;

  // unsure if these are the right values
  m_dwChunkSize = wfxex.Format.nBlockAlign * 3096;
  m_dwDataChunkSize = (m_dwChunkSize / iChannels) * m_uiDataChannels;
  m_dwBufferLen = m_dwChunkSize * 16;
  m_PreCacheSize = m_dwBufferLen - 2*m_dwChunkSize;

  CLog::Log(LOGDEBUG, __FUNCTION__": Packet Size = %d. Avg Bytes Per Second = %d.", m_dwChunkSize, m_AvgBytesPerSec);

  // fill in the secondary sound buffer descriptor
  DSBUFFERDESC dsbdesc;
  memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
  dsbdesc.dwSize = sizeof(DSBUFFERDESC);
  dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
                  | DSBCAPS_GLOBALFOCUS         /** Allows background playing */
                  | DSBCAPS_CTRLVOLUME;         /** volume control enabled */

  if (!g_sysinfo.IsVistaOrHigher())
    dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;     /** Needed for 5.1 on emu101k, always fails on Vista, by design  */

  dsbdesc.dwBufferBytes = m_dwBufferLen;
  dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex;

  // now create the stream buffer
  HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
  if (res != DS_OK)
  {
    if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE)
    {
      SAFE_RELEASE(m_pBuffer);
      CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without LOCHARDWARE.", dserr2str(res));
      // Try without DSBCAPS_LOCHARDWARE
      dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
      res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
    }
    if (res != DS_OK && dsbdesc.dwFlags & DSBCAPS_CTRLVOLUME)
    {
      SAFE_RELEASE(m_pBuffer);
      CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without CTRLVOLUME.", dserr2str(res));
      // Try without DSBCAPS_CTRLVOLUME
      dsbdesc.dwFlags &= ~DSBCAPS_CTRLVOLUME;
      res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL);
    }
    if (res != DS_OK)
    {
      SAFE_RELEASE(m_pBuffer);
      CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res));
      return false;
    }
  }
  CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created");

  m_pBuffer->Stop();

  if (DSERR_CONTROLUNAVAIL == m_pBuffer->SetVolume(g_settings.m_nVolumeLevel))
    CLog::Log(LOGINFO, __FUNCTION__": Volume control is unavailable in the current configuration");

  m_bIsAllocated = true;
  m_BufferOffset = 0;
  m_CacheLen = 0;
  m_LastCacheCheck = XbmcThreads::SystemClockMillis();

  return m_bIsAllocated;
}