Skip to content

Commit

Permalink
Audio Framework: handle more configuration scenarios. Bug fixes
Browse files Browse the repository at this point in the history
Bug fixes:
- If re-encoding to AC3 was occurring while downmixing: audio would be distorted
- Couldn't play 6.1 or 7.0 audio with LPCM/Analog out
- Wouldn't play DTS-HD High-Res unless HBRPassthrough was unchecked

New Features:
* We should now be able to play any number of channels combination from 1 to 8.
  - 1,3,4 will be downmixed to stereo
  - 2 and 5 will be upmixed to 5.1
  - 7,8 will be downmixed to 5.1 if supported, or stereo otherwise.
  This will only occur if the requested audio configuration isn't supported.
* Mono upmixing can be toggled between stereo or 5.1
  • Loading branch information
jyavenard committed Oct 1, 2011
1 parent e05e503 commit 4e5d52a
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 67 deletions.
125 changes: 73 additions & 52 deletions mythtv/libs/libmyth/audio/audiooutputbase.cpp
Expand Up @@ -373,8 +373,8 @@ float AudioOutputBase::GetStretchFactor(void) const
*/
bool AudioOutputBase::ToggleUpmix(void)
{
// Can only upmix from stereo to 6 ch
if (max_channels == 2 || source_channels != 2 || passthru)
// Can only upmix from mono/stereo to 6 ch
if (max_channels == 2 || source_channels > 2 || passthru)
return false;

upmix_default = !upmix_default;
Expand All @@ -392,11 +392,17 @@ bool AudioOutputBase::ToggleUpmix(void)
bool AudioOutputBase::SetupPassthrough(int codec, int codec_profile,
int &samplerate_tmp, int &channels_tmp)
{
if (codec == CODEC_ID_DTS &&
!output_settingsdigital->canFeature(FEATURE_DTSHD))
{
// We do not support DTS-HD bitstream so force extraction of the
// DTS core track instead
codec_profile = FF_PROFILE_DTS;
}
QString log = AudioOutputSettings::GetPassthroughParams(
codec, codec_profile,
samplerate_tmp, channels_tmp,
output_settingsdigital->GetMaxHDRate() == 768000);

VBAUDIO("Setting " + log + " passthrough");

if (m_spdifenc)
Expand All @@ -415,12 +421,8 @@ bool AudioOutputBase::SetupPassthrough(int codec, int codec_profile,
m_spdifenc->SetMaxHDRate(0);
break;
case FF_PROFILE_DTS_HD_HRA:
m_spdifenc->SetMaxHDRate(
OutputSettings(true)->canFeature(FEATURE_DTSHD) ?
192000 : 0);
break;
case FF_PROFILE_DTS_HD_MA:
m_spdifenc->SetMaxHDRate(OutputSettings(true)->GetMaxHDRate());
m_spdifenc->SetMaxHDRate(samplerate_tmp * channels_tmp / 2);
break;
}
}
Expand Down Expand Up @@ -448,39 +450,59 @@ AudioOutputSettings *AudioOutputBase::OutputSettings(bool digital)
*/
void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
{
AudioSettings settings = orig_settings;
int lsource_channels = settings.channels;
bool lneeds_upmix = false;
bool lneeds_downmix = false;
bool lreenc = false;
AudioSettings settings = orig_settings;
int lsource_channels = settings.channels;
int lconfigured_channels = configured_channels;
bool lneeds_upmix = false;
bool lneeds_downmix = false;
bool lreenc = false;

if (!settings.use_passthru)
{
// update channels configuration if source_channels has changed
if (lsource_channels > configured_channels)
{
if (lsource_channels <= 6)
configured_channels = min(max_channels, 6);
lconfigured_channels = min(max_channels, 6);
else if (lsource_channels > 6)
configured_channels = max_channels;
lconfigured_channels = max_channels;
}
else
{
// if source was mono or 5.0, and hardware doesn't support it
// we will upmix it respectively to stereo or 5.1
// (can safely assume hardware supports stereo)
// Ideally we should be able to upmix any channels configuration
if ((lsource_channels == 1 || lsource_channels == 5) &&
!output_settings->IsSupportedChannels(lsource_channels))
if (!output_settings->IsSupportedChannels(lsource_channels))
{
// 1 => 2, 5 => 6
configured_channels = lsource_channels + 1;
// if hardware doesn't support source audio configuration
// we will upmix/downmix to what we can
// (can safely assume hardware supports stereo)
switch (lsource_channels)
{
case 1:
lconfigured_channels = upmix_default ? 6 : 2;
break;
case 2:
//Will never happen
case 3:
case 4:
case 6:
lconfigured_channels = 2;
break;
case 5:
case 7:
if (output_settings->IsSupportedChannels(6))
lconfigured_channels = 6;
else
lconfigured_channels = 2;
break;
default:
lconfigured_channels = max_channels;
break;
}
}
else
configured_channels = (upmix_default && lsource_channels == 2) ?
lconfigured_channels =
(upmix_default && lsource_channels <= 2) ?
max_channels : lsource_channels;
}

/* Might we reencode a bitstream that's been decoded for timestretch?
If the device doesn't support the number of channels - see below */
if (output_settingsdigital->canFeature(FEATURE_AC3) &&
Expand All @@ -490,20 +512,21 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
}

// Enough channels? Upmix if not, but only from mono/stereo/5.0 to 5.1
if (IS_VALID_UPMIX_CHANNEL(settings.channels) && settings.channels < configured_channels)
if (IS_VALID_UPMIX_CHANNEL(settings.channels) &&
settings.channels < lconfigured_channels)
{
int conf_channels = (configured_channels > 6) ?
6 : configured_channels;
lconfigured_channels =
(lconfigured_channels > 6) ? 6 : lconfigured_channels;
VBAUDIO(QString("Needs upmix from %1 -> %2 channels")
.arg(settings.channels).arg(conf_channels));
settings.channels = conf_channels;
.arg(settings.channels).arg(lconfigured_channels));
settings.channels = lconfigured_channels;
lneeds_upmix = true;
}
else if (settings.channels > max_channels)
else if (settings.channels > lconfigured_channels)
{
VBAUDIO(QString("Needs downmix from %1 -> %2 channels")
.arg(settings.channels).arg(max_channels));
settings.channels = max_channels;
.arg(settings.channels).arg(lconfigured_channels));
settings.channels = lconfigured_channels;
lneeds_downmix = true;
}
}
Expand All @@ -517,13 +540,6 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
int samplerate_tmp, channels_tmp;
if (settings.use_passthru)
{
if (settings.codec == CODEC_ID_DTS &&
!output_settingsdigital->canFeature(FEATURE_DTSHD))
{
// We do not support DTS-HD bitstream so force extraction of the
// DTS core track instead
settings.codec_profile = FF_PROFILE_DTS;
}
samplerate_tmp = settings.samplerate;
SetupPassthrough(settings.codec, settings.codec_profile,
samplerate_tmp, channels_tmp);
Expand All @@ -535,6 +551,7 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
settings.format == format &&
settings.samplerate == source_samplerate &&
settings.use_passthru == passthru &&
lconfigured_channels == configured_channels &&
lneeds_upmix == needs_upmix && lreenc == reenc &&
lsource_channels == source_channels &&
lneeds_downmix == needs_downmix;
Expand All @@ -559,6 +576,7 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
reenc = lreenc;
codec = settings.codec;
passthru = settings.use_passthru;
configured_channels = lconfigured_channels;
needs_upmix = lneeds_upmix;
needs_downmix = lneeds_downmix;
format = output_format = settings.format;
Expand Down Expand Up @@ -593,13 +611,15 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
!output_settings->IsSupportedChannels(channels)));

