Skip to content

Commit

Permalink
AESinkAlsa: Use 51Wide and 71Wide Maps when we need to do so
Browse files Browse the repository at this point in the history
  • Loading branch information
fritsch authored and FernetMenta committed Dec 8, 2013
1 parent 6a6b321 commit 6caeb83
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 44 deletions.
122 changes: 81 additions & 41 deletions xbmc/cores/AudioEngine/Sinks/AESinkALSA.cpp
Expand Up @@ -45,12 +45,24 @@ static enum AEChannel ALSAChannelMap[ALSA_MAX_CHANNELS + 1] = {
AE_CH_NULL
};

static enum AEChannel ALSAChannelMapWide[ALSA_MAX_CHANNELS + 1] = {
static enum AEChannel ALSAChannelMap51Wide[ALSA_MAX_CHANNELS + 1] = {
AE_CH_FL , AE_CH_FR , AE_CH_SL , AE_CH_SR , AE_CH_FC , AE_CH_LFE , AE_CH_BL , AE_CH_BR ,
AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
AE_CH_NULL
};

static enum AEChannel ALSAChannelMap71Wide[ALSA_MAX_CHANNELS + 1] = {
AE_CH_FLOC , AE_CH_FROC , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_FL , AE_CH_FR ,
AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
AE_CH_NULL
};

static enum AEChannel ALSAChannelMapPassthrough[ALSA_MAX_CHANNELS + 1] = {
AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW , AE_CH_RAW ,
AE_CH_UNKNOWN1, AE_CH_UNKNOWN2, AE_CH_UNKNOWN3, AE_CH_UNKNOWN4, AE_CH_UNKNOWN5, AE_CH_UNKNOWN6, AE_CH_UNKNOWN7, AE_CH_UNKNOWN8, /* for p16v devices */
AE_CH_NULL
};

static unsigned int ALSASampleRateList[] =
{
5512,
Expand Down Expand Up @@ -87,25 +99,37 @@ CAESinkALSA::~CAESinkALSA()
Deinitialize();
}

inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format)
inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format, unsigned int maxChannels)
{
enum AEChannel* channelMap = ALSAChannelMap;
unsigned int count = 0;

if (format.m_dataFormat == AE_FMT_AC3 ||
format.m_dataFormat == AE_FMT_DTS ||
format.m_dataFormat == AE_FMT_EAC3)
count = 2;
if (format.m_dataFormat == AE_FMT_AC3 ||
format.m_dataFormat == AE_FMT_DTS ||
format.m_dataFormat == AE_FMT_EAC3)
{
count = 2;
channelMap = ALSAChannelMapPassthrough;
}
else if (format.m_dataFormat == AE_FMT_TRUEHD ||
format.m_dataFormat == AE_FMT_DTSHD)
count = 8;
{
count = 8;
channelMap = ALSAChannelMapPassthrough;
}
else
{
// According to CEA-861-D only RL and RR are known. In case of a format having SL and SR channels
// but no BR BL channels, we use the wide map in order to open only the num of channels really
// needed.
enum AEChannel* channelMap = ALSAChannelMap;
if (format.m_channelLayout.HasChannel(AE_CH_SL) && !format.m_channelLayout.HasChannel(AE_CH_BL))
channelMap = ALSAChannelMapWide;
{
channelMap = ALSAChannelMap51Wide;
}
else if (maxChannels >= 8 && format.m_channelLayout.HasChannel(AE_CH_FLOC) && !format.m_channelLayout.HasChannel(AE_CH_SL))
{
channelMap = ALSAChannelMap71Wide;
}
for (unsigned int c = 0; c < 8; ++c)
for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
if (format.m_channelLayout[i] == channelMap[c])
Expand All @@ -116,8 +140,12 @@ inline CAEChannelInfo CAESinkALSA::GetChannelLayout(AEAudioFormat format)
}

CAEChannelInfo info;
for (unsigned int i = 0; i < count; ++i)
info += ALSAChannelMap[i];
for (unsigned int i = 0; i < count && i < maxChannels+1; ++i)
info += channelMap[i];

CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Input Channel Count: %d Output Channel Count: %d", format.m_channelLayout.Count(), count);
CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Requested Layout: %s", std::string(format.m_channelLayout).c_str());
CLog::Log(LOGDEBUG, "CAESinkALSA::GetChannelLayout - Got Layout: %s", std::string(info).c_str());

return info;
}
Expand All @@ -143,20 +171,22 @@ void CAESinkALSA::GetAESParams(AEAudioFormat format, std::string& params)

bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
{
CAEChannelInfo channelLayout;
CAEChannelInfo channelLayout = GetChannelLayout(format, 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))
{
channelLayout = GetChannelLayout(format);
format.m_dataFormat = AE_FMT_S16NE;
m_passthrough = true;
inconfig.format = AE_FMT_S16NE;
m_passthrough = true;
}
else
{
channelLayout = GetChannelLayout(format);
m_passthrough = false;
}
#if defined(HAS_LIBAMCODEC)
Expand All @@ -167,14 +197,12 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
}
#endif

if (channelLayout.Count() == 0)
if (inconfig.channels == 0)
{
CLog::Log(LOGERROR, "CAESinkALSA::Initialize - Unable to open the requested channel layout");
return false;
}

format.m_channelLayout = channelLayout;

AEDeviceType devType = AEDeviceTypeFromName(device);

std::string AESParams;
Expand All @@ -189,7 +217,7 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
snd_config_t *config;
snd_config_copy(&config, snd_config);

if (!OpenPCMDevice(device, AESParams, channelLayout.Count(), &m_pcm, 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);
Expand All @@ -205,13 +233,26 @@ bool CAESinkALSA::Initialize(AEAudioFormat &format, std::string &device)
/* free the sound config */
snd_config_delete(config);

if (!InitializeHW(format) || !InitializeSW(format))
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);
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;

Expand Down Expand Up @@ -255,7 +296,7 @@ snd_pcm_format_t CAESinkALSA::AEFormatToALSAFormat(const enum AEDataFormat forma
}
}

bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
bool CAESinkALSA::InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig)
{
snd_pcm_hw_params_t *hw_params;

Expand All @@ -265,35 +306,35 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
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();
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 (format.m_channelLayout.Count() > channelCount)
if (inconfig.channels > 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];
/* update outconfig */
outconfig.channels = channelCount;

snd_pcm_format_t fmt = AEFormatToALSAFormat(inconfig.format);
outconfig.format = inconfig.format;

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;
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(format.m_dataFormat));
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)
Expand Down Expand Up @@ -322,8 +363,8 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
}

/* 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));
outconfig.format = i;
CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(outconfig.format));
break;
}

Expand Down Expand Up @@ -418,10 +459,9 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, 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);
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);
Expand All @@ -431,7 +471,7 @@ bool CAESinkALSA::InitializeHW(AEAudioFormat &format)
return true;
}

bool CAESinkALSA::InitializeSW(AEAudioFormat &format)
bool CAESinkALSA::InitializeSW(const ALSAConfig &inconfig)
{
snd_pcm_sw_params_t *sw_params;
snd_pcm_uframes_t boundary;
Expand All @@ -444,7 +484,7 @@ bool CAESinkALSA::InitializeSW(AEAudioFormat &format)
snd_pcm_sw_params_set_silence_threshold(m_pcm, sw_params, 0);
snd_pcm_sw_params_get_boundary (sw_params, &boundary);
snd_pcm_sw_params_set_silence_size (m_pcm, sw_params, boundary);
snd_pcm_sw_params_set_avail_min (m_pcm, sw_params, format.m_frames);
snd_pcm_sw_params_set_avail_min (m_pcm, sw_params, inconfig.periodSize);

if (snd_pcm_sw_params(m_pcm, sw_params) < 0)
{
Expand Down
15 changes: 12 additions & 3 deletions xbmc/cores/AudioEngine/Sinks/AESinkALSA.h
Expand Up @@ -52,7 +52,7 @@ class CAESinkALSA : public IAESink

static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false);
private:
CAEChannelInfo GetChannelLayout(AEAudioFormat format);
CAEChannelInfo GetChannelLayout(AEAudioFormat format, unsigned int maxChannels);
void GetAESParams(const AEAudioFormat format, std::string& params);
void HandleError(const char* name, int err);

Expand All @@ -66,10 +66,19 @@ class CAESinkALSA : public IAESink
snd_pcm_t *m_pcm;
int m_timeout;

struct ALSAConfig
{
unsigned int sampleRate;
unsigned int periodSize;
unsigned int frameSize;
unsigned int channels;
AEDataFormat format;
};

static snd_pcm_format_t AEFormatToALSAFormat(const enum AEDataFormat format);

bool InitializeHW(AEAudioFormat &format);
bool InitializeSW(AEAudioFormat &format);
bool InitializeHW(const ALSAConfig &inconfig, ALSAConfig &outconfig);
bool InitializeSW(const ALSAConfig &inconfig);

static void AppendParams(std::string &device, const std::string &params);
static bool TryDevice(const std::string &name, snd_pcm_t **pcmp, snd_config_t *lconf);
Expand Down

0 comments on commit 6caeb83

Please sign in to comment.