bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device) { if (m_initialized) return false; LPGUID deviceGUID = NULL; RPC_CSTR wszUuid = NULL; HRESULT hr; std::list<DSDevice> DSDeviceList; std::string deviceFriendlyName; DirectSoundEnumerate(DSEnumCallback, &DSDeviceList); for (std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); itt++) { if ((*itt).lpGuid) { hr = (UuidToString((*itt).lpGuid, &wszUuid)); std::string sztmp = (char*)wszUuid; std::string szGUID = "{" + std::string(sztmp.begin(), sztmp.end()) + "}"; if (strcasecmp(szGUID.c_str(), device.c_str()) == 0) { deviceGUID = (*itt).lpGuid; deviceFriendlyName = (*itt).name.c_str(); break; } } if (hr == RPC_S_OK) RpcStringFree(&wszUuid); } hr = DirectSoundCreate(deviceGUID, &m_pDSound, NULL); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device."); CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr)); return false; } HWND tmp_hWnd; /* Dodge the null handle on first init by using desktop handle */ if (g_hWnd == NULL) tmp_hWnd = GetDesktopWindow(); else tmp_hWnd = g_hWnd; CLog::Log(LOGDEBUG, __FUNCTION__": Using Window handle: %d", tmp_hWnd); hr = m_pDSound->SetCooperativeLevel(tmp_hWnd, DSSCL_PRIORITY); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Failed to create the DirectSound device cooperative level."); CLog::Log(LOGERROR, __FUNCTION__": DSErr: %s", dserr2str(hr)); m_pDSound->Release(); return false; } WAVEFORMATEXTENSIBLE wfxex = {0}; //fill waveformatex ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE)); wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.Format.nChannels = format.m_channelLayout.Count(); wfxex.Format.nSamplesPerSec = format.m_sampleRate; if (AE_IS_RAW(format.m_dataFormat)) { 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 = SpeakerMaskFromAEChannels(format.m_channelLayout); wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; wfxex.Format.wBitsPerSample = 32; } 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; unsigned int uiFrameCount = (int)(format.m_sampleRate * 0.01); //default to 10ms chunks m_dwFrameSize = wfxex.Format.nBlockAlign; m_dwChunkSize = m_dwFrameSize * uiFrameCount; m_dwBufferLen = m_dwChunkSize * 12; //120ms total buffer // 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 */ if (!g_sysinfo.IsVistaOrHigher()) dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; /** Needed for 5.1 on emu101k, fails by design on Vista */ 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) { 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(); AEChannelsFromSpeakerMask(wfxex.dwChannelMask); format.m_channelLayout = m_channelLayout; format.m_frames = uiFrameCount; format.m_frameSamples = format.m_frames * format.m_channelLayout.Count(); format.m_frameSize = (AE_IS_RAW(format.m_dataFormat) ? wfxex.Format.wBitsPerSample >> 3 : sizeof(float)) * format.m_channelLayout.Count(); format.m_dataFormat = AE_IS_RAW(format.m_dataFormat) ? AE_FMT_S16NE : AE_FMT_FLOAT; m_format = format; m_device = device; m_BufferOffset = 0; m_CacheLen = 0; m_LastCacheCheck = XbmcThreads::SystemClockMillis(); m_initialized = true; CLog::Log(LOGDEBUG, __FUNCTION__": Initializing DirectSound with the following parameters:"); CLog::Log(LOGDEBUG, " Audio Device : %s", ((std::string)deviceFriendlyName).c_str()); CLog::Log(LOGDEBUG, " Sample Rate : %d", wfxex.Format.nSamplesPerSec); CLog::Log(LOGDEBUG, " Sample Format : %s", CAEUtil::DataFormatToStr(format.m_dataFormat)); CLog::Log(LOGDEBUG, " Bits Per Sample : %d", wfxex.Format.wBitsPerSample); CLog::Log(LOGDEBUG, " Valid Bits/Samp : %d", wfxex.Samples.wValidBitsPerSample); CLog::Log(LOGDEBUG, " Channel Count : %d", wfxex.Format.nChannels); CLog::Log(LOGDEBUG, " Block Align : %d", wfxex.Format.nBlockAlign); CLog::Log(LOGDEBUG, " Avg. Bytes Sec : %d", wfxex.Format.nAvgBytesPerSec); CLog::Log(LOGDEBUG, " Samples/Block : %d", wfxex.Samples.wSamplesPerBlock); CLog::Log(LOGDEBUG, " Format cBSize : %d", wfxex.Format.cbSize); CLog::Log(LOGDEBUG, " Channel Layout : %s", ((std::string)format.m_channelLayout).c_str()); CLog::Log(LOGDEBUG, " Channel Mask : %d", wfxex.dwChannelMask); CLog::Log(LOGDEBUG, " Frames : %d", format.m_frames); CLog::Log(LOGDEBUG, " Frame Samples : %d", format.m_frameSamples); CLog::Log(LOGDEBUG, " Frame Size : %d", format.m_frameSize); return true; }
void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config) { snd_pcm_t *pcmhandle = NULL; if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config, false)) return; snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); memset(pcminfo, 0, snd_pcm_info_sizeof()); int err = snd_pcm_info(pcmhandle, pcminfo); if (err < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); } int cardNr = snd_pcm_info_get_card(pcminfo); CAEDeviceInfo info; info.m_deviceName = device; info.m_deviceType = AEDeviceTypeFromName(device); if (cardNr >= 0) { /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */ char *cardName; if (snd_card_get_name(cardNr, &cardName) == 0) info.m_displayName = cardName; if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 && info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI") { /* We already know this is HDMI, strip it */ info.m_displayName.erase(info.m_displayName.size()-5); } /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */ std::string pcminfoName = snd_pcm_info_get_name(pcminfo); /* * Filter "USB Audio", in those cases snd_card_get_name() is more * meaningful already */ if (pcminfoName != "USB Audio") info.m_displayNameExtra = pcminfoName; if (info.m_deviceType == AE_DEVTYPE_HDMI) { /* replace, this was likely "HDMI 0" */ info.m_displayNameExtra = "HDMI"; int dev = snd_pcm_info_get_device(pcminfo); if (dev >= 0) { /* lets see if we can get ELD info */ snd_ctl_t *ctlhandle; std::stringstream sstr; sstr << "hw:" << cardNr; std::string strHwName = sstr.str(); if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0) { snd_hctl_t *hctl; if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0) { snd_hctl_load(hctl); bool badHDMI = false; if (!GetELD(hctl, dev, info, badHDMI)) CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)", device.c_str()); /* snd_hctl_close also closes ctlhandle */ snd_hctl_close(hctl); if (badHDMI) { /* * Warn about disconnected devices, but keep them enabled * Detection can go wrong on Intel, Nvidia and on all * AMD (fglrx) hardware, so it is not safe to close those * handles */ CLog::Log(LOGDEBUG, "CAESinkALSA - HDMI device \"%s\" may be unconnected (no ELD data)", device.c_str()); } } else { snd_ctl_close(ctlhandle); } } } } else if (info.m_deviceType == AE_DEVTYPE_IEC958) { /* append instead of replace, pcminfoName is useful for S/PDIF */ if (!info.m_displayNameExtra.empty()) info.m_displayNameExtra += ' '; info.m_displayNameExtra += "S/PDIF"; } else if (info.m_displayNameExtra.empty()) { /* for USB audio, it gets a bit confusing as there is * - "SB Live! 24-bit External" * - "SB Live! 24-bit External, S/PDIF" * so add "Analog" qualifier to the first one */ info.m_displayNameExtra = "Analog"; } /* "default" is a device that will be used for all inputs, while * "@" will be mangled to front/default/surroundXX as necessary */ if (device == "@" || device == "default") { /* Make it "Default (whatever)" */ info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")"); info.m_displayNameExtra = ""; } } else { /* virtual devices: "default", "pulse", ... */ /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device", * so we only use it for virtual devices that have no better display name */ info.m_displayName = description; } snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); memset(hwparams, 0, snd_pcm_hw_params_sizeof()); /* ensure we can get a playback configuration for the device */ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); return; } /* detect the available sample rates */ for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate) if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0) info.m_sampleRates.push_back(*rate); /* detect the channels available */ int channels = 0; for (int i = ALSA_MAX_CHANNELS; i >= 1; --i) { /* Reopen the device if needed on the special "surroundXX" cases */ if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4)) OpenPCMDevice(device, "", i, &pcmhandle, config, false); if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0) { channels = i; break; } } if (device == "default" && channels == 2) { /* This looks like the ALSA standard default stereo dmix device, we * probably want to use "@" instead to get surroundXX. */ snd_pcm_close(pcmhandle); EnumerateDevice(list, "@", description, config); return; } CAEChannelInfo alsaChannels; for (int i = 0; i < channels; ++i) { if (!info.m_channels.HasChannel(ALSAChannelMap[i])) info.m_channels += ALSAChannelMap[i]; alsaChannels += ALSAChannelMap[i]; } /* remove the channels from m_channels that we cant use */ info.m_channels.ResolveChannels(alsaChannels); /* detect the PCM sample formats that are available */ for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) continue; snd_pcm_format_t fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN) continue; if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0) info.m_dataFormats.push_back(i); } snd_pcm_close(pcmhandle); list.push_back(info); }
bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */, bool job /* = false */) { StreamInfo *si = new StreamInfo(); // check if we advance a track of a CUE sheet // if this is the case we don't need to open a new stream std::string newURL = file.GetMusicInfoTag() ? file.GetMusicInfoTag()->GetURL() : file.GetPath(); std::string oldURL = m_FileItem->GetMusicInfoTag() ? m_FileItem->GetMusicInfoTag()->GetURL() : m_FileItem->GetPath(); if (newURL.compare(oldURL) == 0 && file.m_lStartOffset && file.m_lStartOffset == m_FileItem->m_lEndOffset && m_currentStream && m_currentStream->m_prepareTriggered) { m_continueStream = true; m_upcomingCrossfadeMS = 0; *m_FileItem = file; return true; } else { m_continueStream = false; } if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75)) { CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder"); delete si; // advance playlist if (job) m_callback.OnPlayBackStarted(); m_callback.OnQueueNextItem(); return false; } /* decode until there is data-available */ si->m_decoder.Start(); while(si->m_decoder.GetDataSize() == 0) { int status = si->m_decoder.GetStatus(); if (status == STATUS_ENDED || status == STATUS_NO_FILE || si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples"); si->m_decoder.Destroy(); delete si; // advance playlist if (job) m_callback.OnPlayBackStarted(); m_callback.OnQueueNextItem(); return false; } /* yield our time so that the main PAP thread doesnt stall */ CThread::Sleep(1); } /* init the streaminfo struct */ si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat); si->m_startOffset = file.m_lStartOffset * 1000 / 75; si->m_endOffset = file.m_lEndOffset * 1000 / 75; si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3; si->m_bytesPerFrame = si->m_bytesPerSample * si->m_channelInfo.Count(); si->m_started = false; si->m_finishing = false; si->m_framesSent = 0; si->m_seekNextAtFrame = 0; si->m_seekFrame = -1; si->m_stream = NULL; si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f; si->m_fadeOutTriggered = false; si->m_isSlaved = false; int64_t streamTotalTime = si->m_decoder.TotalTime(); if (si->m_endOffset) streamTotalTime = si->m_endOffset - si->m_startOffset; si->m_prepareNextAtFrame = 0; // cd drives don't really like it to be crossfaded or prepared if(!file.IsCDDA()) { if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS) si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f); } if (m_currentStream && (AE_IS_RAW(m_currentStream->m_dataFormat) || AE_IS_RAW(si->m_dataFormat))) { m_currentStream->m_prepareTriggered = false; m_currentStream->m_waitOnDrain = true; m_currentStream->m_prepareNextAtFrame = 0; si->m_decoder.Destroy(); delete si; return false; } UpdateCrossfadeTime(file); si->m_prepareTriggered = false; si->m_playNextAtFrame = 0; si->m_playNextTriggered = false; si->m_waitOnDrain = false; if (!PrepareStream(si)) { CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream"); si->m_decoder.Destroy(); delete si; // advance playlist if (job) m_callback.OnPlayBackStarted(); m_callback.OnQueueNextItem(); return false; } /* add the stream to the list */ CExclusiveLock lock(m_streamsLock); m_streams.push_back(si); //update the current stream to start playing the next track at the correct frame. UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS); *m_FileItem = file; return true; }
bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) { m_initDevice = device; m_initFormat = format; /* if we are raw, correct the data format */ if (AE_IS_RAW(format.m_dataFormat)) { m_channelLayout = GetChannelLayout(format); format.m_dataFormat = AE_FMT_S16NE; m_passthrough = true; } else { m_channelLayout = GetChannelLayout(format); m_passthrough = false; } #if defined(HAS_AMLPLAYER) || defined(HAS_LIBAMCODEC) if (aml_present()) { aml_set_audio_passthrough(m_passthrough); device = "default"; } #endif if (m_channelLayout.Count() == 0) { CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout"); return false; } format.m_channelLayout = m_channelLayout; AEDeviceType devType = AEDeviceTypeFromName(device); std::string AESParams; /* digital interfaces should have AESx set, though in practice most * receivers don't care */ if (m_passthrough || devType == AE_DEVTYPE_HDMI || devType == AE_DEVTYPE_IEC958) GetAESParams(format, AESParams); CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Attempting to open device \"%s\"", device.c_str()); /* get the sound config */ snd_config_t *config; snd_config_copy(&config, snd_config); snd_config_t *dmixRateConf; long dmixRate; if (snd_config_search(config, "defaults.pcm.dmix.rate", &dmixRateConf) < 0 || snd_config_get_integer(dmixRateConf, &dmixRate) < 0) dmixRate = 48000; /* assume default */ /* Prefer dmix for non-passthrough stereo when sample rate matches */ if (!OpenPCMDevice(device, AESParams, m_channelLayout.Count(), &m_pcm, config, format.m_sampleRate == (unsigned int) dmixRate && !m_passthrough)) { CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"%s\"", device.c_str()); snd_config_delete(config); return false; } /* get the actual device name that was used */ device = snd_pcm_name(m_pcm); m_device = device; CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Opened device \"%s\"", device.c_str()); /* free the sound config */ snd_config_delete(config); if (!InitializeHW(format) || !InitializeSW(format)) return false; snd_pcm_nonblock(m_pcm, 1); snd_pcm_prepare (m_pcm); m_format = format; m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate; return true; }
bool CAESinkALSA::InitializeHW(AEAudioFormat &format) { snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); memset(hw_params, 0, snd_pcm_hw_params_sizeof()); snd_pcm_hw_params_any(m_pcm, hw_params); snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); unsigned int sampleRate = format.m_sampleRate; unsigned int channelCount = format.m_channelLayout.Count(); snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL); snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount); /* ensure we opened X channels or more */ if (format.m_channelLayout.Count() > channelCount) { CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels"); } /* update the channelLayout to what we managed to open */ format.m_channelLayout.Reset(); for (unsigned int i = 0; i < channelCount; ++i) format.m_channelLayout += ALSAChannelMap[i]; snd_pcm_format_t fmt = AEFormatToALSAFormat(format.m_dataFormat); if (fmt == SND_PCM_FORMAT_UNKNOWN) { /* if we dont support the requested format, fallback to float */ format.m_dataFormat = AE_FMT_FLOAT; fmt = SND_PCM_FORMAT_FLOAT; } /* try the data format */ if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { /* if the chosen format is not supported, try each one in decending order */ CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat)); for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) continue; if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE) continue; fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { fmt = SND_PCM_FORMAT_UNKNOWN; continue; } int fmtBits = CAEUtil::DataFormatToBits(i); int bits = snd_pcm_hw_params_get_sbits(hw_params); if (bits != fmtBits) { /* if we opened in 32bit and only have 24bits, pack into 24 */ if (fmtBits == 32 && bits == 24) i = AE_FMT_S24NE4; else continue; } /* record that the format fell back to X */ format.m_dataFormat = i; CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(format.m_dataFormat)); break; } /* if we failed to find a valid output format */ if (fmt == SND_PCM_FORMAT_UNKNOWN) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format"); return false; } } unsigned int periods; snd_pcm_uframes_t periodSize, bufferSize; snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize); bufferSize = std::max(bufferSize, (snd_pcm_uframes_t)8192); periodSize = bufferSize / ALSA_PERIODS; periods = ALSA_PERIODS; CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize); /* work on a copy of the hw params */ snd_pcm_hw_params_t *hw_params_copy; snd_pcm_hw_params_alloca(&hw_params_copy); /* try to set the buffer size then the period size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { /* try to set the period size then the buffer size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { /* try to just set the buffer size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { /* try to just set the period size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Failed to set the parameters"); return false; } } } } snd_pcm_hw_params_get_period_size(hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_get_buffer_size(hw_params_copy, &bufferSize); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize); /* set the format parameters */ format.m_sampleRate = sampleRate; format.m_frames = periodSize; format.m_frameSamples = periodSize * format.m_channelLayout.Count(); format.m_frameSize = snd_pcm_frames_to_bytes(m_pcm, 1); m_bufferSize = (unsigned int)bufferSize; m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout); return true; }
bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device) { CAEChannelInfo channelLayout = GetChannelLayout(format, 2, 8); m_initDevice = device; m_initFormat = format; ALSAConfig inconfig, outconfig; inconfig.format = format.m_dataFormat; inconfig.sampleRate = format.m_sampleRate; inconfig.channels = channelLayout.Count(); /* if we are raw, correct the data format */ if (AE_IS_RAW(format.m_dataFormat)) { inconfig.format = AE_FMT_S16NE; m_passthrough = true; } else { m_passthrough = false; } #if defined(HAS_LIBAMCODEC) if (aml_present()) { aml_set_audio_passthrough(m_passthrough); device = "default"; } #endif if (inconfig.channels == 0) { CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout"); return false; } AEDeviceType devType = AEDeviceTypeFromName(device); std::string AESParams; /* digital interfaces should have AESx set, though in practice most * receivers don't care */ if (m_passthrough || devType == AE_DEVTYPE_HDMI || devType == AE_DEVTYPE_IEC958) GetAESParams(format, AESParams); CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Attempting to open device \"%s\"", device.c_str()); /* get the sound config */ snd_config_t *config; snd_config_copy(&config, snd_config); if (!OpenPCMDevice(device, AESParams, inconfig.channels, &m_pcm, config)) { CLog::Log(LOGERROR, "CAESinkALSA::Initialize - failed to initialize device \"%s\"", device.c_str()); snd_config_delete(config); return false; } /* get the actual device name that was used */ device = snd_pcm_name(m_pcm); m_device = device; CLog::Log(LOGINFO, "CAESinkALSA::Initialize - Opened device \"%s\"", device.c_str()); /* free the sound config */ snd_config_delete(config); if (!InitializeHW(inconfig, outconfig) || !InitializeSW(outconfig)) return false; // we want it blocking snd_pcm_nonblock(m_pcm, 0); snd_pcm_prepare (m_pcm); if (m_passthrough && inconfig.channels != outconfig.channels) { CLog::Log(LOGINFO, "CAESinkALSA::Initialize - could not open required number of channels"); return false; } // adjust format to the configuration we got format.m_channelLayout = GetChannelLayout(format, outconfig.channels, outconfig.channels); format.m_sampleRate = outconfig.sampleRate; format.m_frames = outconfig.periodSize; format.m_frameSize = outconfig.frameSize; format.m_frameSamples = outconfig.periodSize * outconfig.channels; format.m_dataFormat = outconfig.format; m_format = format; m_formatSampleRateMul = 1.0 / (double)m_format.m_sampleRate; return true; }
bool CAESinkALSA::InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig) { snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); memset(hw_params, 0, snd_pcm_hw_params_sizeof()); snd_pcm_hw_params_any(m_pcm, hw_params); snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); unsigned int sampleRate = inconfig.sampleRate; unsigned int channelCount = inconfig.channels; snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL); snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount); /* ensure we opened X channels or more */ if (inconfig.channels > channelCount) { CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels"); } /* update outconfig */ outconfig.channels = channelCount; snd_pcm_format_t fmt = AEFormatToALSAFormat(inconfig.format); outconfig.format = inconfig.format; if (fmt == SND_PCM_FORMAT_UNKNOWN) { /* if we dont support the requested format, fallback to float */ fmt = SND_PCM_FORMAT_FLOAT; outconfig.format = AE_FMT_FLOAT; } /* try the data format */ if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { /* if the chosen format is not supported, try each one in decending order */ CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(outconfig.format)); for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) continue; if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE) continue; fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { fmt = SND_PCM_FORMAT_UNKNOWN; continue; } int fmtBits = CAEUtil::DataFormatToBits(i); int bits = snd_pcm_hw_params_get_sbits(hw_params); if (bits != fmtBits) { /* if we opened in 32bit and only have 24bits, pack into 24 */ if (fmtBits == 32 && bits == 24) i = AE_FMT_S24NE4; else continue; } /* record that the format fell back to X */ outconfig.format = i; CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(outconfig.format)); break; } /* if we failed to find a valid output format */ if (fmt == SND_PCM_FORMAT_UNKNOWN) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format"); return false; } } snd_pcm_uframes_t periodSize, bufferSize; snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize); snd_pcm_hw_params_get_period_size_max(hw_params, &periodSize, NULL); /* We want to make sure, that we have max 200 ms Buffer with a periodSize of approx 50 ms. Choosing a higher bufferSize will cause problems with menu sounds. Buffer will be increased after those are fixed. */ periodSize = std::min(periodSize, (snd_pcm_uframes_t) sampleRate / 20); bufferSize = std::min(bufferSize, (snd_pcm_uframes_t) sampleRate / 5); /* According to upstream we should set buffer size first - so make sure it is always at least 4x period size to not get underruns (some systems seem to have issues with only 2 periods) */ periodSize = std::min(periodSize, bufferSize / 4); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, bufferSize %lu", periodSize, bufferSize); snd_pcm_hw_params_t *hw_params_copy; snd_pcm_hw_params_alloca(&hw_params_copy); snd_pcm_hw_params_copy(hw_params_copy, hw_params); // copy what we have and is already working // Make sure to not initialize too large to not cause underruns snd_pcm_uframes_t periodSizeMax = bufferSize / 3; if(snd_pcm_hw_params_set_period_size_max(m_pcm, hw_params_copy, &periodSizeMax, NULL) != 0) { snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: Failed to limit periodSize to %lu", periodSizeMax); } // first trying bufferSize, PeriodSize // for more info see here: // http://mailman.alsa-project.org/pipermail/alsa-devel/2009-September/021069.html // the last three tries are done as within pulseaudio // backup periodSize and bufferSize first. Restore them after every failed try snd_pcm_uframes_t periodSizeTemp, bufferSizeTemp; periodSizeTemp = periodSize; bufferSizeTemp = bufferSize; if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0 || snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { bufferSize = bufferSizeTemp; periodSize = periodSizeTemp; // retry with PeriodSize, bufferSize snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy if (snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 || snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { // try only periodSize periodSize = periodSizeTemp; snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy if(snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { // try only BufferSize bufferSize = bufferSizeTemp; snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restory working copy if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { // set default that Alsa would choose CLog::Log(LOGWARNING, "CAESinkAlsa::IntializeHW - Using default alsa values - set failed"); if (snd_pcm_hw_params(m_pcm, hw_params) != 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Could not init a valid sink"); return false; } } } // reread values when alsa default was kept snd_pcm_get_params(m_pcm, &bufferSize, &periodSize); } } CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, bufferSize); /* set the format parameters */ outconfig.sampleRate = sampleRate; outconfig.periodSize = periodSize; outconfig.frameSize = snd_pcm_frames_to_bytes(m_pcm, 1); m_bufferSize = (unsigned int)bufferSize; m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout); return true; }
bool CAESinkOSS::Initialize(AEAudioFormat &format, std::string &device) { m_initFormat = format; format.m_channelLayout = GetChannelLayout(format); device = GetDeviceUse(format, device); #ifdef __linux__ /* try to open in exclusive mode first (no software mixing) */ m_fd = open(device.c_str(), O_WRONLY | O_EXCL, 0); if (m_fd == -1) #endif m_fd = open(device.c_str(), O_WRONLY, 0); if (m_fd == -1) { CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to open the audio device: %s", device.c_str()); return false; } int format_mask; if (ioctl(m_fd, SNDCTL_DSP_GETFMTS, &format_mask) == -1) { close(m_fd); CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get supported formats, assuming AFMT_S16_NE"); return false; } #ifdef OSS4 bool useCooked = true; #endif int oss_fmt = 0; #ifdef AFMT_FLOAT if ((format.m_dataFormat == AE_FMT_FLOAT) && (format_mask & AFMT_FLOAT )) oss_fmt = AFMT_FLOAT; else #endif #ifdef AFMT_S32_NE if ((format.m_dataFormat == AE_FMT_S32NE) && (format_mask & AFMT_S32_NE)) oss_fmt = AFMT_S32_NE; else if ((format.m_dataFormat == AE_FMT_S32BE) && (format_mask & AFMT_S32_BE)) oss_fmt = AFMT_S32_BE; else if ((format.m_dataFormat == AE_FMT_S32LE) && (format_mask & AFMT_S32_LE)) oss_fmt = AFMT_S32_LE; else #endif if ((format.m_dataFormat == AE_FMT_S16NE) && (format_mask & AFMT_S16_NE)) oss_fmt = AFMT_S16_NE; else if ((format.m_dataFormat == AE_FMT_S16BE) && (format_mask & AFMT_S16_BE)) oss_fmt = AFMT_S16_BE; else if ((format.m_dataFormat == AE_FMT_S16LE) && (format_mask & AFMT_S16_LE)) oss_fmt = AFMT_S16_LE; else if ((format.m_dataFormat == AE_FMT_S8 ) && (format_mask & AFMT_S8 )) oss_fmt = AFMT_S8; else if ((format.m_dataFormat == AE_FMT_U8 ) && (format_mask & AFMT_U8 )) oss_fmt = AFMT_U8; else if ((AE_IS_RAW(format.m_dataFormat) ) && (format_mask & AFMT_AC3 )) oss_fmt = AFMT_AC3; else if (AE_IS_RAW(format.m_dataFormat)) { close(m_fd); CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable RAW output format"); return false; } else { CLog::Log(LOGINFO, "CAESinkOSS::Initialize - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat)); /* fallback to the best supported format */ #ifdef AFMT_FLOAT if (format_mask & AFMT_FLOAT ) { oss_fmt = AFMT_FLOAT; format.m_dataFormat = AE_FMT_FLOAT; } else #endif #ifdef AFMT_S32_NE if (format_mask & AFMT_S32_NE) { oss_fmt = AFMT_S32_NE; format.m_dataFormat = AE_FMT_S32NE; } else if (format_mask & AFMT_S32_BE) { oss_fmt = AFMT_S32_BE; format.m_dataFormat = AE_FMT_S32BE; } else if (format_mask & AFMT_S32_LE) { oss_fmt = AFMT_S32_LE; format.m_dataFormat = AE_FMT_S32LE; } else #endif if (format_mask & AFMT_S16_NE) { oss_fmt = AFMT_S16_NE; format.m_dataFormat = AE_FMT_S16NE; } else if (format_mask & AFMT_S16_BE) { oss_fmt = AFMT_S16_BE; format.m_dataFormat = AE_FMT_S16BE; } else if (format_mask & AFMT_S16_LE) { oss_fmt = AFMT_S16_LE; format.m_dataFormat = AE_FMT_S16LE; } else if (format_mask & AFMT_S8 ) { oss_fmt = AFMT_S8; format.m_dataFormat = AE_FMT_S8; } else if (format_mask & AFMT_U8 ) { oss_fmt = AFMT_U8; format.m_dataFormat = AE_FMT_U8; } else { CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to find a suitable native output format, will try to use AE_FMT_S16NE anyway"); oss_fmt = AFMT_S16_NE; format.m_dataFormat = AE_FMT_S16NE; #ifdef OSS4 /* dont use cooked if we did not find a native format, OSS might be able to convert */ useCooked = false; #endif } } #ifdef OSS4 if (useCooked) { int oss_cooked = 1; if (ioctl(m_fd, SNDCTL_DSP_COOKEDMODE, &oss_cooked) == -1) CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set cooked mode"); } #endif if (ioctl(m_fd, SNDCTL_DSP_SETFMT, &oss_fmt) == -1) { close(m_fd); CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the data format (%s)", CAEUtil::DataFormatToStr(format.m_dataFormat)); return false; } /* find the number we need to open to access the channels we need */ bool found = false; int oss_ch = 0; for (int ch = format.m_channelLayout.Count(); ch < 9; ++ch) { oss_ch = ch; if (ioctl(m_fd, SNDCTL_DSP_CHANNELS, &oss_ch) != -1 && oss_ch >= (int)format.m_channelLayout.Count()) { found = true; break; } } if (!found) CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to access the number of channels required, falling back"); #if defined(TARGET_FREEBSD) /* fix hdmi 8 channels order */ if (!AE_IS_RAW(format.m_dataFormat) && 8 == oss_ch) { unsigned long long order = 0x0000000087346521ULL; if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1) CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the channel order"); } #elif defined(OSS4) unsigned long long order = 0; for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i) switch (format.m_channelLayout[i]) { case AE_CH_FL : order = (order << 4) | CHID_L ; break; case AE_CH_FR : order = (order << 4) | CHID_R ; break; case AE_CH_FC : order = (order << 4) | CHID_C ; break; case AE_CH_LFE: order = (order << 4) | CHID_LFE; break; case AE_CH_SL : order = (order << 4) | CHID_LS ; break; case AE_CH_SR : order = (order << 4) | CHID_RS ; break; case AE_CH_BL : order = (order << 4) | CHID_LR ; break; case AE_CH_BR : order = (order << 4) | CHID_RR ; break; default: continue; } if (ioctl(m_fd, SNDCTL_DSP_SET_CHNORDER, &order) == -1) { if (ioctl(m_fd, SNDCTL_DSP_GET_CHNORDER, &order) == -1) { CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to get the channel order, assuming CHNORDER_NORMAL"); } } #endif int tmp = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count() * OSS_FRAMES; int pos = 0; while ((tmp & 0x1) == 0x0) { tmp = tmp >> 1; ++pos; } int oss_frag = (4 << 16) | pos; if (ioctl(m_fd, SNDCTL_DSP_SETFRAGMENT, &oss_frag) == -1) CLog::Log(LOGWARNING, "CAESinkOSS::Initialize - Failed to set the fragment size"); int oss_sr = format.m_sampleRate; if (ioctl(m_fd, SNDCTL_DSP_SPEED, &oss_sr) == -1) { close(m_fd); CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set the sample rate"); return false; } audio_buf_info bi; if (ioctl(m_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) { close(m_fd); CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to get the output buffer size"); return false; } if (fcntl(m_fd, F_SETFL, fcntl(m_fd, F_GETFL, 0) | O_NONBLOCK) == -1) { close(m_fd); CLog::Log(LOGERROR, "CAESinkOSS::Initialize - Failed to set non blocking writes"); return false; } format.m_sampleRate = oss_sr; format.m_frameSize = (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3) * format.m_channelLayout.Count(); format.m_frames = bi.fragsize / format.m_frameSize; format.m_frameSamples = format.m_frames * format.m_channelLayout.Count(); m_device = device; m_format = format; return true; }
bool CAESinkPULSE::Initialize(AEAudioFormat &format, std::string &device) { { CSingleLock lock(m_sec); m_IsAllocated = false; } m_passthrough = false; m_BytesPerSecond = 0; m_BufferSize = 0; m_Channels = 0; m_Stream = NULL; m_Context = NULL; m_periodSize = 0; if (!SetupContext(NULL, &m_Context, &m_MainLoop)) { CLog::Log(LOGNOTICE, "PulseAudio might not be running. Context was not created."); Deinitialize(); return false; } pa_threaded_mainloop_lock(m_MainLoop); struct pa_channel_map map; pa_channel_map_init(&map); // PULSE cannot cope with e.g. planar formats so we fallback to FLOAT // when we receive an invalid pulse format if (AEFormatToPulseFormat(format.m_dataFormat) == PA_SAMPLE_INVALID) { CLog::Log(LOGDEBUG, "PULSE does not support format: %s - will fallback to AE_FMT_FLOAT", CAEUtil::DataFormatToStr(format.m_dataFormat)); format.m_dataFormat = AE_FMT_FLOAT; } m_passthrough = AE_IS_RAW(format.m_dataFormat); if(m_passthrough) { map.channels = 2; format.m_channelLayout = AE_CH_LAYOUT_2_0; } else { map = AEChannelMapToPAChannel(format.m_channelLayout); // if count has changed we need to fit the AE Map if(map.channels != format.m_channelLayout.Count()) format.m_channelLayout = PAChannelToAEChannelMap(map); } m_Channels = format.m_channelLayout.Count(); // store information about current sink SinkInfoStruct sinkStruct; sinkStruct.mainloop = m_MainLoop; sinkStruct.device_found = false; // get real sample rate of the device we want to open - to avoid resampling bool isDefaultDevice = (device == "Default"); WaitForOperation(pa_context_get_sink_info_by_name(m_Context, isDefaultDevice ? NULL : device.c_str(), SinkInfoCallback, &sinkStruct), m_MainLoop, "Get Sink Info"); // only check if the device is existing - don't alter the sample rate if (!sinkStruct.device_found) { CLog::Log(LOGERROR, "PulseAudio: Sink %s not found", device.c_str()); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } // Pulse can resample everything between 1 hz and 192000 hz // Make sure we are in the range that we originally added format.m_sampleRate = std::max(5512U, std::min(format.m_sampleRate, 192000U)); pa_format_info *info[1]; info[0] = pa_format_info_new(); info[0]->encoding = AEFormatToPulseEncoding(format.m_dataFormat); if(!m_passthrough) { pa_format_info_set_sample_format(info[0], AEFormatToPulseFormat(format.m_dataFormat)); pa_format_info_set_channel_map(info[0], &map); } pa_format_info_set_channels(info[0], m_Channels); // PA requires m_encodedRate in order to do EAC3 unsigned int samplerate = format.m_sampleRate; if (m_passthrough && (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937)) { // this is only used internally for PA to use EAC3 samplerate = format.m_encodedRate; } pa_format_info_set_rate(info[0], samplerate); if (!pa_format_info_valid(info[0])) { CLog::Log(LOGERROR, "PulseAudio: Invalid format info"); pa_format_info_free(info[0]); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } pa_sample_spec spec; #if PA_CHECK_VERSION(2,0,0) pa_format_info_to_sample_spec(info[0], &spec, NULL); #else spec.rate = (AEFormatToPulseEncoding(format.m_dataFormat) == PA_ENCODING_EAC3_IEC61937) ? 4 * samplerate : samplerate; spec.format = AEFormatToPulseFormat(format.m_dataFormat); spec.channels = m_Channels; #endif if (!pa_sample_spec_valid(&spec)) { CLog::Log(LOGERROR, "PulseAudio: Invalid sample spec"); pa_format_info_free(info[0]); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } m_BytesPerSecond = pa_bytes_per_second(&spec); unsigned int frameSize = pa_frame_size(&spec); m_Stream = pa_stream_new_extended(m_Context, "kodi audio stream", info, 1, NULL); pa_format_info_free(info[0]); if (m_Stream == NULL) { CLog::Log(LOGERROR, "PulseAudio: Could not create a stream"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } pa_stream_set_state_callback(m_Stream, StreamStateCallback, m_MainLoop); pa_stream_set_write_callback(m_Stream, StreamRequestCallback, m_MainLoop); pa_stream_set_latency_update_callback(m_Stream, StreamLatencyUpdateCallback, m_MainLoop); // default buffer construction // align with AE's max buffer unsigned int latency = m_BytesPerSecond / 2.5; // 400 ms unsigned int process_time = latency / 4; // 100 ms if(sinkStruct.isHWDevice) { // on hw devices buffers can be further reduced // 200ms max latency // 50ms min packet size latency = m_BytesPerSecond / 5; process_time = latency / 4; } pa_buffer_attr buffer_attr; buffer_attr.fragsize = latency; buffer_attr.maxlength = (uint32_t) -1; buffer_attr.minreq = process_time; buffer_attr.prebuf = (uint32_t) -1; buffer_attr.tlength = latency; if (pa_stream_connect_playback(m_Stream, isDefaultDevice ? NULL : device.c_str(), &buffer_attr, ((pa_stream_flags)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY)), NULL, NULL) < 0) { CLog::Log(LOGERROR, "PulseAudio: Failed to connect stream to output"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } /* Wait until the stream is ready */ do { pa_threaded_mainloop_wait(m_MainLoop); CLog::Log(LOGDEBUG, "PulseAudio: Stream %s", StreamStateToString(pa_stream_get_state(m_Stream))); } while (pa_stream_get_state(m_Stream) != PA_STREAM_READY && pa_stream_get_state(m_Stream) != PA_STREAM_FAILED); if (pa_stream_get_state(m_Stream) == PA_STREAM_FAILED) { CLog::Log(LOGERROR, "PulseAudio: Waited for the stream but it failed"); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } const pa_buffer_attr *a; if (!(a = pa_stream_get_buffer_attr(m_Stream))) { CLog::Log(LOGERROR, "PulseAudio: %s", pa_strerror(pa_context_errno(m_Context))); pa_threaded_mainloop_unlock(m_MainLoop); Deinitialize(); return false; } else { unsigned int packetSize = a->minreq; m_BufferSize = a->tlength; m_periodSize = a->minreq; format.m_frames = packetSize / frameSize; } { CSingleLock lock(m_sec); // Register Callback for Sink changes pa_context_set_subscribe_callback(m_Context, SinkChangedCallback, this); const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SINK; pa_operation *op = pa_context_subscribe(m_Context, mask, NULL, this); if (op != NULL) pa_operation_unref(op); // Register Callback for Sink Info changes - this handles volume pa_context_set_subscribe_callback(m_Context, SinkInputInfoChangedCallback, this); const pa_subscription_mask_t mask_input = PA_SUBSCRIPTION_MASK_SINK_INPUT; pa_operation* op_sinfo = pa_context_subscribe(m_Context, mask_input, NULL, this); if (op_sinfo != NULL) pa_operation_unref(op_sinfo); } pa_threaded_mainloop_unlock(m_MainLoop); format.m_frameSize = frameSize; format.m_frameSamples = format.m_frames * format.m_channelLayout.Count(); m_format = format; format.m_dataFormat = m_passthrough ? AE_FMT_S16NE : format.m_dataFormat; CLog::Log(LOGNOTICE, "PulseAudio: Opened device %s in %s mode with Buffersize %u ms", device.c_str(), m_passthrough ? "passthrough" : "pcm", (unsigned int) ((m_BufferSize / (float) m_BytesPerSecond) * 1000)); // Cork stream will resume when adding first package Pause(true); { CSingleLock lock(m_sec); m_IsAllocated = true; } return true; }
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list) { /* ensure that ALSA has been initialized */ if(!snd_config) snd_config_update(); snd_ctl_t *ctlhandle; snd_pcm_t *pcmhandle; snd_ctl_card_info_t *ctlinfo; snd_ctl_card_info_alloca(&ctlinfo); memset(ctlinfo, 0, snd_ctl_card_info_sizeof()); snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); memset(hwparams, 0, snd_pcm_hw_params_sizeof()); snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); memset(pcminfo, 0, snd_pcm_info_sizeof()); /* get the sound config */ snd_config_t *config; snd_config_copy(&config, snd_config); std::string strHwName; int n_cards = -1; while (snd_card_next(&n_cards) == 0 && n_cards != -1) { std::stringstream sstr; sstr << "hw:" << n_cards; std::string strHwName = sstr.str(); if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) != 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to open control for device %s", strHwName.c_str()); continue; } if (snd_ctl_card_info(ctlhandle, ctlinfo) != 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to get card control info for device %s", strHwName.c_str()); snd_ctl_close(ctlhandle); continue; } snd_hctl_t *hctl; if (snd_hctl_open_ctl(&hctl, ctlhandle) != 0) hctl = NULL; snd_hctl_load(hctl); int pcm_index = 0; int iec958_index = 0; int hdmi_index = 0; int dev = -1; while (snd_ctl_pcm_next_device(ctlhandle, &dev) == 0 && dev != -1) { snd_pcm_info_set_device (pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0 ); snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK); if (snd_ctl_pcm_info(ctlhandle, pcminfo) < 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping device %s,%d as it does not have PCM playback ability", strHwName.c_str(), dev); continue; } int dev_index; sstr.str(std::string()); CAEDeviceInfo info; std::string devname = snd_pcm_info_get_name(pcminfo); bool maybeHDMI = false; /* detect HDMI */ if (devname.find("HDMI") != std::string::npos) { info.m_deviceType = AE_DEVTYPE_HDMI; dev_index = hdmi_index++; sstr << "hdmi"; } else { /* detect IEC958 */ /* some HDMI devices (intel) report Digital for HDMI also */ if (devname.find("Digital") != std::string::npos) maybeHDMI = true; if (maybeHDMI || devname.find("IEC958" ) != std::string::npos) { info.m_deviceType = AE_DEVTYPE_IEC958; dev_index = iec958_index; /* dont increment, it might be HDMI */ sstr << "iec958"; } else { info.m_deviceType = AE_DEVTYPE_PCM; dev_index = pcm_index++; sstr << "hw"; } } /* build the driver string to pass to ALSA */ sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index; info.m_deviceName = sstr.str(); /* get the friendly display name*/ info.m_displayName = snd_ctl_card_info_get_name(ctlinfo); info.m_displayNameExtra = devname; /* open the device for testing */ int err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config); /* if open of possible IEC958 failed and it could be HDMI, try as HDMI */ if (err < 0 && maybeHDMI) { /* check for HDMI if it failed */ sstr.str(std::string()); dev_index = hdmi_index; sstr << "hdmi"; sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index; info.m_deviceName = sstr.str(); err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config); /* if it was valid, increment the index and set the type */ if (err >= 0) { ++hdmi_index; info.m_deviceType = AE_DEVTYPE_HDMI; } } /* if it's still IEC958, increment the index */ if (info.m_deviceType == AE_DEVTYPE_IEC958) ++iec958_index; /* final error check */ if (err < 0) { CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - Unable to open %s for capability detection", strHwName.c_str()); continue; } /* see if we can get ELD for this device */ if (info.m_deviceType == AE_DEVTYPE_HDMI) { bool badHDMI = false; if (hctl && !GetELD(hctl, dev, info, badHDMI)) CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to obtain ELD information for device %s, make sure you have ALSA >= 1.0.25", info.m_deviceName.c_str()); if (badHDMI) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping HDMI device %s as it has no ELD data", info.m_deviceName.c_str()); continue; } } /* ensure we can get a playback configuration for the device */ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0) { CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - No playback configurations available for device %s", info.m_deviceName.c_str()); snd_pcm_close(pcmhandle); continue; } /* detect the available sample rates */ for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate) if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0) info.m_sampleRates.push_back(*rate); /* detect the channels available */ int channels = 0; for (int i = 1; i <= ALSA_MAX_CHANNELS; ++i) if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0) channels = i; CAEChannelInfo alsaChannels; for (int i = 0; i < channels; ++i) { if (!info.m_channels.HasChannel(ALSAChannelMap[i])) info.m_channels += ALSAChannelMap[i]; alsaChannels += ALSAChannelMap[i]; } /* remove the channels from m_channels that we cant use */ info.m_channels.ResolveChannels(alsaChannels); /* detect the PCM sample formats that are available */ for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) continue; snd_pcm_format_t fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN) continue; if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0) info.m_dataFormats.push_back(i); } snd_pcm_close(pcmhandle); list.push_back(info); } /* snd_hctl_close also closes ctlhandle */ if (hctl) snd_hctl_close(hctl); else snd_ctl_close(ctlhandle); } }
bool CAESinkPi::Initialize(AEAudioFormat &format, std::string &device) { char response[80]; /* if we are raw need to let gpu know */ if (AE_IS_RAW(format.m_dataFormat)) { vc_gencmd(response, sizeof response, "hdmi_stream_channels 1"); m_passthrough = true; } else { vc_gencmd(response, sizeof response, "hdmi_stream_channels 0"); m_passthrough = false; } m_initDevice = device; m_initFormat = format; // setup for a 50ms sink feed from SoftAE format.m_dataFormat = AE_FMT_S16NE; format.m_frames = format.m_sampleRate * AUDIO_PLAYBUFFER; format.m_frameSamples = format.m_channelLayout.Count(); format.m_frameSize = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3); format.m_sampleRate = std::max(8000U, std::min(96000U, format.m_sampleRate)); m_format = format; m_sinkbuffer_size = format.m_frameSize * format.m_frames * NUM_OMX_BUFFERS; m_sinkbuffer_sec_per_byte = 1.0 / (double)(format.m_frameSize * format.m_sampleRate); CLog::Log(LOGDEBUG, "%s:%s Format:%d Channels:%d Samplerate:%d framesize:%d bufsize:%d bytes/s=%.2f", CLASSNAME, __func__, format.m_dataFormat, format.m_channelLayout.Count(), format.m_sampleRate, format.m_frameSize, m_sinkbuffer_size, 1.0/m_sinkbuffer_sec_per_byte); // This may be called before Application calls g_RBP.Initialise, so call it here too g_RBP.Initialize(); CLog::Log(LOGDEBUG, "%s:%s", CLASSNAME, __func__); OMX_ERRORTYPE omx_err = OMX_ErrorNone; if (!m_omx_render.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit)) CLog::Log(LOGERROR, "%s::%s - m_omx_render.Initialize omx_err(0x%08x)", CLASSNAME, __func__, omx_err); OMX_INIT_STRUCTURE(m_pcm_input); m_pcm_input.nPortIndex = m_omx_render.GetInputPort(); m_pcm_input.eNumData = OMX_NumericalDataSigned; m_pcm_input.eEndian = OMX_EndianLittle; m_pcm_input.bInterleaved = OMX_TRUE; m_pcm_input.nBitPerSample = 16; m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear; m_pcm_input.nChannels = m_format.m_frameSamples; m_pcm_input.nSamplingRate = m_format.m_sampleRate; m_pcm_input.eChannelMapping[0] = OMX_AUDIO_ChannelLF; m_pcm_input.eChannelMapping[1] = OMX_AUDIO_ChannelRF; m_pcm_input.eChannelMapping[2] = OMX_AUDIO_ChannelMax; omx_err = m_omx_render.SetParameter(OMX_IndexParamAudioPcm, &m_pcm_input); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s::%s - error m_omx_render SetParameter omx_err(0x%08x)", CLASSNAME, __func__, omx_err); m_omx_render.ResetEos(); SetAudioDest(); // set up the number/size of buffers for decoder input OMX_PARAM_PORTDEFINITIONTYPE port_param; OMX_INIT_STRUCTURE(port_param); port_param.nPortIndex = m_omx_render.GetInputPort(); omx_err = m_omx_render.GetParameter(OMX_IndexParamPortDefinition, &port_param); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - error get OMX_IndexParamPortDefinition (input) omx_err(0x%08x)", CLASSNAME, __func__, omx_err); port_param.nBufferCountActual = std::max((unsigned int)port_param.nBufferCountMin, (unsigned int)NUM_OMX_BUFFERS); port_param.nBufferSize = m_sinkbuffer_size / port_param.nBufferCountActual; omx_err = m_omx_render.SetParameter(OMX_IndexParamPortDefinition, &port_param); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - error set OMX_IndexParamPortDefinition (intput) omx_err(0x%08x)", CLASSNAME, __func__, omx_err); omx_err = m_omx_render.AllocInputBuffers(); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - Error alloc buffers 0x%08x", CLASSNAME, __func__, omx_err); omx_err = m_omx_render.SetStateForComponent(OMX_StateExecuting); if (omx_err != OMX_ErrorNone) CLog::Log(LOGERROR, "%s:%s - m_omx_render OMX_StateExecuting omx_err(0x%08x)", CLASSNAME, __func__, omx_err); m_Initialized = true; return true; }
bool CAESinkIntelSMD::Initialize(AEAudioFormat &format, std::string &device) { VERBOSE2(); CLog::Log(LOGDEBUG, "%s: device: %s, data format: %s, sample rate: %d, channel count: %d, frame size: %d", __DEBUG_ID__, device.c_str(), CAEUtil::DataFormatToStr(format.m_dataFormat), format.m_sampleRate, format.m_channelLayout.Count(), format.m_frameSize); CSingleLock lock(m_SMDAudioLock); bool bIsHDMI = isHDMI(device); bool bIsSPDIF = isSPDIF(device); bool bIsAnalog = isAnalog(device); int deviceType = getDeviceType(device); ismd_result_t result; AEDataFormat inputDataFormat = format.m_dataFormat; bool bSPDIFPassthrough = false; bool bHDMIPassthrough = false; bool bIsRawCodec = AE_IS_RAW(inputDataFormat); format.m_sampleRate = getOutputSampleRate(deviceType, format.m_sampleRate); format.m_dataFormat = getAEDataFormat(deviceType, format.m_dataFormat, format.m_frameSize); int channels = format.m_channelLayout.Count(); // can not support more than 2 channels on anything other than HDMI if (channels > 2 && (bIsSPDIF || bIsAnalog)) channels = 2; // support for more than 8 channels not supported else if (channels > 8) channels = 8; ismd_audio_processor_t audioProcessor = -1; ismd_audio_format_t ismdAudioInputFormat = ISMD_AUDIO_MEDIA_FMT_INVALID; audioProcessor = g_IntelSMDGlobals.GetAudioProcessor(); if(audioProcessor == -1) { CLog::Log(LOGERROR, "%s audioProcessor is not valid", __DEBUG_ID__); return false; } // disable all outputs g_IntelSMDGlobals.DisableAudioOutput(g_IntelSMDGlobals.GetHDMIOutput()); g_IntelSMDGlobals.DisableAudioOutput(g_IntelSMDGlobals.GetSPDIFOutput()); g_IntelSMDGlobals.DisableAudioOutput(g_IntelSMDGlobals.GetI2SOutput()); m_audioDevice = g_IntelSMDGlobals.CreateAudioInput(false); if(m_audioDevice == -1) { CLog::Log(LOGERROR, "%s failed to create audio input", __DEBUG_ID__); return false; } g_IntelSMDGlobals.SetPrimaryAudioDevice(m_audioDevice); m_audioDeviceInput = g_IntelSMDGlobals.GetAudioDevicePort(m_audioDevice); if(m_audioDeviceInput == -1) { CLog::Log(LOGERROR, "%s failed to create audio input port", __DEBUG_ID__); return false; } ismdAudioInputFormat = GetISMDFormat(inputDataFormat); unsigned int uiBitsPerSample = CAEUtil::DataFormatToBits(format.m_dataFormat); unsigned int uiUsedBitsPerSample = CAEUtil::DataFormatToUsedBits(format.m_dataFormat); // Are we doing DD+ -> DD mode bool bAC3Encode = false; if (bIsHDMI) { unsigned int sampleRate = format.m_sampleRate; unsigned int bitsPerSample = uiUsedBitsPerSample; if (format.m_encodedRate != 0) sampleRate = format.m_encodedRate; unsigned int suggSampleRate = sampleRate; if (!CheckEDIDSupport(ismdAudioInputFormat, channels, suggSampleRate, bitsPerSample, bAC3Encode)) { if (suggSampleRate != sampleRate) format.m_sampleRate = suggSampleRate; if (bitsPerSample != uiUsedBitsPerSample) { if (uiUsedBitsPerSample == 24) { format.m_dataFormat = AE_FMT_S24NE4; uiUsedBitsPerSample = bitsPerSample; } else if (uiUsedBitsPerSample == 32) { format.m_dataFormat = AE_FMT_S32LE; uiUsedBitsPerSample = bitsPerSample; } else { format.m_dataFormat = AE_FMT_S16LE; uiUsedBitsPerSample = 16; } uiBitsPerSample = CAEUtil::DataFormatToBits(format.m_dataFormat); } //format.m_frameSize = uiBitsPerSample/4; } } else if (bIsSPDIF && ismdAudioInputFormat == ISMD_AUDIO_MEDIA_FMT_DD_PLUS && ISMD_SUCCESS == ismd_audio_codec_available((ismd_audio_format_t) ISMD_AUDIO_ENCODE_FMT_AC3)) { bAC3Encode = true; } unsigned int outputSampleRate = format.m_sampleRate; // for raw codecs, send as PCM passthrough if (!bAC3Encode && bIsRawCodec && ismdAudioInputFormat != ISMD_AUDIO_MEDIA_FMT_DD && ismdAudioInputFormat != ISMD_AUDIO_MEDIA_FMT_TRUE_HD) { format.m_dataFormat = AE_FMT_S16NE; ismdAudioInputFormat = ISMD_AUDIO_MEDIA_FMT_PCM; bHDMIPassthrough = bSPDIFPassthrough = true; } format.m_channelLayout.Reset(); if (bIsRawCodec) { for (int i = 0; i < channels; ++i) format.m_channelLayout += AE_CH_RAW; } // TODO: This currently handles Mono,Stereo, 5.1, 7.1 correctly // Handle the other cases (i.e. 6.1 DTS) else { for (int i = 0; i < channels; ++i) format.m_channelLayout += s_chMap[i]; } //TODO: Handle non normal channel configs (3 channel, etc). int inputChannelConfig = AUDIO_CHAN_CONFIG_2_CH; if (format.m_channelLayout.Count() == 1) inputChannelConfig = AUDIO_CHAN_CONFIG_1_CH; else if (format.m_channelLayout.Count() == 6) inputChannelConfig = AUDIO_CHAN_CONFIG_6_CH; else if (format.m_channelLayout.Count() == 8) inputChannelConfig = AUDIO_CHAN_CONFIG_8_CH; format.m_frameSize = channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3); // if standard audio keep buffer small so delay is short if (!bIsRawCodec) { // try to keep roughly 5ms buffer using multiples of a 1024 buffer size int numBuffers = ((0.005*((double)(format.m_sampleRate*format.m_frameSize))) / 1024.0) + 0.5; if (numBuffers == 0) numBuffers = 1; else if (numBuffers > 8) numBuffers = 8; m_dwChunkSize = numBuffers*1024; } else { m_dwChunkSize = 8*1024; } m_dwBufferLen = m_dwChunkSize; format.m_frames = m_dwChunkSize/format.m_frameSize; format.m_frameSamples = format.m_frames*channels; m_frameSize = format.m_frameSize; CLog::Log(LOGINFO, "%s ismdAudioInputFormat %d\n", __DEBUG_ID__, ismdAudioInputFormat); int counter = 0; while(counter < 5) { result = ismd_audio_input_set_data_format(audioProcessor, m_audioDevice, ismdAudioInputFormat); if (result != ISMD_SUCCESS) { CLog::Log(LOGERROR, "%s ismd_audio_input_set_data_format failed. retrying %d %d", __DEBUG_ID__, counter, result); counter++; usleep(1000); } else break; } switch( ismdAudioInputFormat ) { case ISMD_AUDIO_MEDIA_FMT_DD: CLog::Log(LOGDEBUG, "%s: Initialize DD detected", __DEBUG_ID__); bHDMIPassthrough = bSPDIFPassthrough = true; break; case ISMD_AUDIO_MEDIA_FMT_DD_PLUS: CLog::Log(LOGDEBUG, "%s: Initialize DD Plus detected", __DEBUG_ID__); bHDMIPassthrough = true; // check special case for DD+->DD using DDCO if(bAC3Encode) { CLog::Log(LOGDEBUG, "%s: Initialize EAC3->AC3 transcoding is on", __DEBUG_ID__); bHDMIPassthrough = false; bAC3Encode = true; ConfigureDolbyPlusModes(audioProcessor, m_audioDevice, bAC3Encode); } break; case ISMD_AUDIO_MEDIA_FMT_DTS: case ISMD_AUDIO_MEDIA_FMT_DTS_LBR: CLog::Log(LOGDEBUG, "%s: Initialize DTS detected", __DEBUG_ID__); bHDMIPassthrough = bSPDIFPassthrough = true; break; case ISMD_AUDIO_MEDIA_FMT_DTS_HD: case ISMD_AUDIO_MEDIA_FMT_DTS_HD_MA: case ISMD_AUDIO_MEDIA_FMT_DTS_HD_HRA: CLog::Log(LOGDEBUG, "%s: Initialize DTS-HD detected", __DEBUG_ID__); bHDMIPassthrough = true; outputSampleRate = format.m_encodedRate; channels = 2; break; case ISMD_AUDIO_MEDIA_FMT_TRUE_HD: CLog::Log(LOGDEBUG, "%s: Initialize TrueHD detected", __DEBUG_ID__); bHDMIPassthrough = true; outputSampleRate = format.m_encodedRate; channels = 2; break; case ISMD_AUDIO_MEDIA_FMT_PCM: result = ismd_audio_input_set_pcm_format(audioProcessor, m_audioDevice, uiBitsPerSample, format.m_sampleRate, inputChannelConfig); if (result != ISMD_SUCCESS) { CLog::Log(LOGERROR, "%s - ismd_audio_input_set_pcm_format: %d", __DEBUG_ID__, result); // return false; } break; default: break; } // I2S. Nothing to touch here. we always use defaults // SPIDF if(bIsSPDIF) { ismd_audio_output_t OutputSPDIF = g_IntelSMDGlobals.GetSPDIFOutput(); ismd_audio_output_config_t spdif_output_config; ConfigureAudioOutputParams(spdif_output_config, AE_DEVTYPE_IEC958, uiUsedBitsPerSample, outputSampleRate, channels, ismdAudioInputFormat, bSPDIFPassthrough, bAC3Encode); if(!g_IntelSMDGlobals.ConfigureAudioOutput(OutputSPDIF, spdif_output_config)) { CLog::Log(LOGERROR, "%s ConfigureAudioOutput SPDIF failed %d", __DEBUG_ID__, result); // return false; } //format.m_sampleRate = spdif_output_config.sample_rate; } // HDMI if(bIsHDMI) { ismd_audio_output_t OutputHDMI = g_IntelSMDGlobals.GetHDMIOutput(); ismd_audio_output_config_t hdmi_output_config; ConfigureAudioOutputParams(hdmi_output_config, AE_DEVTYPE_HDMI, uiUsedBitsPerSample, outputSampleRate, channels, ismdAudioInputFormat, bHDMIPassthrough, bAC3Encode); if(!g_IntelSMDGlobals.ConfigureAudioOutput(OutputHDMI, hdmi_output_config)) { CLog::Log(LOGERROR, "%s ConfigureAudioOutput HDMI failed %d", __DEBUG_ID__, result); return false; } //format.m_sampleRate = hdmi_output_config.sample_rate; } // Configure the master clock frequency CLog::Log(LOGINFO, "%s ConfigureMasterClock %d", __DEBUG_ID__, format.m_sampleRate); g_IntelSMDGlobals.ConfigureMasterClock(format.m_sampleRate); bSPDIFPassthrough = bIsSPDIF && bSPDIFPassthrough; bHDMIPassthrough = bIsHDMI && bHDMIPassthrough; ismd_audio_input_pass_through_config_t passthrough_config; memset(&passthrough_config, 0, sizeof(&passthrough_config)); if (bSPDIFPassthrough || bHDMIPassthrough) { passthrough_config.is_pass_through = TRUE; passthrough_config.supported_format_count = 1; passthrough_config.supported_formats[0] = ismdAudioInputFormat; } result = ismd_audio_input_set_as_primary(audioProcessor, m_audioDevice, passthrough_config); if (result != ISMD_SUCCESS) { CLog::Log(LOGERROR, "%s ismd_audio_input_set_as_primary failed %d", __DEBUG_ID__, result); // return false; } if(!g_IntelSMDGlobals.EnableAudioInput(m_audioDevice)) { CLog::Log(LOGERROR, "%s EnableAudioInput", __DEBUG_ID__); // return false; } // enable outputs if (bIsHDMI) { if(!g_IntelSMDGlobals.EnableAudioOutput(g_IntelSMDGlobals.GetHDMIOutput())) { CLog::Log(LOGERROR, "%s EnableAudioOutput HDMI failed", __DEBUG_ID__); // return false; } } if (bIsSPDIF) { if(!g_IntelSMDGlobals.EnableAudioOutput(g_IntelSMDGlobals.GetSPDIFOutput())) { CLog::Log(LOGERROR, "%s EnableAudioOutput SPDIF failed", __DEBUG_ID__); // return false; } } if (bIsAnalog) { if(!g_IntelSMDGlobals.EnableAudioOutput(g_IntelSMDGlobals.GetI2SOutput())) { CLog::Log(LOGERROR, "%s EnableAudioOutput I2S failed", __DEBUG_ID__); // return false; } } g_IntelSMDGlobals.SetAudioDeviceState(ISMD_DEV_STATE_PLAY, m_audioDevice); // m_fCurrentVolume = g_settings.m_fVolumeLevel; //g_IntelSMDGlobals.SetMasterVolume(m_fCurrentVolume); m_bPause = false; m_dSampleRate = format.m_sampleRate; m_bIsAllocated = true; // set latency when using passthrough since we are not using a timed audio interface if (bAC3Encode || ismdAudioInputFormat == ISMD_AUDIO_MEDIA_FMT_DD) m_latency = 0.675;//0.45; CLog::Log(LOGINFO, "%s done", __DEBUG_ID__); return true; }