VBAUDIO(QString("enc(%1), passthru(%2), features (%3) "
"configured_channels(%4), %5 channels supported(%6)")
"configured_channels(%4), %5 channels supported(%6) "
"max_channels(%7)")
.arg(enc)
.arg(passthru)
.arg(output_settingsdigital->FeaturesToString())
.arg(configured_channels)
.arg(channels)
.arg(output_settings->IsSupportedChannels(channels)));
.arg(output_settings->IsSupportedChannels(channels))
.arg(max_channels));

int dest_rate = 0;

Expand Down Expand Up @@ -630,7 +650,7 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
.arg(settings.samplerate/1000).arg(samplerate/1000)
.arg(quality_string(src_quality)));

int chans = needs_downmix ? channels : source_channels;
int chans = needs_downmix ? configured_channels : source_channels;

src_ctx = src_new(2-src_quality, chans, &error);
if (error)
Expand Down Expand Up @@ -748,13 +768,12 @@ void AudioOutputBase::Reconfigure(const AudioSettings &orig_settings)
volume = gCoreContext->GetNumSetting(volumeControl, 80);
}

VolumeBase::SetChannels(channels);
VolumeBase::SetChannels(configured_channels);
VolumeBase::SyncVolume();
VolumeBase::UpdateVolume();

