diff options
author | Hendrik Leppkes <h.leppkes@gmail.com> | 2012-06-30 00:31:17 +0400 |
---|---|---|
committer | Hendrik Leppkes <h.leppkes@gmail.com> | 2012-06-30 00:31:17 +0400 |
commit | 1aeae144dd8b7b4ec5b03dd6c598cd5b11478ab3 (patch) | |
tree | a0e0cd00379edcd2c06d93ee157e9f8d01199aec /decoder/LAVAudio | |
parent | badd2c36f89cad9a5014dcd6f2e9de031d97047b (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.cpp | 146 | ||||
-rw-r--r-- | decoder/LAVAudio/AudioSettingsProp.h | 36 | ||||
-rw-r--r-- | decoder/LAVAudio/LAVAudio.cpp | 84 | ||||
-rw-r--r-- | decoder/LAVAudio/LAVAudio.h | 17 | ||||
-rw-r--r-- | decoder/LAVAudio/LAVAudio.rc | 21 | ||||
-rw-r--r-- | decoder/LAVAudio/LAVAudio.vcxproj | 8 | ||||
-rw-r--r-- | decoder/LAVAudio/LAVAudioSettings.h | 13 | ||||
-rw-r--r-- | decoder/LAVAudio/Media.cpp | 23 | ||||
-rw-r--r-- | decoder/LAVAudio/Media.h | 1 | ||||
-rw-r--r-- | decoder/LAVAudio/PostProcessor.cpp | 81 | ||||
-rw-r--r-- | decoder/LAVAudio/dllmain.cpp | 6 | ||||
-rw-r--r-- | decoder/LAVAudio/resource.h | 18 | ||||
-rw-r--r-- | decoder/LAVAudio/stdafx.h | 1 |
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) |