unsigned int CAEEncoderFFmpeg::BuildChannelLayout(const int64_t ffmap, CAEChannelInfo& layout) { /* build the channel layout and count the channels */ layout.Reset(); if (ffmap & AV_CH_FRONT_LEFT ) layout += AE_CH_FL ; if (ffmap & AV_CH_FRONT_RIGHT ) layout += AE_CH_FR ; if (ffmap & AV_CH_FRONT_CENTER ) layout += AE_CH_FC ; if (ffmap & AV_CH_LOW_FREQUENCY ) layout += AE_CH_LFE ; if (ffmap & AV_CH_BACK_LEFT ) layout += AE_CH_BL ; if (ffmap & AV_CH_BACK_RIGHT ) layout += AE_CH_BR ; if (ffmap & AV_CH_FRONT_LEFT_OF_CENTER ) layout += AE_CH_FLOC; if (ffmap & AV_CH_FRONT_RIGHT_OF_CENTER) layout += AE_CH_FROC; if (ffmap & AV_CH_BACK_CENTER ) layout += AE_CH_BC ; if (ffmap & AV_CH_SIDE_LEFT ) layout += AE_CH_SL ; if (ffmap & AV_CH_SIDE_RIGHT ) layout += AE_CH_SR ; if (ffmap & AV_CH_TOP_CENTER ) layout += AE_CH_TC ; if (ffmap & AV_CH_TOP_FRONT_LEFT ) layout += AE_CH_TFL ; if (ffmap & AV_CH_TOP_FRONT_CENTER ) layout += AE_CH_TFC ; if (ffmap & AV_CH_TOP_FRONT_RIGHT ) layout += AE_CH_TFR ; if (ffmap & AV_CH_TOP_BACK_LEFT ) layout += AE_CH_TBL ; if (ffmap & AV_CH_TOP_BACK_CENTER ) layout += AE_CH_TBC ; if (ffmap & AV_CH_TOP_BACK_RIGHT ) layout += AE_CH_TBR ; return layout.Count(); }
CAEChannelInfo CAEUtil::GetAEChannelLayout(uint64_t layout) { CAEChannelInfo channelLayout; channelLayout.Reset(); if (layout & AV_CH_FRONT_LEFT) channelLayout += AE_CH_FL; if (layout & AV_CH_FRONT_RIGHT) channelLayout += AE_CH_FR; if (layout & AV_CH_FRONT_CENTER) channelLayout += AE_CH_FC; if (layout & AV_CH_LOW_FREQUENCY) channelLayout += AE_CH_LFE; if (layout & AV_CH_BACK_LEFT) channelLayout += AE_CH_BL; if (layout & AV_CH_BACK_RIGHT) channelLayout += AE_CH_BR; if (layout & AV_CH_FRONT_LEFT_OF_CENTER) channelLayout += AE_CH_FLOC; if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) channelLayout += AE_CH_FROC; if (layout & AV_CH_BACK_CENTER) channelLayout += AE_CH_BC; if (layout & AV_CH_SIDE_LEFT) channelLayout += AE_CH_SL; if (layout & AV_CH_SIDE_RIGHT) channelLayout += AE_CH_SR; if (layout & AV_CH_TOP_CENTER) channelLayout += AE_CH_TC; if (layout & AV_CH_TOP_FRONT_LEFT) channelLayout += AE_CH_TFL; if (layout & AV_CH_TOP_FRONT_CENTER) channelLayout += AE_CH_TFC; if (layout & AV_CH_TOP_FRONT_RIGHT) channelLayout += AE_CH_TFR; if (layout & AV_CH_TOP_BACK_LEFT) channelLayout += AE_CH_BL; if (layout & AV_CH_TOP_BACK_CENTER) channelLayout += AE_CH_BC; if (layout & AV_CH_TOP_BACK_RIGHT) channelLayout += AE_CH_BR; return channelLayout; }
static CAEChannelInfo PAChannelToAEChannelMap(pa_channel_map channels) { CAEChannelInfo info; AEChannel ch; info.Reset(); for (unsigned int i=0; i<channels.channels; i++) { ch = PAChannelToAEChannel(channels.map[i]); if(ch != AE_CH_NULL) info += ch; } return info; }
void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; IMMDevice* pDefaultDevice = NULL; CAEDeviceInfo deviceInfo; CAEChannelInfo deviceChannels; LPWSTR pwszID = NULL; std::wstring wstrDDID; WAVEFORMATEXTENSIBLE wfxex = {0}; HRESULT hr; 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: %li", hr) UINT uiCount = 0; // get the default audio endpoint if(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDefaultDevice) == S_OK) { if(pDefaultDevice->GetId(&pwszID) == S_OK) { wstrDDID = pwszID; CoTaskMemFree(pwszID); } SAFE_RELEASE(pDefaultDevice); } // enumerate over all audio endpoints 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++) { IMMDevice *pDevice = NULL; IPropertyStore *pProperty = NULL; PROPVARIANT varName; PropVariantInit(&varName); deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); hr = pEnumDevices->Item(i, &pDevice); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint failed."); goto failed; } hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint properties failed."); SAFE_RELEASE(pDevice); goto failed; } hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strFriendlyName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); if(FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint GUID failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strDevName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint form factor failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType; AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint speaker layout failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } unsigned int uiChannelMask = std::max(varName.uintVal, (unsigned int) KSAUDIO_SPEAKER_STEREO); deviceChannels.Reset(); for (unsigned int c = 0; c < WASAPI_SPEAKER_COUNT; c++) { if (uiChannelMask & WASAPIChannelOrder[c]) deviceChannels += AEChannelNames[c]; } PropVariantClear(&varName); IAudioClient *pClient; hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient); if (SUCCEEDED(hr)) { /* Test format DTS-HD */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.Format.nSamplesPerSec = 192000; wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD; wfxex.Format.wBitsPerSample = 16; wfxex.Samples.wValidBitsPerSample = 16; wfxex.Format.nChannels = 8; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_DTSHD)); /* Test format Dolby TrueHD */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_TRUEHD)); /* Test format Dolby EAC3 */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS; wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_EAC3)); /* Test format DTS */ wfxex.Format.nSamplesPerSec = 48000; wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_DTS)); /* Test format Dolby AC3 */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); /* Test format AAC */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_AAC; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AAC)); /* Test format for PCM format iteration */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; for (int p = AE_FMT_FLOAT; p > AE_FMT_INVALID; p--) { if (p < AE_FMT_FLOAT) wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.wBitsPerSample = CAEUtil::DataFormatToBits((AEDataFormat) p); wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; if (p <= AE_FMT_S24NE4 && p >= AE_FMT_S24BE4) { wfxex.Samples.wValidBitsPerSample = 24; } else { wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample; } hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back((AEDataFormat) p); } /* Test format for sample rate iteration */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.wBitsPerSample = 16; wfxex.Samples.wValidBitsPerSample = 16; wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; for (int j = 0; j < WASAPISampleRateCount; j++) { wfxex.Format.nSamplesPerSec = WASAPISampleRates[j]; wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]); } /* Test format for channels iteration */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.nSamplesPerSec = 48000; wfxex.Format.wBitsPerSample = 16; wfxex.Samples.wValidBitsPerSample = 16; wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; bool hasLpcm = false; // Try with KSAUDIO_SPEAKER_DIRECTOUT for (unsigned int k = WASAPI_SPEAKER_COUNT; k > 0; k--) { wfxex.dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT; wfxex.Format.nChannels = k; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) { if (k > 3) // Add only multichannel LPCM { deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM); hasLpcm = true; } break; } } /* Try with reported channel mask */ for (unsigned int k = WASAPI_SPEAKER_COUNT; k > 0; k--) { wfxex.dwChannelMask = uiChannelMask; wfxex.Format.nChannels = k; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) { if ( !hasLpcm && k > 3) // Add only multichannel LPCM { deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM); hasLpcm = true; } break; } } /* Try with specific speakers configurations */ for (unsigned int i = 0; i < ARRAYSIZE(layoutsList); i++) { unsigned int nmbOfCh; wfxex.dwChannelMask = ChLayoutToChMask(layoutsList[i], &nmbOfCh); wfxex.Format.nChannels = nmbOfCh; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) { if ( deviceChannels.Count() < nmbOfCh) deviceChannels = layoutsList[i]; if ( !hasLpcm && nmbOfCh > 3) // Add only multichannel LPCM { deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM); hasLpcm = true; } } } pClient->Release(); } else { CLog::Log(LOGDEBUG, __FUNCTION__": Failed to activate device for passthrough capability testing."); } deviceInfo.m_deviceName = strDevName; deviceInfo.m_displayName = strWinDevType.append(strFriendlyName); deviceInfo.m_displayNameExtra = std::string("WASAPI: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; deviceInfo.m_channels = deviceChannels; /* Store the device info */ deviceInfoList.push_back(deviceInfo); if(pDevice->GetId(&pwszID) == S_OK) { if(wstrDDID.compare(pwszID) == 0) { deviceInfo.m_deviceName = std::string("default"); deviceInfo.m_displayName = std::string("default"); deviceInfo.m_displayNameExtra = std::string(""); deviceInfoList.push_back(deviceInfo); } CoTaskMemFree(pwszID); } SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); }
void CActiveAEStream::InitRemapper() { // check if input format follows ffmpeg channel mask bool needRemap = false; unsigned int avLast, avCur = 0; for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++) { avLast = avCur; avCur = CAEUtil::GetAVChannel(m_format.m_channelLayout[i]); if(avCur < avLast) { needRemap = true; break; } } if(needRemap) { CLog::Log(LOGDEBUG, "CActiveAEStream::%s - initialize remapper", __FUNCTION__); m_remapper = CAEResampleFactory::Create(); uint64_t avLayout = CAEUtil::GetAVChannelLayout(m_format.m_channelLayout); // build layout according to ffmpeg channel order // we need this for reference CAEChannelInfo ffmpegLayout; ffmpegLayout.Reset(); int idx = 0; for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++) { for(unsigned int j=0; j<m_format.m_channelLayout.Count(); j++) { idx = CAEUtil::GetAVChannelIndex(m_format.m_channelLayout[j], avLayout); if (idx == (int)i) { ffmpegLayout += m_format.m_channelLayout[j]; break; } } } // build remap layout we can pass to resampler as destination layout CAEChannelInfo remapLayout; remapLayout.Reset(); for(unsigned int i=0; i<m_format.m_channelLayout.Count(); i++) { for(unsigned int j=0; j<m_format.m_channelLayout.Count(); j++) { idx = CAEUtil::GetAVChannelIndex(m_format.m_channelLayout[j], avLayout); if (idx == (int)i) { remapLayout += ffmpegLayout[j]; break; } } } // initialize resampler for only doing remapping m_remapper->Init(avLayout, m_format.m_channelLayout.Count(), m_format.m_sampleRate, CAEUtil::GetAVSampleFormat(m_format.m_dataFormat), CAEUtil::DataFormatToUsedBits(m_format.m_dataFormat), CAEUtil::DataFormatToDitherBits(m_format.m_dataFormat), avLayout, m_format.m_channelLayout.Count(), m_format.m_sampleRate, CAEUtil::GetAVSampleFormat(m_format.m_dataFormat), CAEUtil::DataFormatToUsedBits(m_format.m_dataFormat), CAEUtil::DataFormatToDitherBits(m_format.m_dataFormat), false, false, &remapLayout, AE_QUALITY_LOW, // not used for remapping false); // extra sound packet, we can't resample to the same buffer m_remapBuffer = new CSoundPacket(m_inputBuffers->m_allSamples[0]->pkt->config, m_inputBuffers->m_allSamples[0]->pkt->max_nb_samples); } }
//Note: in multichannel mode CA will either pull 2 channels of data (stereo) or 6/8 channels of data //(every speaker setup with more then 2 speakers). The difference between the number of real speakers //and 6/8 channels needs to be padded with unknown channels so that the sample size fits 6/8 channels // //device [in] - the device whose channel layout should be used //channelMap [in/out] - if filled it will it indicates that we are called from initialize and we log the requested map, out returns the channelMap for device //channelsPerFrame [in] - the number of channels this device is configured to (e.x. 2 or 6/8) void AEDeviceEnumerationOSX::GetAEChannelMap(CAEChannelInfo &channelMap, unsigned int channelsPerFrame) const { CCoreAudioChannelLayout calayout; bool logMapping = channelMap.Count() > 0; // only log if the engine requests a layout during init bool mapAvailable = false; unsigned int numberChannelsInDeviceLayout = CA_MAX_CHANNELS; // default number of channels from CAChannelMap AudioChannelLayout *layout = NULL; // try to fetch either the multichannel or the stereo channel layout from the device if (channelsPerFrame == 2 || channelMap.Count() == 2) mapAvailable = m_caDevice.GetPreferredChannelLayoutForStereo(calayout); else mapAvailable = m_caDevice.GetPreferredChannelLayout(calayout); // if a map was fetched - check if it is usable if (mapAvailable) { layout = calayout; if (layout == NULL || layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) mapAvailable = false;// wrong map format else numberChannelsInDeviceLayout = layout->mNumberChannelDescriptions; } // start the mapping action // the number of channels to be added to the outgoing channelmap // this is CA_MAX_CHANNELS at max and might be lower for some output devices (channelsPerFrame) unsigned int numChannelsToMap = std::min((unsigned int)CA_MAX_CHANNELS, (unsigned int)channelsPerFrame); // if there was a map fetched we force the number of // channels to map to channelsPerFrame (this allows mapping // of more then CA_MAX_CHANNELS if needed) if (mapAvailable) numChannelsToMap = channelsPerFrame; std::string layoutStr; if (logMapping) { CLog::Log(LOGDEBUG, "%s Engine requests layout %s", __FUNCTION__, ((std::string)channelMap).c_str()); if (mapAvailable) CLog::Log(LOGDEBUG, "%s trying to map to %s layout: %s", __FUNCTION__, channelsPerFrame == 2 ? "stereo" : "multichannel", calayout.ChannelLayoutToString(*layout, layoutStr)); else CLog::Log(LOGDEBUG, "%s no map available - using static multichannel map layout", __FUNCTION__); } channelMap.Reset();// start with an empty map for (unsigned int channel = 0; channel < numChannelsToMap; channel++) { // we only try to map channels which are defined in the device layout enum AEChannel currentChannel; if (channel < numberChannelsInDeviceLayout) { // get the channel from the fetched map if (mapAvailable) currentChannel = caChannelToAEChannel(layout->mChannelDescriptions[channel].mChannelLabel); else// get the channel from the default map currentChannel = CAChannelMap[channel]; } else// fill with unknown channels currentChannel = caChannelToAEChannel(kAudioChannelLabel_Unknown); if(!channelMap.HasChannel(currentChannel))// only add if not already added channelMap += currentChannel; } if (logMapping) CLog::Log(LOGDEBUG, "%s mapped channels to layout %s", __FUNCTION__, ((std::string)channelMap).c_str()); }