// Upmix Stereo or 5.0 to 5.1
if (needs_upmix &&
(source_channels == 2 || source_channels == 5) &&
// Upmix Mono, Stereo or 5.0 to 5.1
if (needs_upmix && IS_VALID_UPMIX_CHANNEL(source_channels) &&
configured_channels > 2)
{
surround_mode = gCoreContext->GetNumSetting("AudioUpmixType", QUALITY_HIGH);
Expand Down Expand Up @@ -1162,7 +1181,7 @@ int AudioOutputBase::CopyWithUpmix(char *buffer, int frames, int &org_waud)
}

// Convert mono to stereo as most devices can't accept mono
if (channels == 2 && source_channels == 1)
if (configured_channels == 2 && source_channels == 1)
{
int bdFrames = bdiff / bpf;
if (bdFrames <= frames)
Expand Down Expand Up @@ -1318,7 +1337,7 @@ bool AudioOutputBase::AddData(void *in_buffer, int in_len,

// Account for changes in number of channels
if (needs_upmix || needs_downmix)
len = (len / source_channels) * channels;
len = (len / source_channels) * configured_channels;

// Check we have enough space to write the data
if (need_resampler && src_ctx)
Expand Down Expand Up @@ -1367,7 +1386,8 @@ bool AudioOutputBase::AddData(void *in_buffer, int in_len,

// Perform downmix if necessary
if (needs_downmix)
if(AudioOutputDownmix::DownmixFrames(source_channels, channels,
if(AudioOutputDownmix::DownmixFrames(source_channels,
configured_channels,
src_in, src_in, frames) < 0)
VBERROR("Error occurred while downmixing");

Expand Down Expand Up @@ -1685,10 +1705,11 @@ int AudioOutputBase::GetAudioData(uchar *buffer, int size, bool full_buffer,

// Mute individual channels through mono->stereo duplication
MuteState mute_state = GetMuteState();
if (written_size && channels > 1 &&
if (!enc && !passthru &&
written_size && configured_channels > 1 &&
(mute_state == kMuteLeft || mute_state == kMuteRight))
{
AudioOutputUtil::MuteChannel(obytes << 3, channels,
AudioOutputUtil::MuteChannel(obytes << 3, configured_channels,
mute_state == kMuteLeft ? 0 : 1,
buffer, written_size);
}
Expand Down
50 changes: 35 additions & 15 deletions mythtv/libs/libmyth/audio/audiooutputdownmix.cpp
@@ -1,7 +1,10 @@
#include "audiooutputbase.h"
#include "audiooutputdownmix.h"

#include "string.h"

#define LOC QString("Downmixer: ")

/*
SMPTE channel layout
DUAL-MONO L R
Expand Down Expand Up @@ -134,9 +137,11 @@ static const float s51_matrix[2][8][6] =
int AudioOutputDownmix::DownmixFrames(int channels_in, int channels_out,
float *dst, float *src, int frames)
{
if (channels_in <= channels_out)
if (channels_in < channels_out)
return -1;

//VBAUDIO(LOC + QString("Downmixing %1 frames (in:%2 out:%3)")
// .arg(frames).arg(channels_in).arg(channels_out));
if (channels_out == 2)
{
float tmp;
Expand All @@ -155,21 +160,36 @@ int AudioOutputDownmix::DownmixFrames(int channels_in, int channels_out,
}
else if (channels_out == 6)
{
int lensamples = channels_in - 4;
int lenbytes = lensamples * sizeof(float);
for (int n=0; n < frames; n++)
// dummy 5.1 -> 5.1 downmixer for test purposes
if (channels_in == 6)
{
int lensamples = channels_in;
int lenbytes = lensamples * sizeof(float);
for (int n=0; n < frames; n++)
{
memcpy(dst, src, lenbytes);
src += lensamples;
dst += lensamples;
}
}
else
{
memcpy(dst, src, lenbytes);
src += lensamples;
dst += 4;
//read value first, as src and dst can overlap
float ls = src[0];
float rs = src[1];
float rls = src[2];
float rrs = src[3];
*dst++ = ls * m3db + rls * m3db; // LS = LS*-3dB + Rls*-3dB
*dst++ = rs * m3db + rrs * m3db; // RS = RS*-3dB + Rrs*-3dB
src += 4;
int lensamples = channels_in - 4;
int lenbytes = lensamples * sizeof(float);
for (int n=0; n < frames; n++)
{
memcpy(dst, src, lenbytes);
src += lensamples;
dst += 4;
//read value first, as src and dst can overlap
float ls = src[0];
float rs = src[1];
float rls = src[2];
float rrs = src[3];
*dst++ = ls * m3db + rls * m3db; // LS = LS*-3dB + Rls*-3dB
*dst++ = rs * m3db + rrs * m3db; // RS = RS*-3dB + Rrs*-3dB
src += 4;
}
}
}
else
Expand Down
3 changes: 3 additions & 0 deletions mythtv/libs/libmyth/audio/audiooutputsettings.cpp
Expand Up @@ -450,6 +450,9 @@ QString AudioOutputSettings::GetPassthroughParams(int codec, int codec_profile,
log = "DTS 96/24";
break;
case FF_PROFILE_DTS_HD_HRA:
samplerate = 192000;
log = "DTS-HD High-Res";
break;
case FF_PROFILE_DTS_HD_MA:
samplerate = 192000;
if (canDTSHDMA)
Expand Down

0 comments on commit 4e5d52a

Please sign in to comment.