Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mpc-hc/LAVFilters.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHendrik Leppkes <h.leppkes@gmail.com>2012-06-30 00:31:17 +0400
committerHendrik Leppkes <h.leppkes@gmail.com>2012-06-30 00:31:17 +0400
commit1aeae144dd8b7b4ec5b03dd6c598cd5b11478ab3 (patch)
treea0e0cd00379edcd2c06d93ee157e9f8d01199aec /decoder/LAVAudio
parentbadd2c36f89cad9a5014dcd6f2e9de031d97047b (diff)
Implement basic mixing functionality.
Only downmixing to a pre-defined set of speaker layouts is currently supported. If a stream with fewer channels is being played, it'll be filled with empty channels.
Diffstat (limited to 'decoder/LAVAudio')
-rw-r--r--decoder/LAVAudio/AudioSettingsProp.cpp146
-rw-r--r--decoder/LAVAudio/AudioSettingsProp.h36
-rw-r--r--decoder/LAVAudio/LAVAudio.cpp84
-rw-r--r--decoder/LAVAudio/LAVAudio.h17
-rw-r--r--decoder/LAVAudio/LAVAudio.rc21
-rw-r--r--decoder/LAVAudio/LAVAudio.vcxproj8
-rw-r--r--decoder/LAVAudio/LAVAudioSettings.h13
-rw-r--r--decoder/LAVAudio/Media.cpp23
-rw-r--r--decoder/LAVAudio/Media.h1
-rw-r--r--decoder/LAVAudio/PostProcessor.cpp81
-rw-r--r--decoder/LAVAudio/dllmain.cpp6
-rw-r--r--decoder/LAVAudio/resource.h18
-rw-r--r--decoder/LAVAudio/stdafx.h1
13 files changed, 438 insertions, 17 deletions
diff --git a/decoder/LAVAudio/AudioSettingsProp.cpp b/decoder/LAVAudio/AudioSettingsProp.cpp
index d2b1450e..9861b99d 100644
--- a/decoder/LAVAudio/AudioSettingsProp.cpp
+++ b/decoder/LAVAudio/AudioSettingsProp.cpp
@@ -325,6 +325,152 @@ INT_PTR CLAVAudioSettingsProp::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wPa
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Mixer Configurations
+
+CLAVAudioMixingProp::CLAVAudioMixingProp(LPUNKNOWN pUnk, HRESULT* phr)
+ : CBaseDSPropPage(NAME("LAVCAudioMixing"), pUnk, IDD_PROPPAGE_AUDIO_MIXING, IDS_MIXER), m_pAudioSettings(NULL)
+{
+}
+
+
+CLAVAudioMixingProp::~CLAVAudioMixingProp()
+{
+}
+
+HRESULT CLAVAudioMixingProp::OnConnect(IUnknown *pUnk)
+{
+ if (pUnk == NULL)
+ {
+ return E_POINTER;
+ }
+ ASSERT(m_pAudioSettings == NULL);
+ return pUnk->QueryInterface(&m_pAudioSettings);
+}
+
+HRESULT CLAVAudioMixingProp::OnDisconnect()
+{
+ SafeRelease(&m_pAudioSettings);
+ return S_OK;
+}
+
+static DWORD dwSpkLayouts[] = {
+ AV_CH_LAYOUT_STEREO,
+ AV_CH_LAYOUT_2_2,
+ AV_CH_LAYOUT_5POINT1_BACK,
+ AV_CH_LAYOUT_6POINT1,
+ AV_CH_LAYOUT_7POINT1,
+};
+static DWORD get_speaker_index(DWORD dwLayout) {
+ int i = 0;
+ for(i = 0; i < countof(dwSpkLayouts); i++) {
+ if (dwSpkLayouts[i] == dwLayout)
+ return i;
+ }
+ return (DWORD)-1;
+}
+
+HRESULT CLAVAudioMixingProp::OnApplyChanges()
+{
+ ASSERT(m_pAudioSettings != NULL);
+ HRESULT hr = S_OK;
+ DWORD dwVal = 0;
+ BOOL bVal = FALSE;
+
+ dwVal = (DWORD)SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_GETCURSEL, 0, 0);
+ m_pAudioSettings->SetMixingLayout(dwSpkLayouts[dwVal]);
+
+ bVal = (BOOL)SendDlgItemMessage(m_Dlg, IDC_MIXING, BM_GETCHECK, 0, 0);
+ m_pAudioSettings->SetMixingEnabled(bVal);
+
+ DWORD dwMixingFlags = 0;
+ bVal = (BOOL)SendDlgItemMessage(m_Dlg, IDC_UNTOUCHED_STEREO, BM_GETCHECK, 0, 0);
+ if (bVal) dwMixingFlags |= LAV_MIXING_FLAG_UNTOUCHED_STEREO;
+
+ m_pAudioSettings->SetMixingFlags(dwMixingFlags);
+
+ LoadData();
+
+ return hr;
+}
+
+HRESULT CLAVAudioMixingProp::OnActivate()
+{
+ HRESULT hr = S_OK;
+ INITCOMMONCONTROLSEX icc;
+ icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icc.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
+ if (InitCommonControlsEx(&icc) == FALSE)
+ {
+ return E_FAIL;
+ }
+ ASSERT(m_pAudioSettings != NULL);
+
+ WCHAR spkStereo[] = L"Stereo";
+ WCHAR spkQuadro[] = L"4.0";
+ WCHAR spk51Surround[] = L"5.1";
+ WCHAR spk61Surround[] = L"6.1";
+ WCHAR spk71Surround[] = L"7.1";
+
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_RESETCONTENT, 0, 0);
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)spkStereo);
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)spkQuadro);
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)spk51Surround);
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)spk61Surround);
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)spk71Surround);
+
+ hr = LoadData();
+ if (SUCCEEDED(hr)) {
+ SendDlgItemMessage(m_Dlg, IDC_MIXING, BM_SETCHECK, m_bMixing, 0);
+ SendDlgItemMessage(m_Dlg, IDC_OUTPUT_SPEAKERS, CB_SETCURSEL, get_speaker_index(m_dwSpeakerLayout), 0);
+
+ SendDlgItemMessage(m_Dlg, IDC_UNTOUCHED_STEREO, BM_SETCHECK, !!(m_dwFlags & LAV_MIXING_FLAG_UNTOUCHED_STEREO), 0);
+ }
+
+ return hr;
+}
+
+HRESULT CLAVAudioMixingProp::LoadData()
+{
+ HRESULT hr = S_OK;
+
+ m_dwSpeakerLayout = m_pAudioSettings->GetMixingLayout();
+ m_bMixing = m_pAudioSettings->GetMixingEnabled();
+ m_dwFlags = m_pAudioSettings->GetMixingFlags();
+
+ return hr;
+}
+
+INT_PTR CLAVAudioMixingProp::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lValue;
+ switch (uMsg)
+ {
+ case WM_COMMAND:
+ if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_OUTPUT_SPEAKERS) {
+ lValue = SendDlgItemMessage(m_Dlg, LOWORD(wParam), CB_GETCURSEL, 0, 0);
+ if (lValue != m_dwSpeakerLayout) {
+ SetDirty();
+ }
+ } else if (LOWORD(wParam) == IDC_MIXING && HIWORD(wParam) == BN_CLICKED) {
+ lValue = SendDlgItemMessage(m_Dlg, LOWORD(wParam), BM_GETCHECK, 0, 0);
+ if (lValue != m_bMixing) {
+ SetDirty();
+ }
+ } else if (LOWORD(wParam) == IDC_UNTOUCHED_STEREO && HIWORD(wParam) == BN_CLICKED) {
+ lValue = SendDlgItemMessage(m_Dlg, LOWORD(wParam), BM_GETCHECK, 0, 0);
+ if (lValue == !(m_dwFlags & LAV_MIXING_FLAG_UNTOUCHED_STEREO)) {
+ SetDirty();
+ }
+ }
+ break;
+ case WM_HSCROLL:
+ break;
+ }
+ // Let the parent class handle the message.
+ return __super::OnReceiveMessage(hwnd, uMsg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
// Format Configurations
CLAVAudioFormatsProp::CLAVAudioFormatsProp(LPUNKNOWN pUnk, HRESULT* phr)
diff --git a/decoder/LAVAudio/AudioSettingsProp.h b/decoder/LAVAudio/AudioSettingsProp.h
index 60c005fd..7e01e3ab 100644
--- a/decoder/LAVAudio/AudioSettingsProp.h
+++ b/decoder/LAVAudio/AudioSettingsProp.h
@@ -27,6 +27,10 @@
DEFINE_GUID(CLSID_LAVAudioSettingsProp,
0x2d8f1801, 0xa70d, 0x48f4, 0xb7, 0x6b, 0x7f, 0x5a, 0xe0, 0x22, 0xab, 0x54);
+// {C89FC33C-E60A-4C97-BEF4-ACC5762B6404}
+DEFINE_GUID(CLSID_LAVAudioMixingProp,
+0xc89fc33c, 0xe60a, 0x4c97, 0xbe, 0xf4, 0xac, 0xc5, 0x76, 0x2b, 0x64, 0x4);
+
// {20ED4A03-6AFD-4FD9-980B-2F6143AA0892}
DEFINE_GUID(CLSID_LAVAudioStatusProp,
0x20ed4a03, 0x6afd, 0x4fd9, 0x98, 0xb, 0x2f, 0x61, 0x43, 0xaa, 0x8, 0x92);
@@ -76,6 +80,38 @@ private:
int m_iAudioDelay;
};
+class CLAVAudioMixingProp : public CBaseDSPropPage
+{
+public:
+ CLAVAudioMixingProp(LPUNKNOWN pUnk, HRESULT* phr);
+ ~CLAVAudioMixingProp();
+
+ HRESULT OnActivate();
+ HRESULT OnConnect(IUnknown *pUnk);
+ HRESULT OnDisconnect();
+ HRESULT OnApplyChanges();
+ INT_PTR OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+private:
+ HRESULT LoadData();
+
+ void SetDirty()
+ {
+ m_bDirty = TRUE;
+ if (m_pPageSite)
+ {
+ m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
+ }
+ }
+
+private:
+ ILAVAudioSettings *m_pAudioSettings;
+
+ BOOL m_bMixing;
+ DWORD m_dwSpeakerLayout;
+ DWORD m_dwFlags;
+};
+
class CLAVAudioFormatsProp : public CBaseDSPropPage
{
public:
diff --git a/decoder/LAVAudio/LAVAudio.cpp b/decoder/LAVAudio/LAVAudio.cpp
index a3c3b0ef..833f0bcb 100644
--- a/decoder/LAVAudio/LAVAudio.cpp
+++ b/decoder/LAVAudio/LAVAudio.cpp
@@ -83,6 +83,8 @@ CLAVAudio::CLAVAudio(LPUNKNOWN pUnk, HRESULT* phr)
, m_bNeedSyncpoint(FALSE)
, m_dRate(1.0)
, m_bInputPadded(FALSE)
+ , m_avrContext(NULL)
+ , m_bAVResampleFailed(FALSE)
{
#ifdef DEBUG
DbgSetModuleLevel (LOG_CUSTOM1, DWORD_MAX); // FFMPEG messages use custom1
@@ -189,6 +191,10 @@ HRESULT CLAVAudio::LoadDefaults()
m_settings.AudioDelayEnabled = FALSE;
m_settings.AudioDelay = 0;
+ m_settings.MixingEnabled = FALSE;
+ m_settings.MixingLayout = AV_CH_LAYOUT_STEREO;
+ m_settings.MixingFlags = 0;
+
return S_OK;
}
@@ -251,6 +257,15 @@ HRESULT CLAVAudio::LoadSettings()
bFlag = reg.ReadBOOL(L"OutputStandardLayout", hr);
if (SUCCEEDED(hr)) m_settings.OutputStandardLayout = bFlag;
+ bFlag = reg.ReadBOOL(L"Mixing", hr);
+ if (SUCCEEDED(hr)) m_settings.MixingEnabled = bFlag;
+
+ dwVal = reg.ReadDWORD(L"MixingLayout", hr);
+ if (SUCCEEDED(hr)) m_settings.MixingLayout = dwVal;
+
+ dwVal = reg.ReadDWORD(L"MixingFlags", hr);
+ if (SUCCEEDED(hr)) m_settings.MixingFlags = dwVal;
+
// Deprecated sample format storage
pBuf = reg.ReadBinary(L"SampleFormats", dwVal, hr);
if (SUCCEEDED(hr)) {
@@ -307,6 +322,10 @@ HRESULT CLAVAudio::SaveSettings()
reg.WriteBOOL(L"AudioDelayEnabled", m_settings.AudioDelayEnabled);
reg.WriteDWORD(L"AudioDelay", m_settings.AudioDelay);
+ reg.WriteBOOL(L"Mixing", m_settings.MixingEnabled);
+ reg.WriteDWORD(L"MixingLayout", m_settings.MixingLayout);
+ reg.WriteDWORD(L"MixingFlags", m_settings.MixingFlags);
+
reg.DeleteKey(L"Formats");
CRegistry regF = CRegistry(HKEY_CURRENT_USER, LAVC_AUDIO_REGISTRY_KEY_FORMATS, hr);
for (int i = 0; i < Codec_NB; ++i) {
@@ -346,6 +365,11 @@ void CLAVAudio::ffmpeg_shutdown()
m_pParser = NULL;
}
+ if (m_avrContext) {
+ avresample_close(m_avrContext);
+ avresample_free(&m_avrContext);
+ }
+
FreeBitstreamContext();
FreeDTSDecoder();
@@ -373,15 +397,16 @@ STDMETHODIMP CLAVAudio::GetPages(CAUUID *pPages)
{
CheckPointer(pPages, E_POINTER);
BOOL bShowStatusPage = m_pInput && m_pInput->IsConnected();
- pPages->cElems = bShowStatusPage ? 3 : 2;
+ pPages->cElems = bShowStatusPage ? 4 : 3;
pPages->pElems = (GUID *)CoTaskMemAlloc(sizeof(GUID) * pPages->cElems);
if (pPages->pElems == NULL) {
return E_OUTOFMEMORY;
}
pPages->pElems[0] = CLSID_LAVAudioSettingsProp;
- pPages->pElems[1] = CLSID_LAVAudioFormatsProp;
+ pPages->pElems[1] = CLSID_LAVAudioMixingProp;
+ pPages->pElems[2] = CLSID_LAVAudioFormatsProp;
if (bShowStatusPage)
- pPages->pElems[2] = CLSID_LAVAudioStatusProp;
+ pPages->pElems[3] = CLSID_LAVAudioStatusProp;
return S_OK;
}
@@ -395,6 +420,8 @@ STDMETHODIMP CLAVAudio::CreatePage(const GUID& guid, IPropertyPage** ppPage)
if (guid == CLSID_LAVAudioSettingsProp)
*ppPage = new CLAVAudioSettingsProp(NULL, &hr);
+ else if (guid == CLSID_LAVAudioMixingProp)
+ *ppPage = new CLAVAudioMixingProp(NULL, &hr);
else if (guid == CLSID_LAVAudioFormatsProp)
*ppPage = new CLAVAudioFormatsProp(NULL, &hr);
else if (guid == CLSID_LAVAudioStatusProp)
@@ -601,6 +628,45 @@ STDMETHODIMP CLAVAudio::SetAudioDelay(BOOL bEnabled, int delay)
return S_OK;
}
+STDMETHODIMP CLAVAudio::SetMixingEnabled(BOOL bEnabled)
+{
+ m_settings.MixingEnabled = bEnabled;
+ SaveSettings();
+
+ return S_OK;
+}
+
+STDMETHODIMP_(BOOL) CLAVAudio::GetMixingEnabled()
+{
+ return m_settings.MixingEnabled;
+}
+
+STDMETHODIMP CLAVAudio::SetMixingLayout(DWORD dwLayout)
+{
+ m_settings.MixingLayout = dwLayout;
+ SaveSettings();
+
+ return S_OK;
+}
+
+STDMETHODIMP_(DWORD) CLAVAudio::GetMixingLayout()
+{
+ return m_settings.MixingLayout;
+}
+
+STDMETHODIMP CLAVAudio::SetMixingFlags(DWORD dwFlags)
+{
+ m_settings.MixingFlags = dwFlags;
+ SaveSettings();
+
+ return S_OK;
+}
+
+STDMETHODIMP_(DWORD) CLAVAudio::GetMixingFlags()
+{
+ return m_settings.MixingFlags;
+}
+
// ILAVAudioStatus
BOOL CLAVAudio::IsSampleFormatSupported(LAVAudioSampleFormat sfCheck)
{
@@ -1651,6 +1717,7 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
// Channel re-mapping and sample format conversion
if (got_frame && m_pFrame->nb_samples > 0) {
const DWORD idx_start = out.bBuffer->GetCount();
+ const DWORD allocated = out.bBuffer->GetAllocated();
out.wChannels = m_pAVCtx->channels;
out.dwSamplesPerSec = m_pAVCtx->sample_rate;
@@ -1661,27 +1728,33 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
out.nSamples = m_pFrame->nb_samples;
DWORD dwPCMSize = out.nSamples * out.wChannels * av_get_bytes_per_sample(m_pAVCtx->sample_fmt);
+ DWORD dwPCMSizeAligned = FFALIGN(out.nSamples, 32) * out.wChannels * av_get_bytes_per_sample(m_pAVCtx->sample_fmt);
switch (m_pAVCtx->sample_fmt) {
case AV_SAMPLE_FMT_U8:
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->Append(m_pFrame->data[0], dwPCMSize);
out.sfFormat = SampleFormat_U8;
break;
case AV_SAMPLE_FMT_S16:
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->Append(m_pFrame->data[0], dwPCMSize);
out.sfFormat = SampleFormat_16;
break;
case AV_SAMPLE_FMT_S32:
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->Append(m_pFrame->data[0], dwPCMSize);
out.sfFormat = SampleFormat_32;
out.wBitsPerSample = m_pAVCtx->bits_per_raw_sample;
break;
case AV_SAMPLE_FMT_FLT:
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->Append(m_pFrame->data[0], dwPCMSize);
out.sfFormat = SampleFormat_FP32;
break;
case AV_SAMPLE_FMT_DBL:
{
+ out.bBuffer->Allocate(allocated + (dwPCMSizeAligned / 2));
out.bBuffer->SetSize(idx_start + (dwPCMSize / 2));
float *pDataOut = (float *)(out.bBuffer->Ptr() + idx_start);
@@ -1697,6 +1770,7 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
// Planar Formats
case AV_SAMPLE_FMT_U8P:
{
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->SetSize(idx_start + dwPCMSize);
uint8_t *pOut = (uint8_t *)(out.bBuffer->Ptr() + idx_start);
@@ -1710,6 +1784,7 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
break;
case AV_SAMPLE_FMT_S16P:
{
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->SetSize(idx_start + dwPCMSize);
int16_t *pOut = (int16_t *)(out.bBuffer->Ptr() + idx_start);
@@ -1723,6 +1798,7 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
break;
case AV_SAMPLE_FMT_S32P:
{
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->SetSize(idx_start + dwPCMSize);
int32_t *pOut = (int32_t *)(out.bBuffer->Ptr() + idx_start);
@@ -1737,6 +1813,7 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
break;
case AV_SAMPLE_FMT_FLTP:
{
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->SetSize(idx_start + dwPCMSize);
float *pOut = (float *)(out.bBuffer->Ptr() + idx_start);
@@ -1750,6 +1827,7 @@ HRESULT CLAVAudio::Decode(const BYTE * const buffer, int buffsize, int &consumed
break;
case AV_SAMPLE_FMT_DBLP:
{
+ out.bBuffer->Allocate(allocated + dwPCMSizeAligned);
out.bBuffer->SetSize(idx_start + (dwPCMSize / 2));
float *pOut = (float *)(out.bBuffer->Ptr() + idx_start);
diff --git a/decoder/LAVAudio/LAVAudio.h b/decoder/LAVAudio/LAVAudio.h
index fa150be2..aeb4de8b 100644
--- a/decoder/LAVAudio/LAVAudio.h
+++ b/decoder/LAVAudio/LAVAudio.h
@@ -132,6 +132,12 @@ public:
STDMETHODIMP SetSampleFormat(LAVAudioSampleFormat format, BOOL bEnabled);
STDMETHODIMP GetAudioDelay(BOOL *pbEnabled, int *pDelay);
STDMETHODIMP SetAudioDelay(BOOL bEnabled, int delay);
+ STDMETHODIMP SetMixingEnabled(BOOL bEnabled);
+ STDMETHODIMP_(BOOL) GetMixingEnabled();
+ STDMETHODIMP SetMixingLayout(DWORD dwLayout);
+ STDMETHODIMP_(DWORD) GetMixingLayout();
+ STDMETHODIMP SetMixingFlags(DWORD dwFlags);
+ STDMETHODIMP_(DWORD) GetMixingFlags();
// ILAVAudioStatus
STDMETHODIMP_(BOOL) IsSampleFormatSupported(LAVAudioSampleFormat sfCheck);
@@ -224,6 +230,8 @@ private:
HRESULT ConvertSampleFormat(BufferDetails *pcm, LAVAudioSampleFormat outputFormat);
HRESULT Truncate32Buffer(BufferDetails *buffer);
+ HRESULT PerformMixing(BufferDetails *buffer);
+
private:
CodecID m_nCodecId; // FFMPEG Codec Id
AVCodec *m_pAVCodec; // AVCodec reference
@@ -256,6 +264,11 @@ private:
BOOL m_bInputPadded;
+ AVAudioResampleContext *m_avrContext;
+ LAVAudioSampleFormat m_sfRemixFormat;
+ DWORD m_dwRemixLayout;
+ BOOL m_bAVResampleFailed;
+
// Settings
struct AudioSettings {
BOOL DRCEnabled;
@@ -271,6 +284,10 @@ private:
BOOL bSampleFormats[SampleFormat_NB];
BOOL AudioDelayEnabled;
int AudioDelay;
+
+ BOOL MixingEnabled;
+ DWORD MixingLayout;
+ DWORD MixingFlags;
} m_settings;
BOOL m_bRuntimeConfig;
diff --git a/decoder/LAVAudio/LAVAudio.rc b/decoder/LAVAudio/LAVAudio.rc
index 949bf3d6..1e6dd81d 100644
--- a/decoder/LAVAudio/LAVAudio.rc
+++ b/decoder/LAVAudio/LAVAudio.rc
@@ -105,6 +105,18 @@ BEGIN
CONTROL "",IDC_DELAYSPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | UDS_HOTTRACK,305,35,11,13
END
+IDD_PROPPAGE_AUDIO_MIXING DIALOGEX 0, 0, 369, 215
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+FONT 8, "MS Shell Dlg", 400, 0, 0x0
+BEGIN
+ GROUPBOX "Mixer",IDC_LBL_MIXING,7,7,214,67
+ CONTROL "Enable Mixing",IDC_MIXING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,19,59,10
+ LTEXT "Output Speaker Configuration:",IDC_LBL_OUTPUT_SPEAKERS,15,32,100,8
+ COMBOBOX IDC_OUTPUT_SPEAKERS,121,30,90,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Settings",IDC_MIX_SETTINGS,225,7,133,53
+ CONTROL "Don't mix Stereo sources",IDC_UNTOUCHED_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,237,19,100,10
+END
+
IDD_PROPPAGE_FORMATS DIALOGEX 0, 0, 369, 215
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x0
@@ -177,6 +189,14 @@ BEGIN
BOTTOMMARGIN, 210
END
+ IDD_PROPPAGE_AUDIO_MIXING, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 358
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 210
+ END
+
IDD_PROPPAGE_FORMATS, DIALOG
BEGIN
LEFTMARGIN, 7
@@ -206,6 +226,7 @@ BEGIN
IDS_SETTINGS "Audio Settings"
IDS_STATUS "Status"
IDS_FORMATS "Formats"
+ IDS_MIXER "Mixing"
END
#endif // English (United States) resources
diff --git a/decoder/LAVAudio/LAVAudio.vcxproj b/decoder/LAVAudio/LAVAudio.vcxproj
index 7cf850a1..8da1af88 100644
--- a/decoder/LAVAudio/LAVAudio.vcxproj
+++ b/decoder/LAVAudio/LAVAudio.vcxproj
@@ -92,7 +92,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbasd.lib;dsutild.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib</AdditionalDependencies>
+ <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbasd.lib;dsutild.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib;avresample-lav.lib</AdditionalDependencies>
<ModuleDefinitionFile>LAVAudio.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
@@ -103,7 +103,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbasd.lib;dsutild.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib</AdditionalDependencies>
+ <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbasd.lib;dsutild.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib;avresample-lav.lib</AdditionalDependencies>
<ModuleDefinitionFile>LAVAudio.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
@@ -116,7 +116,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbase.lib;dsutil.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib</AdditionalDependencies>
+ <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbase.lib;dsutil.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib;avresample-lav.lib</AdditionalDependencies>
<ModuleDefinitionFile>LAVAudio.def</ModuleDefinitionFile>
</Link>
<CustomBuildStep>
@@ -134,7 +134,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbase.lib;dsutil.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib</AdditionalDependencies>
+ <AdditionalDependencies>advapi32.lib;ole32.lib;winmm.lib;user32.lib;oleaut32.lib;shell32.lib;Shlwapi.lib;Comctl32.lib;strmbase.lib;dsutil.lib;avformat-lav.lib;avutil-lav.lib;avcodec-lav.lib;avresample-lav.lib</AdditionalDependencies>
<ModuleDefinitionFile>LAVAudio.def</ModuleDefinitionFile>
</Link>
<CustomBuildStep>
diff --git a/decoder/LAVAudio/LAVAudioSettings.h b/decoder/LAVAudio/LAVAudioSettings.h
index 221fd7eb..6c1dbf75 100644
--- a/decoder/LAVAudio/LAVAudioSettings.h
+++ b/decoder/LAVAudio/LAVAudioSettings.h
@@ -138,6 +138,19 @@ interface ILAVAudioSettings : public IUnknown
// Configure a delay for the audio
STDMETHOD(GetAudioDelay)(BOOL *pbEnabled, int *pDelay) = 0;
STDMETHOD(SetAudioDelay)(BOOL bEnabled, int delay) = 0;
+
+ // Enable/Disable Mixing
+ STDMETHOD(SetMixingEnabled)(BOOL bEnabled) = 0;
+ STDMETHOD_(BOOL,GetMixingEnabled)() = 0;
+
+ // Control Mixing Layout
+ STDMETHOD(SetMixingLayout)(DWORD dwLayout) = 0;
+ STDMETHOD_(DWORD,GetMixingLayout)() = 0;
+
+#define LAV_MIXING_FLAG_UNTOUCHED_STEREO 0x0001
+ // Enable/Disable Mixing
+ STDMETHOD(SetMixingFlags)(DWORD dwFlags) = 0;
+ STDMETHOD_(DWORD,GetMixingFlags)() = 0;
};
// LAV Audio Status Interface
diff --git a/decoder/LAVAudio/Media.cpp b/decoder/LAVAudio/Media.cpp
index 960b7d0e..59e59f14 100644
--- a/decoder/LAVAudio/Media.cpp
+++ b/decoder/LAVAudio/Media.cpp
@@ -370,6 +370,29 @@ LAVAudioSampleFormat get_lav_sample_fmt(AVSampleFormat sample_fmt, int bits)
return lav_sample_fmt;
}
+AVSampleFormat get_ff_sample_fmt(LAVAudioSampleFormat sample_fmt)
+{
+ AVSampleFormat ff_sample_fmt;
+ switch(sample_fmt) {
+ case SampleFormat_16:
+ ff_sample_fmt = AV_SAMPLE_FMT_S16;
+ break;
+ case SampleFormat_32:
+ ff_sample_fmt = AV_SAMPLE_FMT_S32;
+ break;
+ case SampleFormat_FP32:
+ ff_sample_fmt = AV_SAMPLE_FMT_FLT;
+ break;
+ case SampleFormat_U8:
+ ff_sample_fmt = AV_SAMPLE_FMT_U8;
+ break;
+ case SampleFormat_24:
+ assert(0);
+ break;
+ }
+ return ff_sample_fmt;
+}
+
static BYTE get_lpcm_sample_rate_index(int sample_rate)
{
switch(sample_rate) {
diff --git a/decoder/LAVAudio/Media.h b/decoder/LAVAudio/Media.h
index 92988ae9..0fcdc28f 100644
--- a/decoder/LAVAudio/Media.h
+++ b/decoder/LAVAudio/Media.h
@@ -57,6 +57,7 @@ const char *get_sample_format_desc(CMediaType &mt);
BYTE get_byte_per_sample(LAVAudioSampleFormat sfFormat);
LAVAudioSampleFormat get_lav_sample_fmt(AVSampleFormat sample_fmt, int bits = 0);
+AVSampleFormat get_ff_sample_fmt(LAVAudioSampleFormat sample_fmt);
WORD get_channel_from_flag(DWORD dwMask, DWORD dwFlag);
DWORD get_flag_from_channel(DWORD dwMask, WORD wChannel);
diff --git a/decoder/LAVAudio/PostProcessor.cpp b/decoder/LAVAudio/PostProcessor.cpp
index aa6b8141..8345e3e6 100644
--- a/decoder/LAVAudio/PostProcessor.cpp
+++ b/decoder/LAVAudio/PostProcessor.cpp
@@ -547,13 +547,71 @@ HRESULT CLAVAudio::Truncate32Buffer(BufferDetails *buffer)
return S_OK;
}
-HRESULT CLAVAudio::PostProcess(BufferDetails *buffer)
+HRESULT CLAVAudio::PerformMixing(BufferDetails *buffer)
{
- LAVAudioSampleFormat outputFormat = GetBestAvailableSampleFormat(buffer->sfFormat);
- if (outputFormat != buffer->sfFormat) {
- ConvertSampleFormat(buffer, outputFormat);
+ // Check if we need mixing, either already in target mask or in stereo (no downmixing from stereo)
+ if (buffer->dwChannelMask == m_settings.MixingLayout || (buffer->wChannels <= 2 && (m_settings.MixingFlags & LAV_MIXING_FLAG_UNTOUCHED_STEREO)))
+ return S_FALSE;
+
+ // Sadly, we need to convert this, avresample has no 24-bit mode
+ if (buffer->sfFormat == SampleFormat_24) {
+ ConvertSampleFormat(buffer, SampleFormat_32);
}
+ if (buffer->dwChannelMask != m_DecodeLayoutSanified || (!m_avrContext && !m_bAVResampleFailed) || m_settings.MixingLayout != m_dwRemixLayout) {
+ m_bAVResampleFailed = FALSE;
+ if (m_avrContext) {
+ avresample_close(m_avrContext);
+ avresample_free(&m_avrContext);
+ }
+
+ m_DecodeLayoutSanified = buffer->dwChannelMask;
+ // We prefer to mix to FP32, so try to use that
+ m_sfRemixFormat = GetBestAvailableSampleFormat(SampleFormat_FP32);
+ // avresample has no 24-bit mode
+ if (m_sfRemixFormat == SampleFormat_24)
+ m_sfRemixFormat = SampleFormat_32;
+
+ m_avrContext = avresample_alloc_context();
+ av_opt_set_int(m_avrContext, "in_channel_layout", buffer->dwChannelMask, 0);
+ av_opt_set_int(m_avrContext, "in_sample_fmt", get_ff_sample_fmt(buffer->sfFormat), 0);
+
+ av_opt_set_int(m_avrContext, "out_channel_layout", m_settings.MixingLayout, 0);
+ av_opt_set_int(m_avrContext, "out_sample_fmt", get_ff_sample_fmt(m_sfRemixFormat), 0);
+
+ int ret = avresample_open(m_avrContext);
+ if (ret < 0) {
+ DbgLog((LOG_ERROR, 10, L"avresample_open failed, layout in: %x, out: %x, sample fmt in: %d, out: %d", buffer->dwChannelMask, m_settings.MixingLayout, buffer->sfFormat, m_sfRemixFormat));
+ avresample_free(&m_avrContext);
+ m_bAVResampleFailed = TRUE;
+ }
+
+ m_dwRemixLayout = m_settings.MixingLayout;
+ }
+
+ if (!m_avrContext)
+ return S_FALSE;
+
+ GrowableArray<BYTE> *pcmOut = new GrowableArray<BYTE>();
+ pcmOut->Allocate(FFALIGN(buffer->nSamples, 32) * av_get_channel_layout_nb_channels(m_dwRemixLayout) * get_byte_per_sample(m_sfRemixFormat));
+ BYTE *pOut = pcmOut->Ptr();
+
+ BYTE *pIn = buffer->bBuffer->Ptr();
+ avresample_convert(m_avrContext, (void **)&pOut, pcmOut->GetAllocated(), buffer->nSamples, (void **)&pIn, buffer->bBuffer->GetAllocated(), buffer->nSamples);
+
+ delete buffer->bBuffer;
+ buffer->bBuffer = pcmOut;
+ buffer->dwChannelMask = m_dwRemixLayout;
+ buffer->sfFormat = m_sfRemixFormat;
+ buffer->wBitsPerSample = get_byte_per_sample(m_sfRemixFormat) << 3;
+ buffer->wChannels = av_get_channel_layout_nb_channels(m_dwRemixLayout);
+ buffer->bBuffer->SetSize(buffer->wChannels * buffer->nSamples * get_byte_per_sample(m_sfRemixFormat));
+
+ return S_OK;
+}
+
+HRESULT CLAVAudio::PostProcess(BufferDetails *buffer)
+{
buffer->dwChannelMask = sanitize_mask(buffer->dwChannelMask, m_nCodecId);
int layout_channels = av_get_channel_layout_nb_channels(buffer->dwChannelMask);
@@ -563,8 +621,11 @@ HRESULT CLAVAudio::PostProcess(BufferDetails *buffer)
buffer->dwChannelMask = get_channel_mask(buffer->wChannels);
}
+ if (m_settings.MixingEnabled) {
+ PerformMixing(buffer);
+ }
// Remap to standard configurations, if requested
- if (m_settings.OutputStandardLayout) {
+ else if (m_settings.OutputStandardLayout) {
if (buffer->dwChannelMask != m_DecodeLayoutSanified) {
m_DecodeLayoutSanified = buffer->dwChannelMask;
CheckChannelLayoutConformity(buffer->dwChannelMask);
@@ -593,8 +654,16 @@ HRESULT CLAVAudio::PostProcess(BufferDetails *buffer)
UpdateVolumeStats(*buffer);
}
- if (buffer->sfFormat == SampleFormat_32 && buffer->wBitsPerSample && buffer->wBitsPerSample <= 24) {
+ // Truncate 24-in-32 to real 24
+ if (buffer->sfFormat == SampleFormat_32 && GetSampleFormat(SampleFormat_24) && buffer->wBitsPerSample && buffer->wBitsPerSample <= 24) {
Truncate32Buffer(buffer);
}
+
+ // Convert Sample format, if necessary
+ LAVAudioSampleFormat outputFormat = GetBestAvailableSampleFormat(buffer->sfFormat);
+ if (outputFormat != buffer->sfFormat) {
+ ConvertSampleFormat(buffer, outputFormat);
+ }
+
return S_OK;
}
diff --git a/decoder/LAVAudio/dllmain.cpp b/decoder/LAVAudio/dllmain.cpp
index 73c5fe26..107b69b7 100644
--- a/decoder/LAVAudio/dllmain.cpp
+++ b/decoder/LAVAudio/dllmain.cpp
@@ -76,6 +76,12 @@ CFactoryTemplate g_Templates[] = {
NULL, NULL
},
{
+ L"LAV Audio Mixer",
+ &CLSID_LAVAudioMixingProp,
+ CreateInstance<CLAVAudioMixingProp>,
+ NULL, NULL
+ },
+ {
L"LAV Audio Format Settings",
&CLSID_LAVAudioFormatsProp,
CreateInstance<CLAVAudioFormatsProp>,
diff --git a/decoder/LAVAudio/resource.h b/decoder/LAVAudio/resource.h
index 41fbc4ab..6bbf9149 100644
--- a/decoder/LAVAudio/resource.h
+++ b/decoder/LAVAudio/resource.h
@@ -3,12 +3,14 @@
// Used by LAVAudio.rc
//
#define IDD_PROPPAGE_AUDIO_SETTINGS 9
-#define IDD_PROPPAGE_FORMATS 10
-#define IDD_PROPPAGE_STATUS 11
+#define IDD_PROPPAGE_AUDIO_MIXING 10
+#define IDD_PROPPAGE_FORMATS 11
+#define IDD_PROPPAGE_STATUS 12
#define IDS_SETTINGS 101
#define IDS_STATUS 102
#define IDS_FORMATS 103
#define IDI_ICON1 104
+#define IDS_MIXER 105
#define IDC_INPUT_STATUS 1013
#define IDC_SUP_SAMPLE_FORMATS 1020
#define IDC_INT16 1021
@@ -85,14 +87,22 @@
#define IDC_DELAY 1101
#define IDC_DELAYSPIN 1102
#define IDC_LBL_FORMATS 1103
+#define IDC_MIXING 1104
+#define IDC_MIXER 1105
+#define IDC_DOWNMIX 1106
+#define IDC_OUTPUT_SPEAKERS 1108
+#define IDC_UNTOUCHED_STEREO 1109
+#define IDC_MIX_SETTINGS 1110
+#define IDC_LBL_OUTPUT_SPEAKERS 1111
+#define IDC_LBL_MIXING 1112
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 105
+#define _APS_NEXT_RESOURCE_VALUE 106
#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1104
+#define _APS_NEXT_CONTROL_VALUE 1113
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/decoder/LAVAudio/stdafx.h b/decoder/LAVAudio/stdafx.h
index afcd70bc..ad2472d8 100644
--- a/decoder/LAVAudio/stdafx.h
+++ b/decoder/LAVAudio/stdafx.h
@@ -44,6 +44,7 @@ extern "C" {
#include "libavcodec/avcodec.h"
#include "libavutil/opt.h"
#include "libavutil/intreadwrite.h"
+#include "libavresample/avresample.h"
}
#pragma warning(pop)