├── .editorconfig ├── .gitignore ├── .gitmodules ├── IGuidedReclock.h ├── LICENSE ├── README.md ├── TODO.txt ├── changelog.txt ├── dll ├── base.props ├── platform.props ├── sanear-dll.sln ├── sanear-platform.props ├── sanear.props ├── skel │ ├── register.bat │ └── unregister.bat └── src │ ├── baseclasses.vcxproj │ ├── baseclasses │ ├── amextra.cpp │ ├── amextra.h │ ├── amfilter.cpp │ ├── amfilter.h │ ├── amvideo.cpp │ ├── arithutil.cpp │ ├── cache.h │ ├── checkbmi.h │ ├── combase.cpp │ ├── combase.h │ ├── cprop.cpp │ ├── cprop.h │ ├── ctlutil.cpp │ ├── ctlutil.h │ ├── ddmm.cpp │ ├── ddmm.h │ ├── dllentry.cpp │ ├── dllsetup.cpp │ ├── dllsetup.h │ ├── dxmperf.h │ ├── fourcc.h │ ├── measure.h │ ├── msgthrd.h │ ├── mtype.cpp │ ├── mtype.h │ ├── outputq.cpp │ ├── outputq.h │ ├── perflog.cpp │ ├── perflog.h │ ├── perfstruct.h │ ├── pstream.cpp │ ├── pstream.h │ ├── pullpin.cpp │ ├── pullpin.h │ ├── refclock.cpp │ ├── refclock.h │ ├── reftime.h │ ├── renbase.cpp │ ├── renbase.h │ ├── schedule.cpp │ ├── schedule.h │ ├── seekpt.cpp │ ├── seekpt.h │ ├── source.cpp │ ├── source.h │ ├── streams.cpp │ ├── streams.h │ ├── strmctl.cpp │ ├── strmctl.h │ ├── sysclock.cpp │ ├── sysclock.h │ ├── transfrm.cpp │ ├── transfrm.h │ ├── transip.cpp │ ├── transip.h │ ├── videoctl.cpp │ ├── videoctl.h │ ├── vtrans.cpp │ ├── vtrans.h │ ├── winctrl.cpp │ ├── winctrl.h │ ├── winutil.cpp │ ├── winutil.h │ ├── wxdebug.cpp │ ├── wxdebug.h │ ├── wxlist.cpp │ ├── wxlist.h │ ├── wxutil.cpp │ └── wxutil.h │ ├── bs2b.vcxproj │ ├── fftw-config │ └── config.h │ ├── fftw.vcxproj │ ├── fftw.vcxproj.filters │ ├── rubberband.vcxproj │ ├── rubberband.vcxproj.filters │ ├── sanear-dll.vcxproj │ ├── sanear-dll.vcxproj.filters │ ├── sanear-dll │ ├── Entry.cpp │ ├── OuterFilter.cpp │ ├── OuterFilter.h │ ├── RegistryKey.cpp │ ├── RegistryKey.h │ ├── TrayWindow.cpp │ ├── TrayWindow.h │ ├── pch.cpp │ ├── pch.h │ ├── resource.h │ ├── sanear.def │ ├── sanear.ico │ ├── sanear.rc │ └── sanear.svg │ ├── snprintf.h │ ├── soundtouch.vcxproj │ ├── soxr-config.h │ └── soxr.vcxproj ├── sanear.vcxproj ├── sanear.vcxproj.filters └── src ├── AudioDevice.h ├── AudioDeviceEvent.cpp ├── AudioDeviceEvent.h ├── AudioDeviceManager.cpp ├── AudioDeviceManager.h ├── AudioDevicePush.cpp ├── AudioDevicePush.h ├── AudioRenderer.cpp ├── AudioRenderer.h ├── DspBalance.cpp ├── DspBalance.h ├── DspBase.h ├── DspChunk.cpp ├── DspChunk.h ├── DspCrossfeed.cpp ├── DspCrossfeed.h ├── DspDither.cpp ├── DspDither.h ├── DspFormat.h ├── DspLimiter.cpp ├── DspLimiter.h ├── DspMatrix.cpp ├── DspMatrix.h ├── DspRate.cpp ├── DspRate.h ├── DspTempo.cpp ├── DspTempo.h ├── DspTempo2.cpp ├── DspTempo2.h ├── DspVolume.cpp ├── DspVolume.h ├── Factory.cpp ├── Factory.h ├── Interfaces.h ├── MyBasicAudio.cpp ├── MyBasicAudio.h ├── MyClock.cpp ├── MyClock.h ├── MyFilter.cpp ├── MyFilter.h ├── MyPin.cpp ├── MyPin.h ├── MyPropertyPage.cpp ├── MyPropertyPage.h ├── MyTestClock.cpp ├── MyTestClock.h ├── SampleCorrection.cpp ├── SampleCorrection.h ├── Settings.cpp ├── Settings.h ├── Utils.h ├── pch.cpp └── pch.h /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{cpp,h}] 4 | end_of_line = crlf 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dll/bin 2 | /dll/ipch 3 | *.sdf 4 | *.opensdf 5 | *.suo 6 | *.vcxproj.user 7 | *.aps 8 | *.vspx 9 | *.opendb 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbs2b"] 2 | path = dll/src/libbs2b 3 | url = git://github.com/alexmarsev/libbs2b.git 4 | [submodule "soxr"] 5 | path = dll/src/soxr 6 | url = git://github.com/alexmarsev/soxr.git 7 | [submodule "soundtouch"] 8 | path = dll/src/soundtouch 9 | url = git://github.com/alexmarsev/soundtouch.git 10 | [submodule "rubberband"] 11 | path = dll/src/rubberband 12 | url = git://github.com/breakfastquay/rubberband.git 13 | [submodule "fftw"] 14 | path = dll/src/fftw 15 | url = git://github.com/alexmarsev/fftw3.git 16 | branch = codelets-3.3.4 17 | -------------------------------------------------------------------------------- /IGuidedReclock.h: -------------------------------------------------------------------------------- 1 | // This file is released under CC0 1.0 license 2 | // License text can be found at http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | // Originally designed as part of sanear project 5 | 6 | #pragma once 7 | 8 | struct __declspec(uuid("243F1282-94C3-46A1-B3F6-72B400786FEC")) 9 | IGuidedReclock : IUnknown 10 | { 11 | STDMETHOD(SlaveClock)(DOUBLE multiplier) = 0; 12 | STDMETHOD(UnslaveClock)() = 0; 13 | 14 | STDMETHOD(OffsetClock)(LONGLONG offset) = 0; 15 | 16 | STDMETHOD(GetImmediateTime)(LONGLONG* pTime) = 0; 17 | }; 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Sanear 2 | 3 | ### What is it? 4 | Open-source DirectShow audio renderer. With WASAPI support and more. 5 | 6 | ### Binary releases 7 | Can be found here https://github.com/alexmarsev/sanear/releases 8 | 9 | ### Compilation instructions 10 | If you want to partake in the development or simply feeling adventurous: 11 | 12 | 1. Have recent `Visual Studio` installed (free versions like `Visual Studio Community 2013 Update 5` or `Visual Studio Community 2015 Update 1` work just fine) 13 | 2. Check out `master` branch of sanear 14 | 3. Ensure that all submodules are up-to-date by running `git submodule update --init --recursive` from inside the tree 15 | 4. Open `sanear-dll.sln` solution file and build 16 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | - design and implement "guided reclock" interface 2 | - don't recreate the device when media type changes during playback and the old one can be used 3 | - override advise portion of IReferenceClock interface 4 | - add "excessive precision processing" option 5 | - play silence during pause in exclusive mode (for ati hdmi) 6 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | Sanear, a robust DirectShow audio renderer 2 | ========================================== 3 | 4 | v0.3 - 2015/08/08 5 | * Bugfixing release 6 | * Improves status page compatibility with certain players 7 | 8 | v0.2 - 2015/08/02 9 | * Bugfixing release 10 | * Fixes audible noise in dithering 11 | 12 | v0.1 - 2015/07/22 13 | * Initial release 14 | * Outputs sound through WASAPI (shared or exclusive) 15 | * Employs automatic channel downmixing (as per standard) 16 | * Provides stereo crossfeed processing option (for headphones) 17 | * Tries to preserve signal pitch when playing at custom rate (time stretching) 18 | * Uses audio device clock as clock source 19 | * Comes in 32-bit and 64-bit versions 20 | * Supports bitstreaming 21 | -------------------------------------------------------------------------------- /dll/base.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\obj\ 5 | $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\ 6 | $(SolutionDir)bin\$(Configuration)$(PlatformArchitecture)\obj\$(ProjectName)\ 7 | 8 | 9 | 10 | true 11 | 12 | 13 | 14 | 15 | MultiThreadedDebug 16 | 17 | 18 | 19 | 20 | MultiThreaded 21 | NDEBUG;%(PreprocessorDefinitions) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /dll/platform.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | v140 5 | 6 | 7 | v120 8 | 9 | 10 | -------------------------------------------------------------------------------- /dll/sanear-platform.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /dll/sanear.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SANEAR_GPL_PHASE_VOCODER;%(PreprocessorDefinitions) 7 | $(SolutionDir)src\baseclasses;$(SolutionDir)src\soxr\src;$(SolutionDir)src\libbs2b\src;$(SolutionDir)src\soundtouch\include;$(SolutionDir)src\zita-resampler\libs;$(SolutionDir)src\rubberband\rubberband;%(AdditionalIncludeDirectories) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /dll/skel/register.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd /d "%~dp0" 3 | 4 | regsvr32.exe sanear.ax /s 5 | if %ERRORLEVEL% neq 0 goto fail 6 | 7 | if "%PROCESSOR_ARCHITECTURE%" == "x86" goto ok 8 | regsvr32.exe sanear64.ax /s 9 | if %ERRORLEVEL% neq 0 goto fail 10 | 11 | :ok 12 | echo. 13 | echo Registration succeeded 14 | echo. 15 | goto done 16 | 17 | :fail 18 | echo. 19 | echo Registration failed! 20 | echo. 21 | echo Try to right-click on %~nx0 and select "Run as administrator" 22 | echo. 23 | 24 | :done 25 | pause >nul 26 | -------------------------------------------------------------------------------- /dll/skel/unregister.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd /d "%~dp0" 3 | 4 | regsvr32.exe /u sanear.ax /s 5 | if %ERRORLEVEL% neq 0 goto fail 6 | 7 | if "%PROCESSOR_ARCHITECTURE%" == "x86" goto ok 8 | regsvr32.exe /u sanear64.ax /s 9 | if %ERRORLEVEL% neq 0 goto fail 10 | 11 | :ok 12 | echo. 13 | echo Unregistration succeeded 14 | echo. 15 | goto done 16 | 17 | :fail 18 | echo. 19 | echo Unregistration failed! 20 | echo. 21 | echo Try to right-click on %~nx0 and select "Run as administrator" 22 | echo. 23 | 24 | :done 25 | pause >nul 26 | -------------------------------------------------------------------------------- /dll/src/baseclasses/amextra.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: AMExtra.cpp 3 | // 4 | // Desc: DirectShow base classes - implements CRenderedInputPin class. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #include // DirectShow base class definitions 11 | #include // Needed for definition of timeGetTime 12 | #include // Standard data type limit definitions 13 | #include // Used for time critical log functions 14 | 15 | #include "amextra.h" 16 | 17 | #pragma warning(disable:4355) 18 | 19 | // Implements CRenderedInputPin class 20 | 21 | CRenderedInputPin::CRenderedInputPin(__in_opt LPCTSTR pObjectName, 22 | __in CBaseFilter *pFilter, 23 | __in CCritSec *pLock, 24 | __inout HRESULT *phr, 25 | __in_opt LPCWSTR pName) : 26 | CBaseInputPin(pObjectName, pFilter, pLock, phr, pName), 27 | m_bAtEndOfStream(FALSE), 28 | m_bCompleteNotified(FALSE) 29 | { 30 | } 31 | #ifdef UNICODE 32 | CRenderedInputPin::CRenderedInputPin(__in_opt LPCSTR pObjectName, 33 | __in CBaseFilter *pFilter, 34 | __in CCritSec *pLock, 35 | __inout HRESULT *phr, 36 | __in_opt LPCWSTR pName) : 37 | CBaseInputPin(pObjectName, pFilter, pLock, phr, pName), 38 | m_bAtEndOfStream(FALSE), 39 | m_bCompleteNotified(FALSE) 40 | { 41 | } 42 | #endif 43 | 44 | // Flush end of stream condition - caller should do any 45 | // necessary stream level locking before calling this 46 | 47 | STDMETHODIMP CRenderedInputPin::EndOfStream() 48 | { 49 | HRESULT hr = CheckStreaming(); 50 | 51 | // Do EC_COMPLETE handling for rendered pins 52 | if (S_OK == hr && !m_bAtEndOfStream) { 53 | m_bAtEndOfStream = TRUE; 54 | FILTER_STATE fs; 55 | EXECUTE_ASSERT(SUCCEEDED(m_pFilter->GetState(0, &fs))); 56 | if (fs == State_Running) { 57 | DoCompleteHandling(); 58 | } 59 | } 60 | return hr; 61 | } 62 | 63 | 64 | // Called to complete the flush 65 | 66 | STDMETHODIMP CRenderedInputPin::EndFlush() 67 | { 68 | CAutoLock lck(m_pLock); 69 | 70 | // Clean up renderer state 71 | m_bAtEndOfStream = FALSE; 72 | m_bCompleteNotified = FALSE; 73 | 74 | return CBaseInputPin::EndFlush(); 75 | } 76 | 77 | 78 | // Notify of Run() from filter 79 | 80 | HRESULT CRenderedInputPin::Run(REFERENCE_TIME tStart) 81 | { 82 | UNREFERENCED_PARAMETER(tStart); 83 | m_bCompleteNotified = FALSE; 84 | if (m_bAtEndOfStream) { 85 | DoCompleteHandling(); 86 | } 87 | return S_OK; 88 | } 89 | 90 | 91 | // Clear status on going into paused state 92 | 93 | HRESULT CRenderedInputPin::Active() 94 | { 95 | m_bAtEndOfStream = FALSE; 96 | m_bCompleteNotified = FALSE; 97 | return CBaseInputPin::Active(); 98 | } 99 | 100 | 101 | // Do stuff to deliver end of stream 102 | 103 | void CRenderedInputPin::DoCompleteHandling() 104 | { 105 | ASSERT(m_bAtEndOfStream); 106 | if (!m_bCompleteNotified) { 107 | m_bCompleteNotified = TRUE; 108 | m_pFilter->NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)m_pFilter); 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /dll/src/baseclasses/amextra.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: AMExtra.h 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #ifndef __AMEXTRA__ 11 | #define __AMEXTRA__ 12 | 13 | // Simple rendered input pin 14 | // 15 | // NOTE if your filter queues stuff before rendering then it may not be 16 | // appropriate to use this class 17 | // 18 | // In that case queue the end of stream condition until the last sample 19 | // is actually rendered and flush the condition appropriately 20 | 21 | class CRenderedInputPin : public CBaseInputPin 22 | { 23 | public: 24 | 25 | CRenderedInputPin(__in_opt LPCTSTR pObjectName, 26 | __in CBaseFilter *pFilter, 27 | __in CCritSec *pLock, 28 | __inout HRESULT *phr, 29 | __in_opt LPCWSTR pName); 30 | #ifdef UNICODE 31 | CRenderedInputPin(__in_opt LPCSTR pObjectName, 32 | __in CBaseFilter *pFilter, 33 | __in CCritSec *pLock, 34 | __inout HRESULT *phr, 35 | __in_opt LPCWSTR pName); 36 | #endif 37 | 38 | // Override methods to track end of stream state 39 | STDMETHODIMP EndOfStream(); 40 | STDMETHODIMP EndFlush(); 41 | 42 | HRESULT Active(); 43 | HRESULT Run(REFERENCE_TIME tStart); 44 | 45 | protected: 46 | 47 | // Member variables to track state 48 | BOOL m_bAtEndOfStream; // Set by EndOfStream 49 | BOOL m_bCompleteNotified; // Set when we notify for EC_COMPLETE 50 | 51 | private: 52 | void DoCompleteHandling(); 53 | }; 54 | 55 | #endif // __AMEXTRA__ 56 | 57 | -------------------------------------------------------------------------------- /dll/src/baseclasses/cache.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: Cache.h 3 | // 4 | // Desc: DirectShow base classes - efines a non-MFC generic cache class. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | /* This class implements a simple cache. A cache object is instantiated 11 | with the number of items it is to hold. An item is a pointer to an 12 | object derived from CBaseObject (helps reduce memory leaks). The cache 13 | can then have objects added to it and removed from it. The cache size 14 | is fixed at construction time and may therefore run out or be flooded. 15 | If it runs out it returns a NULL pointer, if it fills up it also returns 16 | a NULL pointer instead of a pointer to the object just inserted */ 17 | 18 | /* Making these classes inherit from CBaseObject does nothing for their 19 | functionality but it allows us to check there are no memory leaks */ 20 | 21 | /* WARNING Be very careful when using this class, what it lets you do is 22 | store and retrieve objects so that you can minimise object creation 23 | which in turns improves efficiency. However the object you store is 24 | exactly the same as the object you get back which means that it short 25 | circuits the constructor initialisation phase. This means any class 26 | variables the object has (eg pointers) are highly likely to be invalid. 27 | Therefore ensure you reinitialise the object before using it again */ 28 | 29 | 30 | #ifndef __CACHE__ 31 | #define __CACHE__ 32 | 33 | 34 | class CCache : CBaseObject { 35 | 36 | /* Make copy constructor and assignment operator inaccessible */ 37 | 38 | CCache(const CCache &refCache); 39 | CCache &operator=(const CCache &refCache); 40 | 41 | private: 42 | 43 | /* These are initialised in the constructor. The first variable points to 44 | an array of pointers, each of which points to a CBaseObject derived 45 | object. The m_iCacheSize is the static fixed size for the cache and the 46 | m_iUsed defines the number of places filled with objects at any time. 47 | We fill the array of pointers from the start (ie m_ppObjects[0] first) 48 | and then only add and remove objects from the end position, so in this 49 | respect the array of object pointers should be treated as a stack */ 50 | 51 | CBaseObject **m_ppObjects; 52 | const INT m_iCacheSize; 53 | INT m_iUsed; 54 | 55 | public: 56 | 57 | CCache(__in_opt LPCTSTR pName,INT iItems); 58 | virtual ~CCache(); 59 | 60 | /* Add an item to the cache */ 61 | CBaseObject *AddToCache(__in CBaseObject *pObject); 62 | 63 | /* Remove an item from the cache */ 64 | CBaseObject *RemoveFromCache(); 65 | 66 | /* Delete all the objects held in the cache */ 67 | void RemoveAll(void); 68 | 69 | /* Return the cache size which is set during construction */ 70 | INT GetCacheSize(void) const {return m_iCacheSize;}; 71 | }; 72 | 73 | #endif /* __CACHE__ */ 74 | 75 | -------------------------------------------------------------------------------- /dll/src/baseclasses/checkbmi.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved. 2 | 3 | #ifndef _CHECKBMI_H_ 4 | #define _CHECKBMI_H_ 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | // Helper 11 | __inline BOOL MultiplyCheckOverflow(DWORD a, DWORD b, __deref_out_range(==, a * b) DWORD *pab) { 12 | *pab = a * b; 13 | if ((a == 0) || (((*pab) / a) == b)) { 14 | return TRUE; 15 | } 16 | return FALSE; 17 | } 18 | 19 | 20 | // Checks if the fields in a BITMAPINFOHEADER won't generate 21 | // overlows and buffer overruns 22 | // This is not a complete check and does not guarantee code using this structure will be secure 23 | // from attack 24 | // Bugs this is guarding against: 25 | // 1. Total structure size calculation overflowing 26 | // 2. biClrUsed > 256 for 8-bit palettized content 27 | // 3. Total bitmap size in bytes overflowing 28 | // 4. biSize < size of the base structure leading to accessessing random memory 29 | // 5. Total structure size exceeding know size of data 30 | // 31 | 32 | __success(return != 0) __inline BOOL ValidateBitmapInfoHeader( 33 | const BITMAPINFOHEADER *pbmi, // pointer to structure to check 34 | __out_range(>=, sizeof(BITMAPINFOHEADER)) DWORD cbSize // size of memory block containing structure 35 | ) 36 | { 37 | DWORD dwWidthInBytes; 38 | DWORD dwBpp; 39 | DWORD dwWidthInBits; 40 | DWORD dwHeight; 41 | DWORD dwSizeImage; 42 | DWORD dwClrUsed; 43 | 44 | // Reject bad parameters - do the size check first to avoid reading bad memory 45 | if (cbSize < sizeof(BITMAPINFOHEADER) || 46 | pbmi->biSize < sizeof(BITMAPINFOHEADER) || 47 | pbmi->biSize > 4096) { 48 | return FALSE; 49 | } 50 | 51 | // Reject 0 size 52 | if (pbmi->biWidth == 0 || pbmi->biHeight == 0) { 53 | return FALSE; 54 | } 55 | 56 | // Use bpp of 200 for validating against further overflows if not set for compressed format 57 | dwBpp = 200; 58 | 59 | if (pbmi->biBitCount > dwBpp) { 60 | return FALSE; 61 | } 62 | 63 | // Strictly speaking abs can overflow so cast explicitly to DWORD 64 | dwHeight = (DWORD)abs(pbmi->biHeight); 65 | 66 | if (!MultiplyCheckOverflow(dwBpp, (DWORD)pbmi->biWidth, &dwWidthInBits)) { 67 | return FALSE; 68 | } 69 | 70 | // Compute correct width in bytes - rounding up to 4 bytes 71 | dwWidthInBytes = (dwWidthInBits / 8 + 3) & ~3; 72 | 73 | if (!MultiplyCheckOverflow(dwWidthInBytes, dwHeight, &dwSizeImage)) { 74 | return FALSE; 75 | } 76 | 77 | // Fail if total size is 0 - this catches indivual quantities being 0 78 | // Also don't allow huge values > 1GB which might cause arithmetic 79 | // errors for users 80 | if (dwSizeImage > 0x40000000 || 81 | pbmi->biSizeImage > 0x40000000) { 82 | return FALSE; 83 | } 84 | 85 | // Fail if biClrUsed looks bad 86 | if (pbmi->biClrUsed > 256) { 87 | return FALSE; 88 | } 89 | 90 | if (pbmi->biClrUsed == 0 && pbmi->biBitCount <= 8 && pbmi->biBitCount > 0) { 91 | dwClrUsed = (1 << pbmi->biBitCount); 92 | } else { 93 | dwClrUsed = pbmi->biClrUsed; 94 | } 95 | 96 | // Check total size 97 | if (cbSize < pbmi->biSize + dwClrUsed * sizeof(RGBQUAD) + 98 | (pbmi->biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : 0)) { 99 | return FALSE; 100 | } 101 | 102 | // If it is RGB validate biSizeImage - lots of code assumes the size is correct 103 | if (pbmi->biCompression == BI_RGB || pbmi->biCompression == BI_BITFIELDS) { 104 | if (pbmi->biSizeImage != 0) { 105 | DWORD dwBits = (DWORD)pbmi->biWidth * (DWORD)pbmi->biBitCount; 106 | DWORD dwWidthInBytes = ((DWORD)((dwBits+31) & (~31)) / 8); 107 | DWORD dwTotalSize = (DWORD)abs(pbmi->biHeight) * dwWidthInBytes; 108 | if (dwTotalSize > pbmi->biSizeImage) { 109 | return FALSE; 110 | } 111 | } 112 | } 113 | return TRUE; 114 | } 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | #endif // _CHECKBMI_H_ 121 | -------------------------------------------------------------------------------- /dll/src/baseclasses/cprop.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: CProp.h 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #ifndef __CPROP__ 11 | #define __CPROP__ 12 | 13 | // Base property page class. Filters typically expose custom properties by 14 | // implementing special control interfaces, examples are IDirectDrawVideo 15 | // and IQualProp on renderers. This allows property pages to be built that 16 | // use the given interface. Applications such as the ActiveMovie OCX query 17 | // filters for the property pages they support and expose them to the user 18 | // 19 | // This class provides all the framework for a property page. A property 20 | // page is a COM object that supports IPropertyPage. We should be created 21 | // with a resource ID for the dialog which we will load when required. We 22 | // should also be given in the constructor a resource ID for a title string 23 | // we will load from the DLLs STRINGTABLE. The property page titles must be 24 | // stored in resource files so that they can be easily internationalised 25 | // 26 | // We have a number of virtual methods (not PURE) that may be overriden in 27 | // derived classes to query for interfaces and so on. These functions have 28 | // simple implementations here that just return NOERROR. Derived classes 29 | // will almost definately have to override the message handler method called 30 | // OnReceiveMessage. We have a static dialog procedure that calls the method 31 | // so that derived classes don't have to fiddle around with the this pointer 32 | 33 | class AM_NOVTABLE CBasePropertyPage : public IPropertyPage, public CUnknown 34 | { 35 | protected: 36 | 37 | LPPROPERTYPAGESITE m_pPageSite; // Details for our property site 38 | HWND m_hwnd; // Window handle for the page 39 | HWND m_Dlg; // Actual dialog window handle 40 | BOOL m_bDirty; // Has anything been changed 41 | int m_TitleId; // Resource identifier for title 42 | int m_DialogId; // Dialog resource identifier 43 | 44 | static INT_PTR CALLBACK DialogProc(HWND hwnd, 45 | UINT uMsg, 46 | WPARAM wParam, 47 | LPARAM lParam); 48 | 49 | private: 50 | BOOL m_bObjectSet ; // SetObject has been called or not. 51 | public: 52 | 53 | CBasePropertyPage(__in_opt LPCTSTR pName, // Debug only name 54 | __inout_opt LPUNKNOWN pUnk, // COM Delegator 55 | int DialogId, // Resource ID 56 | int TitleId); // To get tital 57 | 58 | #ifdef UNICODE 59 | CBasePropertyPage(__in_opt LPCSTR pName, 60 | __inout_opt LPUNKNOWN pUnk, 61 | int DialogId, 62 | int TitleId); 63 | #endif 64 | virtual ~CBasePropertyPage() { }; 65 | DECLARE_IUNKNOWN 66 | 67 | // Override these virtual methods 68 | 69 | virtual HRESULT OnConnect(IUnknown *pUnknown) { return NOERROR; }; 70 | virtual HRESULT OnDisconnect() { return NOERROR; }; 71 | virtual HRESULT OnActivate() { return NOERROR; }; 72 | virtual HRESULT OnDeactivate() { return NOERROR; }; 73 | virtual HRESULT OnApplyChanges() { return NOERROR; }; 74 | virtual INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); 75 | 76 | // These implement an IPropertyPage interface 77 | 78 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv); 79 | STDMETHODIMP_(ULONG) NonDelegatingRelease(); 80 | STDMETHODIMP_(ULONG) NonDelegatingAddRef(); 81 | STDMETHODIMP SetPageSite(__in_opt LPPROPERTYPAGESITE pPageSite); 82 | STDMETHODIMP Activate(HWND hwndParent, LPCRECT prect,BOOL fModal); 83 | STDMETHODIMP Deactivate(void); 84 | STDMETHODIMP GetPageInfo(__out LPPROPPAGEINFO pPageInfo); 85 | STDMETHODIMP SetObjects(ULONG cObjects, __in_ecount_opt(cObjects) LPUNKNOWN *ppUnk); 86 | STDMETHODIMP Show(UINT nCmdShow); 87 | STDMETHODIMP Move(LPCRECT prect); 88 | STDMETHODIMP IsPageDirty(void) { return m_bDirty ? S_OK : S_FALSE; } 89 | STDMETHODIMP Apply(void); 90 | STDMETHODIMP Help(LPCWSTR lpszHelpDir) { return E_NOTIMPL; } 91 | STDMETHODIMP TranslateAccelerator(__inout LPMSG lpMsg) { return E_NOTIMPL; } 92 | }; 93 | 94 | #endif // __CPROP__ 95 | 96 | -------------------------------------------------------------------------------- /dll/src/baseclasses/ddmm.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: DDMM.cpp 3 | // 4 | // Desc: DirectShow base classes - implements routines for using DirectDraw 5 | // on a multimonitor system. 6 | // 7 | // Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | #include 12 | #include 13 | #include "ddmm.h" 14 | 15 | /* 16 | * FindDeviceCallback 17 | */ 18 | typedef struct { 19 | LPSTR szDevice; 20 | GUID* lpGUID; 21 | GUID GUID; 22 | BOOL fFound; 23 | } FindDeviceData; 24 | 25 | BOOL CALLBACK FindDeviceCallback(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam) 26 | { 27 | FindDeviceData *p = (FindDeviceData*)lParam; 28 | 29 | if (lstrcmpiA(p->szDevice, szDevice) == 0) { 30 | if (lpGUID) { 31 | p->GUID = *lpGUID; 32 | p->lpGUID = &p->GUID; 33 | } else { 34 | p->lpGUID = NULL; 35 | } 36 | p->fFound = TRUE; 37 | return FALSE; 38 | } 39 | return TRUE; 40 | } 41 | 42 | 43 | BOOL CALLBACK FindDeviceCallbackEx(__in_opt GUID* lpGUID, __in LPSTR szName, __in LPSTR szDevice, __in LPVOID lParam, HMONITOR hMonitor) 44 | { 45 | FindDeviceData *p = (FindDeviceData*)lParam; 46 | 47 | if (lstrcmpiA(p->szDevice, szDevice) == 0) { 48 | if (lpGUID) { 49 | p->GUID = *lpGUID; 50 | p->lpGUID = &p->GUID; 51 | } else { 52 | p->lpGUID = NULL; 53 | } 54 | p->fFound = TRUE; 55 | return FALSE; 56 | } 57 | return TRUE; 58 | } 59 | 60 | 61 | /* 62 | * DirectDrawCreateFromDevice 63 | * 64 | * create a DirectDraw object for a particular device 65 | */ 66 | IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP) 67 | { 68 | IDirectDraw* pdd = NULL; 69 | FindDeviceData find; 70 | 71 | if (szDevice == NULL) { 72 | DirectDrawCreateP(NULL, &pdd, NULL); 73 | return pdd; 74 | } 75 | 76 | find.szDevice = szDevice; 77 | find.fFound = FALSE; 78 | DirectDrawEnumerateP(FindDeviceCallback, (LPVOID)&find); 79 | 80 | if (find.fFound) 81 | { 82 | // 83 | // In 4bpp mode the following DDraw call causes a message box to be popped 84 | // up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we 85 | // make sure it doesn't happen. 86 | // 87 | UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); 88 | DirectDrawCreateP(find.lpGUID, &pdd, NULL); 89 | SetErrorMode(ErrorMode); 90 | } 91 | 92 | return pdd; 93 | } 94 | 95 | 96 | /* 97 | * DirectDrawCreateFromDeviceEx 98 | * 99 | * create a DirectDraw object for a particular device 100 | */ 101 | IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP) 102 | { 103 | IDirectDraw* pdd = NULL; 104 | FindDeviceData find; 105 | 106 | if (szDevice == NULL) { 107 | DirectDrawCreateP(NULL, &pdd, NULL); 108 | return pdd; 109 | } 110 | 111 | find.szDevice = szDevice; 112 | find.fFound = FALSE; 113 | DirectDrawEnumerateExP(FindDeviceCallbackEx, (LPVOID)&find, 114 | DDENUM_ATTACHEDSECONDARYDEVICES); 115 | 116 | if (find.fFound) 117 | { 118 | // 119 | // In 4bpp mode the following DDraw call causes a message box to be popped 120 | // up by DDraw (!?!). It's DDraw's fault, but we don't like it. So we 121 | // make sure it doesn't happen. 122 | // 123 | UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); 124 | DirectDrawCreateP(find.lpGUID, &pdd, NULL); 125 | SetErrorMode(ErrorMode); 126 | } 127 | 128 | return pdd; 129 | } 130 | -------------------------------------------------------------------------------- /dll/src/baseclasses/ddmm.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: DDMM.h 3 | // 4 | // Desc: DirectShow base classes - efines routines for using DirectDraw 5 | // on a multimonitor system. 6 | // 7 | // Copyright (c) 1995-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | #ifdef __cplusplus 12 | extern "C" { /* Assume C declarations for C++ */ 13 | #endif /* __cplusplus */ 14 | 15 | // DDRAW.H might not include these 16 | #ifndef DDENUM_ATTACHEDSECONDARYDEVICES 17 | #define DDENUM_ATTACHEDSECONDARYDEVICES 0x00000001L 18 | #endif 19 | 20 | typedef HRESULT (*PDRAWCREATE)(IID *,LPDIRECTDRAW *,LPUNKNOWN); 21 | typedef HRESULT (*PDRAWENUM)(LPDDENUMCALLBACKA, LPVOID); 22 | 23 | IDirectDraw * DirectDrawCreateFromDevice(__in_opt LPSTR, PDRAWCREATE, PDRAWENUM); 24 | IDirectDraw * DirectDrawCreateFromDeviceEx(__in_opt LPSTR, PDRAWCREATE, LPDIRECTDRAWENUMERATEEXA); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif /* __cplusplus */ 29 | -------------------------------------------------------------------------------- /dll/src/baseclasses/dllsetup.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: DllSetup.h 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | // To be self registering, OLE servers must 11 | // export functions named DllRegisterServer 12 | // and DllUnregisterServer. To allow use of 13 | // custom and default implementations the 14 | // defaults are named AMovieDllRegisterServer 15 | // and AMovieDllUnregisterServer. 16 | // 17 | // To the use the default implementation you 18 | // must provide stub functions. 19 | // 20 | // i.e. STDAPI DllRegisterServer() 21 | // { 22 | // return AMovieDllRegisterServer(); 23 | // } 24 | // 25 | // STDAPI DllUnregisterServer() 26 | // { 27 | // return AMovieDllUnregisterServer(); 28 | // } 29 | // 30 | // 31 | // AMovieDllRegisterServer calls IAMovieSetup.Register(), and 32 | // AMovieDllUnregisterServer calls IAMovieSetup.Unregister(). 33 | 34 | STDAPI AMovieDllRegisterServer2( BOOL ); 35 | STDAPI AMovieDllRegisterServer(); 36 | STDAPI AMovieDllUnregisterServer(); 37 | 38 | // helper functions 39 | STDAPI EliminateSubKey( HKEY, LPCTSTR ); 40 | 41 | 42 | STDAPI 43 | AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata 44 | , IFilterMapper2 * pIFM2 45 | , BOOL bRegister ); 46 | 47 | -------------------------------------------------------------------------------- /dll/src/baseclasses/fourcc.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: FourCC.h 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | // FOURCCMap 11 | // 12 | // provides a mapping between old-style multimedia format DWORDs 13 | // and new-style GUIDs. 14 | // 15 | // A range of 4 billion GUIDs has been allocated to ensure that this 16 | // mapping can be done straightforwardly one-to-one in both directions. 17 | // 18 | // January 95 19 | 20 | 21 | #ifndef __FOURCC__ 22 | #define __FOURCC__ 23 | 24 | 25 | // Multimedia format types are marked with DWORDs built from four 8-bit 26 | // chars and known as FOURCCs. New multimedia AM_MEDIA_TYPE definitions include 27 | // a subtype GUID. In order to simplify the mapping, GUIDs in the range: 28 | // XXXXXXXX-0000-0010-8000-00AA00389B71 29 | // are reserved for FOURCCs. 30 | 31 | class FOURCCMap : public GUID 32 | { 33 | 34 | public: 35 | FOURCCMap(); 36 | FOURCCMap(DWORD Fourcc); 37 | FOURCCMap(const GUID *); 38 | 39 | 40 | DWORD GetFOURCC(void); 41 | void SetFOURCC(DWORD fourcc); 42 | void SetFOURCC(const GUID *); 43 | 44 | private: 45 | void InitGUID(); 46 | }; 47 | 48 | #define GUID_Data2 0 49 | #define GUID_Data3 0x10 50 | #define GUID_Data4_1 0xaa000080 51 | #define GUID_Data4_2 0x719b3800 52 | 53 | inline void 54 | FOURCCMap::InitGUID() { 55 | Data2 = GUID_Data2; 56 | Data3 = GUID_Data3; 57 | ((DWORD *)Data4)[0] = GUID_Data4_1; 58 | ((DWORD *)Data4)[1] = GUID_Data4_2; 59 | } 60 | 61 | inline 62 | FOURCCMap::FOURCCMap() { 63 | InitGUID(); 64 | SetFOURCC( DWORD(0)); 65 | } 66 | 67 | inline 68 | FOURCCMap::FOURCCMap(DWORD fourcc) 69 | { 70 | InitGUID(); 71 | SetFOURCC(fourcc); 72 | } 73 | 74 | inline 75 | FOURCCMap::FOURCCMap(const GUID * pGuid) 76 | { 77 | InitGUID(); 78 | SetFOURCC(pGuid); 79 | } 80 | 81 | inline void 82 | FOURCCMap::SetFOURCC(const GUID * pGuid) 83 | { 84 | FOURCCMap * p = (FOURCCMap*) pGuid; 85 | SetFOURCC(p->GetFOURCC()); 86 | } 87 | 88 | inline void 89 | FOURCCMap::SetFOURCC(DWORD fourcc) 90 | { 91 | Data1 = fourcc; 92 | } 93 | 94 | inline DWORD 95 | FOURCCMap::GetFOURCC(void) 96 | { 97 | return Data1; 98 | } 99 | 100 | #endif /* __FOURCC__ */ 101 | 102 | -------------------------------------------------------------------------------- /dll/src/baseclasses/msgthrd.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: MsgThrd.h 3 | // 4 | // Desc: DirectShow base classes - provides support for a worker thread 5 | // class to which one can asynchronously post messages. 6 | // 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | // Message class - really just a structure. 12 | // 13 | class CMsg { 14 | public: 15 | UINT uMsg; 16 | DWORD dwFlags; 17 | LPVOID lpParam; 18 | CAMEvent *pEvent; 19 | 20 | CMsg(UINT u, DWORD dw, __inout_opt LPVOID lp, __in_opt CAMEvent *pEvnt) 21 | : uMsg(u), dwFlags(dw), lpParam(lp), pEvent(pEvnt) {} 22 | 23 | CMsg() 24 | : uMsg(0), dwFlags(0L), lpParam(NULL), pEvent(NULL) {} 25 | }; 26 | 27 | // This is the actual thread class. It exports all the usual thread control 28 | // functions. The created thread is different from a normal WIN32 thread in 29 | // that it is prompted to perform particaular tasks by responding to messages 30 | // posted to its message queue. 31 | // 32 | class AM_NOVTABLE CMsgThread { 33 | private: 34 | static DWORD WINAPI DefaultThreadProc(__inout LPVOID lpParam); 35 | DWORD m_ThreadId; 36 | HANDLE m_hThread; 37 | 38 | protected: 39 | 40 | // if you want to override GetThreadMsg to block on other things 41 | // as well as this queue, you need access to this 42 | CGenericList m_ThreadQueue; 43 | CCritSec m_Lock; 44 | HANDLE m_hSem; 45 | LONG m_lWaiting; 46 | 47 | public: 48 | CMsgThread() 49 | : m_ThreadId(0), 50 | m_hThread(NULL), 51 | m_lWaiting(0), 52 | m_hSem(NULL), 53 | // make a list with a cache of 5 items 54 | m_ThreadQueue(NAME("MsgThread list"), 5) 55 | { 56 | } 57 | 58 | ~CMsgThread(); 59 | // override this if you want to block on other things as well 60 | // as the message loop 61 | void virtual GetThreadMsg(__out CMsg *msg); 62 | 63 | // override this if you want to do something on thread startup 64 | virtual void OnThreadInit() { 65 | }; 66 | 67 | BOOL CreateThread(); 68 | 69 | BOOL WaitForThreadExit(__out LPDWORD lpdwExitCode) { 70 | if (m_hThread != NULL) { 71 | WaitForSingleObject(m_hThread, INFINITE); 72 | return GetExitCodeThread(m_hThread, lpdwExitCode); 73 | } 74 | return FALSE; 75 | } 76 | 77 | DWORD ResumeThread() { 78 | return ::ResumeThread(m_hThread); 79 | } 80 | 81 | DWORD SuspendThread() { 82 | return ::SuspendThread(m_hThread); 83 | } 84 | 85 | int GetThreadPriority() { 86 | return ::GetThreadPriority(m_hThread); 87 | } 88 | 89 | BOOL SetThreadPriority(int nPriority) { 90 | return ::SetThreadPriority(m_hThread, nPriority); 91 | } 92 | 93 | HANDLE GetThreadHandle() { 94 | return m_hThread; 95 | } 96 | 97 | DWORD GetThreadId() { 98 | return m_ThreadId; 99 | } 100 | 101 | 102 | void PutThreadMsg(UINT uMsg, DWORD dwMsgFlags, 103 | __in_opt LPVOID lpMsgParam, __in_opt CAMEvent *pEvent = NULL) { 104 | CAutoLock lck(&m_Lock); 105 | CMsg* pMsg = new CMsg(uMsg, dwMsgFlags, lpMsgParam, pEvent); 106 | m_ThreadQueue.AddTail(pMsg); 107 | if (m_lWaiting != 0) { 108 | ReleaseSemaphore(m_hSem, m_lWaiting, 0); 109 | m_lWaiting = 0; 110 | } 111 | } 112 | 113 | // This is the function prototype of the function that the client 114 | // supplies. It is always called on the created thread, never on 115 | // the creator thread. 116 | // 117 | virtual LRESULT ThreadMessageProc( 118 | UINT uMsg, DWORD dwFlags, __inout_opt LPVOID lpParam, __in_opt CAMEvent *pEvent) = 0; 119 | }; 120 | 121 | -------------------------------------------------------------------------------- /dll/src/baseclasses/mtype.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: MtType.h 3 | // 4 | // Desc: DirectShow base classes - defines a class that holds and manages 5 | // media type information. 6 | // 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | #ifndef __MTYPE__ 12 | #define __MTYPE__ 13 | 14 | /* Helper class that derived pin objects can use to compare media 15 | types etc. Has same data members as the struct AM_MEDIA_TYPE defined 16 | in the streams IDL file, but also has (non-virtual) functions */ 17 | 18 | class CMediaType : public _AMMediaType { 19 | 20 | public: 21 | 22 | ~CMediaType(); 23 | CMediaType(); 24 | CMediaType(const GUID * majortype); 25 | CMediaType(const AM_MEDIA_TYPE&, __out_opt HRESULT* phr = NULL); 26 | CMediaType(const CMediaType&, __out_opt HRESULT* phr = NULL); 27 | 28 | CMediaType& operator=(const CMediaType&); 29 | CMediaType& operator=(const AM_MEDIA_TYPE&); 30 | 31 | BOOL operator == (const CMediaType&) const; 32 | BOOL operator != (const CMediaType&) const; 33 | 34 | HRESULT Set(const CMediaType& rt); 35 | HRESULT Set(const AM_MEDIA_TYPE& rt); 36 | 37 | BOOL IsValid() const; 38 | 39 | const GUID *Type() const { return &majortype;} ; 40 | void SetType(const GUID *); 41 | const GUID *Subtype() const { return &subtype;} ; 42 | void SetSubtype(const GUID *); 43 | 44 | BOOL IsFixedSize() const {return bFixedSizeSamples; }; 45 | BOOL IsTemporalCompressed() const {return bTemporalCompression; }; 46 | ULONG GetSampleSize() const; 47 | 48 | void SetSampleSize(ULONG sz); 49 | void SetVariableSize(); 50 | void SetTemporalCompression(BOOL bCompressed); 51 | 52 | // read/write pointer to format - can't change length without 53 | // calling SetFormat, AllocFormatBuffer or ReallocFormatBuffer 54 | 55 | BYTE* Format() const {return pbFormat; }; 56 | ULONG FormatLength() const { return cbFormat; }; 57 | 58 | void SetFormatType(const GUID *); 59 | const GUID *FormatType() const {return &formattype; }; 60 | BOOL SetFormat(__in_bcount(length) BYTE *pFormat, ULONG length); 61 | void ResetFormatBuffer(); 62 | BYTE* AllocFormatBuffer(ULONG length); 63 | BYTE* ReallocFormatBuffer(ULONG length); 64 | 65 | void InitMediaType(); 66 | 67 | BOOL MatchesPartial(const CMediaType* ppartial) const; 68 | BOOL IsPartiallySpecified(void) const; 69 | }; 70 | 71 | 72 | /* General purpose functions to copy and delete a task allocated AM_MEDIA_TYPE 73 | structure which is useful when using the IEnumMediaFormats interface as 74 | the implementation allocates the structures which you must later delete */ 75 | 76 | void WINAPI DeleteMediaType(__inout_opt AM_MEDIA_TYPE *pmt); 77 | AM_MEDIA_TYPE * WINAPI CreateMediaType(AM_MEDIA_TYPE const *pSrc); 78 | HRESULT WINAPI CopyMediaType(__out AM_MEDIA_TYPE *pmtTarget, const AM_MEDIA_TYPE *pmtSource); 79 | void WINAPI FreeMediaType(__inout AM_MEDIA_TYPE& mt); 80 | 81 | // Initialize a media type from a WAVEFORMATEX 82 | 83 | STDAPI CreateAudioMediaType( 84 | const WAVEFORMATEX *pwfx, 85 | __out AM_MEDIA_TYPE *pmt, 86 | BOOL bSetFormat); 87 | 88 | #endif /* __MTYPE__ */ 89 | 90 | -------------------------------------------------------------------------------- /dll/src/baseclasses/outputq.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: OutputQ.h 3 | // 4 | // Desc: DirectShow base classes - defines the COutputQueue class, which 5 | // makes a queue of samples and sends them to an output pin. The 6 | // class will optionally send the samples to the pin directly. 7 | // 8 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 9 | //------------------------------------------------------------------------------ 10 | 11 | 12 | typedef CGenericList CSampleList; 13 | 14 | class COutputQueue : public CCritSec 15 | { 16 | public: 17 | // Constructor 18 | COutputQueue(IPin *pInputPin, // Pin to send stuff to 19 | __inout HRESULT *phr, // 'Return code' 20 | BOOL bAuto = TRUE, // Ask pin if blocks 21 | BOOL bQueue = TRUE, // Send through queue (ignored if 22 | // bAuto set) 23 | LONG lBatchSize = 1, // Batch 24 | BOOL bBatchExact = FALSE,// Batch exactly to BatchSize 25 | LONG lListSize = // Likely number in the list 26 | DEFAULTCACHE, 27 | DWORD dwPriority = // Priority of thread to create 28 | THREAD_PRIORITY_NORMAL, 29 | bool bFlushingOpt = false // flushing optimization 30 | ); 31 | ~COutputQueue(); 32 | 33 | // enter flush state - discard all data 34 | void BeginFlush(); // Begin flushing samples 35 | 36 | // re-enable receives (pass this downstream) 37 | void EndFlush(); // Complete flush of samples - downstream 38 | // pin guaranteed not to block at this stage 39 | 40 | void EOS(); // Call this on End of stream 41 | 42 | void SendAnyway(); // Send batched samples anyway (if bBatchExact set) 43 | 44 | void NewSegment( 45 | REFERENCE_TIME tStart, 46 | REFERENCE_TIME tStop, 47 | double dRate); 48 | 49 | HRESULT Receive(IMediaSample *pSample); 50 | 51 | // do something with these media samples 52 | HRESULT ReceiveMultiple ( 53 | __in_ecount(nSamples) IMediaSample **pSamples, 54 | long nSamples, 55 | __out long *nSamplesProcessed); 56 | 57 | void Reset(); // Reset m_hr ready for more data 58 | 59 | // See if its idle or not 60 | BOOL IsIdle(); 61 | 62 | // give the class an event to fire after everything removed from the queue 63 | void SetPopEvent(HANDLE hEvent); 64 | 65 | protected: 66 | static DWORD WINAPI InitialThreadProc(__in LPVOID pv); 67 | DWORD ThreadProc(); 68 | BOOL IsQueued() 69 | { 70 | return m_List != NULL; 71 | }; 72 | 73 | // The critical section MUST be held when this is called 74 | void QueueSample(IMediaSample *pSample); 75 | 76 | BOOL IsSpecialSample(IMediaSample *pSample) 77 | { 78 | return (DWORD_PTR)pSample > (DWORD_PTR)(LONG_PTR)(-16); 79 | }; 80 | 81 | // Remove and Release() batched and queued samples 82 | void FreeSamples(); 83 | 84 | // Notify the thread there is something to do 85 | void NotifyThread(); 86 | 87 | 88 | protected: 89 | // Queue 'messages' 90 | #define SEND_PACKET ((IMediaSample *)(LONG_PTR)(-2)) // Send batch 91 | #define EOS_PACKET ((IMediaSample *)(LONG_PTR)(-3)) // End of stream 92 | #define RESET_PACKET ((IMediaSample *)(LONG_PTR)(-4)) // Reset m_hr 93 | #define NEW_SEGMENT ((IMediaSample *)(LONG_PTR)(-5)) // send NewSegment 94 | 95 | // new segment packet is always followed by one of these 96 | struct NewSegmentPacket { 97 | REFERENCE_TIME tStart; 98 | REFERENCE_TIME tStop; 99 | double dRate; 100 | }; 101 | 102 | // Remember input stuff 103 | IPin * const m_pPin; 104 | IMemInputPin * m_pInputPin; 105 | BOOL const m_bBatchExact; 106 | LONG const m_lBatchSize; 107 | 108 | CSampleList * m_List; 109 | HANDLE m_hSem; 110 | CAMEvent m_evFlushComplete; 111 | HANDLE m_hThread; 112 | __field_ecount_opt(m_lBatchSize) IMediaSample ** m_ppSamples; 113 | __range(0, m_lBatchSize) LONG m_nBatched; 114 | 115 | // Wait optimization 116 | LONG m_lWaiting; 117 | // Flush synchronization 118 | BOOL m_bFlushing; 119 | 120 | // flushing optimization. some downstream filters have trouble 121 | // with the queue's flushing optimization. other rely on it 122 | BOOL m_bFlushed; 123 | bool m_bFlushingOpt; 124 | 125 | // Terminate now 126 | BOOL m_bTerminate; 127 | 128 | // Send anyway flag for batching 129 | BOOL m_bSendAnyway; 130 | 131 | // Deferred 'return code' 132 | HRESULT volatile m_hr; 133 | 134 | // an event that can be fired after every deliver 135 | HANDLE m_hEventPop; 136 | }; 137 | 138 | -------------------------------------------------------------------------------- /dll/src/baseclasses/perflog.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: perflog.h 3 | // 4 | // Desc: Performance logging framework. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | typedef struct _PERFLOG_LOGGING_PARAMS { 10 | GUID ControlGuid; 11 | void (*OnStateChanged)(void); 12 | ULONG NumberOfTraceGuids; 13 | TRACE_GUID_REGISTRATION TraceGuids[ANYSIZE_ARRAY]; 14 | } PERFLOG_LOGGING_PARAMS, *PPERFLOG_LOGGING_PARAMS; 15 | 16 | BOOL 17 | PerflogInitIfEnabled( 18 | IN HINSTANCE hInstance, 19 | __in PPERFLOG_LOGGING_PARAMS LogParams 20 | ); 21 | 22 | BOOL 23 | PerflogInitialize ( 24 | __in PPERFLOG_LOGGING_PARAMS LogParams 25 | ); 26 | 27 | VOID 28 | PerflogShutdown ( 29 | VOID 30 | ); 31 | 32 | VOID 33 | PerflogTraceEvent ( 34 | __in PEVENT_TRACE_HEADER Event 35 | ); 36 | 37 | extern ULONG PerflogEnableFlags; 38 | extern UCHAR PerflogEnableLevel; 39 | extern ULONG PerflogModuleLevel; 40 | extern TRACEHANDLE PerflogTraceHandle; 41 | extern TRACEHANDLE PerflogRegHandle; 42 | 43 | #define PerflogTracingEnabled() (PerflogTraceHandle != 0) 44 | 45 | #define PerflogEvent( _x_ ) PerflogTraceEventLevel _x_ 46 | 47 | VOID 48 | PerflogTraceEventLevel( 49 | ULONG Level, 50 | __in PEVENT_TRACE_HEADER Event 51 | ); 52 | 53 | VOID 54 | PerflogTraceEvent ( 55 | __in PEVENT_TRACE_HEADER Event 56 | ); 57 | -------------------------------------------------------------------------------- /dll/src/baseclasses/pstream.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: PStream.cpp 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #include 11 | #include 12 | 13 | #ifdef PERF 14 | #include 15 | #endif 16 | // #include "pstream.h" in streams.h 17 | 18 | // 19 | // Constructor 20 | // 21 | CPersistStream::CPersistStream(IUnknown *punk, __inout HRESULT *phr) 22 | : mPS_fDirty(FALSE) 23 | { 24 | mPS_dwFileVersion = GetSoftwareVersion(); 25 | } 26 | 27 | 28 | // 29 | // Destructor 30 | // 31 | CPersistStream::~CPersistStream() { 32 | // Nothing to do 33 | } 34 | 35 | #if 0 36 | SAMPLE CODE TO COPY - not active at the moment 37 | 38 | // 39 | // NonDelegatingQueryInterface 40 | // 41 | // This object supports IPersist & IPersistStream 42 | STDMETHODIMP CPersistStream::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv) 43 | { 44 | if (riid == IID_IPersist) { 45 | return GetInterface((IPersist *) this, ppv); // ??? 46 | } 47 | else if (riid == IID_IPersistStream) { 48 | return GetInterface((IPersistStream *) this, ppv); 49 | } 50 | else { 51 | return CUnknown::NonDelegatingQueryInterface(riid, ppv); 52 | } 53 | } 54 | #endif 55 | 56 | 57 | // 58 | // WriteToStream 59 | // 60 | // Writes to the stream (default action is to write nothing) 61 | HRESULT CPersistStream::WriteToStream(IStream *pStream) 62 | { 63 | // You can override this to do things like 64 | // hr = pStream->Write(MyStructure, sizeof(MyStructure), NULL); 65 | 66 | return NOERROR; 67 | } 68 | 69 | 70 | 71 | HRESULT CPersistStream::ReadFromStream(IStream * pStream) 72 | { 73 | // You can override this to do things like 74 | // hr = pStream->Read(MyStructure, sizeof(MyStructure), NULL); 75 | 76 | return NOERROR; 77 | } 78 | 79 | 80 | // 81 | // Load 82 | // 83 | // Load all the data from the given stream 84 | STDMETHODIMP CPersistStream::Load(LPSTREAM pStm) 85 | { 86 | HRESULT hr; 87 | // Load the version number then the data 88 | mPS_dwFileVersion = ReadInt(pStm, hr); 89 | if (FAILED(hr)) { 90 | return hr; 91 | } 92 | 93 | return ReadFromStream(pStm); 94 | } // Load 95 | 96 | 97 | 98 | // 99 | // Save 100 | // 101 | // Save the contents of this Stream. 102 | STDMETHODIMP CPersistStream::Save(LPSTREAM pStm, BOOL fClearDirty) 103 | { 104 | 105 | HRESULT hr = WriteInt(pStm, GetSoftwareVersion()); 106 | if (FAILED(hr)) { 107 | return hr; 108 | } 109 | 110 | hr = WriteToStream(pStm); 111 | if (FAILED(hr)) { 112 | return hr; 113 | } 114 | 115 | mPS_fDirty = !fClearDirty; 116 | 117 | return hr; 118 | } // Save 119 | 120 | 121 | // WriteInt 122 | // 123 | // Writes an integer to an IStream as 11 UNICODE characters followed by one space. 124 | // You could use this for shorts or unsigneds or anything (up to 32 bits) 125 | // where the value isn't actually truncated by squeezing it into 32 bits. 126 | // Values such as (unsigned) 0x80000000 would come out as -2147483648 127 | // but would then load as 0x80000000 through ReadInt. Cast as you please. 128 | 129 | STDAPI WriteInt(IStream *pIStream, int n) 130 | { 131 | WCHAR Buff[13]; // Allows for trailing null that we don't write 132 | (void)StringCchPrintfW(Buff, NUMELMS(Buff),L"%011d ",n); 133 | return pIStream->Write(&(Buff[0]), 12*sizeof(WCHAR), NULL); 134 | } // WriteInt 135 | 136 | 137 | // ReadInt 138 | // 139 | // Reads an integer from an IStream. 140 | // Read as 4 bytes. You could use this for shorts or unsigneds or anything 141 | // where the value isn't actually truncated by squeezing it into 32 bits 142 | // Striped down subset of what sscanf can do (without dragging in the C runtime) 143 | 144 | STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr) 145 | { 146 | 147 | int Sign = 1; 148 | unsigned int n = 0; // result wil be n*Sign 149 | WCHAR wch; 150 | 151 | hr = pIStream->Read( &wch, sizeof(wch), NULL); 152 | if (FAILED(hr)) { 153 | return 0; 154 | } 155 | 156 | if (wch==L'-'){ 157 | Sign = -1; 158 | hr = pIStream->Read( &wch, sizeof(wch), NULL); 159 | if (FAILED(hr)) { 160 | return 0; 161 | } 162 | } 163 | 164 | for( ; ; ) { 165 | if (wch>=L'0' && wch<=L'9') { 166 | n = 10*n+(int)(wch-L'0'); 167 | } else if ( wch == L' ' 168 | || wch == L'\t' 169 | || wch == L'\r' 170 | || wch == L'\n' 171 | || wch == L'\0' 172 | ) { 173 | break; 174 | } else { 175 | hr = VFW_E_INVALID_FILE_FORMAT; 176 | return 0; 177 | } 178 | 179 | hr = pIStream->Read( &wch, sizeof(wch), NULL); 180 | if (FAILED(hr)) { 181 | return 0; 182 | } 183 | } 184 | 185 | if (n==0x80000000 && Sign==-1) { 186 | // This is the negative number that has no positive version! 187 | return (int)n; 188 | } 189 | else return (int)n * Sign; 190 | } // ReadInt 191 | 192 | 193 | // The microsoft C/C++ compile generates level 4 warnings to the effect that 194 | // a particular inline function (from some base class) was not needed. 195 | // This line gets rid of hundreds of such unwanted messages and makes 196 | // -W4 compilation feasible: 197 | #pragma warning(disable: 4514) 198 | -------------------------------------------------------------------------------- /dll/src/baseclasses/pstream.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: PStream.h 3 | // 4 | // Desc: DirectShow base classes - defines a class for persistent properties 5 | // of filters. 6 | // 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | #ifndef __PSTREAM__ 12 | #define __PSTREAM__ 13 | 14 | // Base class for persistent properties of filters 15 | // (i.e. filter properties in saved graphs) 16 | 17 | // The simplest way to use this is: 18 | // 1. Arrange for your filter to inherit this class 19 | // 2. Implement in your class WriteToStream and ReadFromStream 20 | // These will override the "do nothing" functions here. 21 | // 3. Change your NonDelegatingQueryInterface to handle IPersistStream 22 | // 4. Implement SizeMax to return the number of bytes of data you save. 23 | // If you save UNICODE data, don't forget a char is 2 bytes. 24 | // 5. Whenever your data changes, call SetDirty() 25 | // 26 | // At some point you may decide to alter, or extend the format of your data. 27 | // At that point you will wish that you had a version number in all the old 28 | // saved graphs, so that you can tell, when you read them, whether they 29 | // represent the old or new form. To assist you in this, this class 30 | // writes and reads a version number. 31 | // When it writes, it calls GetSoftwareVersion() to enquire what version 32 | // of the software we have at the moment. (In effect this is a version number 33 | // of the data layout in the file). It writes this as the first thing in the data. 34 | // If you want to change the version, implement (override) GetSoftwareVersion(). 35 | // It reads this from the file into mPS_dwFileVersion before calling ReadFromStream, 36 | // so in ReadFromStream you can check mPS_dwFileVersion to see if you are reading 37 | // an old version file. 38 | // Normally you should accept files whose version is no newer than the software 39 | // version that's reading them. 40 | 41 | 42 | // CPersistStream 43 | // 44 | // Implements IPersistStream. 45 | // See 'OLE Programmers Reference (Vol 1):Structured Storage Overview' for 46 | // more implementation information. 47 | class CPersistStream : public IPersistStream { 48 | private: 49 | 50 | // Internal state: 51 | 52 | protected: 53 | DWORD mPS_dwFileVersion; // version number of file (being read) 54 | BOOL mPS_fDirty; 55 | 56 | public: 57 | 58 | // IPersistStream methods 59 | 60 | STDMETHODIMP IsDirty() 61 | {return (mPS_fDirty ? S_OK : S_FALSE);} // note FALSE means clean 62 | STDMETHODIMP Load(LPSTREAM pStm); 63 | STDMETHODIMP Save(LPSTREAM pStm, BOOL fClearDirty); 64 | STDMETHODIMP GetSizeMax(__out ULARGE_INTEGER * pcbSize) 65 | // Allow 24 bytes for version. 66 | { pcbSize->QuadPart = 12*sizeof(WCHAR)+SizeMax(); return NOERROR; } 67 | 68 | // implementation 69 | 70 | CPersistStream(IUnknown *punk, __inout HRESULT *phr); 71 | ~CPersistStream(); 72 | 73 | HRESULT SetDirty(BOOL fDirty) 74 | { mPS_fDirty = fDirty; return NOERROR;} 75 | 76 | 77 | // override to reveal IPersist & IPersistStream 78 | // STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv); 79 | 80 | // --- IPersist --- 81 | 82 | // You must override this to provide your own class id 83 | STDMETHODIMP GetClassID(__out CLSID *pClsid) PURE; 84 | 85 | // overrideable if you want 86 | // file version number. Override it if you ever change format 87 | virtual DWORD GetSoftwareVersion(void) { return 0; } 88 | 89 | 90 | //========================================================================= 91 | // OVERRIDE THESE to read and write your data 92 | // OVERRIDE THESE to read and write your data 93 | // OVERRIDE THESE to read and write your data 94 | 95 | virtual int SizeMax() {return 0;} 96 | virtual HRESULT WriteToStream(IStream *pStream); 97 | virtual HRESULT ReadFromStream(IStream *pStream); 98 | //========================================================================= 99 | 100 | private: 101 | 102 | }; 103 | 104 | 105 | // --- Useful helpers --- 106 | 107 | 108 | // Writes an int to an IStream as UNICODE. 109 | STDAPI WriteInt(IStream *pIStream, int n); 110 | 111 | // inverse of WriteInt 112 | STDAPI_(int) ReadInt(IStream *pIStream, __out HRESULT &hr); 113 | 114 | #endif // __PSTREAM__ 115 | -------------------------------------------------------------------------------- /dll/src/baseclasses/pullpin.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: PullPin.h 3 | // 4 | // Desc: DirectShow base classes - defines CPullPin class. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #ifndef __PULLPIN_H__ 11 | #define __PULLPIN_H__ 12 | 13 | // 14 | // CPullPin 15 | // 16 | // object supporting pulling data from an IAsyncReader interface. 17 | // Given a start/stop position, calls a pure Receive method with each 18 | // IMediaSample received. 19 | // 20 | // This is essentially for use in a MemInputPin when it finds itself 21 | // connected to an IAsyncReader pin instead of a pushing pin. 22 | // 23 | 24 | class CPullPin : public CAMThread 25 | { 26 | IAsyncReader* m_pReader; 27 | REFERENCE_TIME m_tStart; 28 | REFERENCE_TIME m_tStop; 29 | REFERENCE_TIME m_tDuration; 30 | BOOL m_bSync; 31 | 32 | enum ThreadMsg { 33 | TM_Pause, // stop pulling and wait for next message 34 | TM_Start, // start pulling 35 | TM_Exit, // stop and exit 36 | }; 37 | 38 | ThreadMsg m_State; 39 | 40 | // override pure thread proc from CAMThread 41 | DWORD ThreadProc(void); 42 | 43 | // running pull method (check m_bSync) 44 | void Process(void); 45 | 46 | // clean up any cancelled i/o after a flush 47 | void CleanupCancelled(void); 48 | 49 | // suspend thread from pulling, eg during seek 50 | HRESULT PauseThread(); 51 | 52 | // start thread pulling - create thread if necy 53 | HRESULT StartThread(); 54 | 55 | // stop and close thread 56 | HRESULT StopThread(); 57 | 58 | // called from ProcessAsync to queue and collect requests 59 | HRESULT QueueSample( 60 | __inout REFERENCE_TIME& tCurrent, 61 | REFERENCE_TIME tAlignStop, 62 | BOOL bDiscontinuity); 63 | 64 | HRESULT CollectAndDeliver( 65 | REFERENCE_TIME tStart, 66 | REFERENCE_TIME tStop); 67 | 68 | HRESULT DeliverSample( 69 | IMediaSample* pSample, 70 | REFERENCE_TIME tStart, 71 | REFERENCE_TIME tStop); 72 | 73 | protected: 74 | IMemAllocator * m_pAlloc; 75 | 76 | public: 77 | CPullPin(); 78 | virtual ~CPullPin(); 79 | 80 | // returns S_OK if successfully connected to an IAsyncReader interface 81 | // from this object 82 | // Optional allocator should be proposed as a preferred allocator if 83 | // necessary 84 | // bSync is TRUE if we are to use sync reads instead of the 85 | // async methods. 86 | HRESULT Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync); 87 | 88 | // disconnect any connection made in Connect 89 | HRESULT Disconnect(); 90 | 91 | // agree an allocator using RequestAllocator - optional 92 | // props param specifies your requirements (non-zero fields). 93 | // returns an error code if fail to match requirements. 94 | // optional IMemAllocator interface is offered as a preferred allocator 95 | // but no error occurs if it can't be met. 96 | virtual HRESULT DecideAllocator( 97 | IMemAllocator* pAlloc, 98 | __inout_opt ALLOCATOR_PROPERTIES * pProps); 99 | 100 | // set start and stop position. if active, will start immediately at 101 | // the new position. Default is 0 to duration 102 | HRESULT Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop); 103 | 104 | // return the total duration 105 | HRESULT Duration(__out REFERENCE_TIME* ptDuration); 106 | 107 | // start pulling data 108 | HRESULT Active(void); 109 | 110 | // stop pulling data 111 | HRESULT Inactive(void); 112 | 113 | // helper functions 114 | LONGLONG AlignDown(LONGLONG ll, LONG lAlign) { 115 | // aligning downwards is just truncation 116 | return ll & ~(lAlign-1); 117 | }; 118 | 119 | LONGLONG AlignUp(LONGLONG ll, LONG lAlign) { 120 | // align up: round up to next boundary 121 | return (ll + (lAlign -1)) & ~(lAlign -1); 122 | }; 123 | 124 | // GetReader returns the (addrefed) IAsyncReader interface 125 | // for SyncRead etc 126 | IAsyncReader* GetReader() { 127 | m_pReader->AddRef(); 128 | return m_pReader; 129 | }; 130 | 131 | // -- pure -- 132 | 133 | // override this to handle data arrival 134 | // return value other than S_OK will stop data 135 | virtual HRESULT Receive(IMediaSample*) PURE; 136 | 137 | // override this to handle end-of-stream 138 | virtual HRESULT EndOfStream(void) PURE; 139 | 140 | // called on runtime errors that will have caused pulling 141 | // to stop 142 | // these errors are all returned from the upstream filter, who 143 | // will have already reported any errors to the filtergraph. 144 | virtual void OnError(HRESULT hr) PURE; 145 | 146 | // flush this pin and all downstream 147 | virtual HRESULT BeginFlush() PURE; 148 | virtual HRESULT EndFlush() PURE; 149 | 150 | }; 151 | 152 | #endif //__PULLPIN_H__ 153 | -------------------------------------------------------------------------------- /dll/src/baseclasses/reftime.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: RefTime.h 3 | // 4 | // Desc: DirectShow base classes - defines CRefTime, a class that manages 5 | // reference times. 6 | // 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | // 12 | // CRefTime 13 | // 14 | // Manage reference times. 15 | // Shares same data layout as REFERENCE_TIME, but adds some (nonvirtual) 16 | // functions providing simple comparison, conversion and arithmetic. 17 | // 18 | // A reference time (at the moment) is a unit of seconds represented in 19 | // 100ns units as is used in the Win32 FILETIME structure. BUT the time 20 | // a REFERENCE_TIME represents is NOT the time elapsed since 1/1/1601 it 21 | // will either be stream time or reference time depending upon context 22 | // 23 | // This class provides simple arithmetic operations on reference times 24 | // 25 | // keep non-virtual otherwise the data layout will not be the same as 26 | // REFERENCE_TIME 27 | 28 | 29 | // ----- 30 | // note that you are safe to cast a CRefTime* to a REFERENCE_TIME*, but 31 | // you will need to do so explicitly 32 | // ----- 33 | 34 | 35 | #ifndef __REFTIME__ 36 | #define __REFTIME__ 37 | 38 | 39 | const LONGLONG MILLISECONDS = (1000); // 10 ^ 3 40 | const LONGLONG NANOSECONDS = (1000000000); // 10 ^ 9 41 | const LONGLONG UNITS = (NANOSECONDS / 100); // 10 ^ 7 42 | 43 | /* Unfortunately an inline function here generates a call to __allmul 44 | - even for constants! 45 | */ 46 | #define MILLISECONDS_TO_100NS_UNITS(lMs) \ 47 | Int32x32To64((lMs), (UNITS / MILLISECONDS)) 48 | 49 | class CRefTime 50 | { 51 | public: 52 | 53 | // *MUST* be the only data member so that this class is exactly 54 | // equivalent to a REFERENCE_TIME. 55 | // Also, must be *no virtual functions* 56 | 57 | REFERENCE_TIME m_time; 58 | 59 | inline CRefTime() 60 | { 61 | // default to 0 time 62 | m_time = 0; 63 | }; 64 | 65 | inline CRefTime(LONG msecs) 66 | { 67 | m_time = MILLISECONDS_TO_100NS_UNITS(msecs); 68 | }; 69 | 70 | inline CRefTime(REFERENCE_TIME rt) 71 | { 72 | m_time = rt; 73 | }; 74 | 75 | inline operator REFERENCE_TIME() const 76 | { 77 | return m_time; 78 | }; 79 | 80 | inline CRefTime& operator=(const CRefTime& rt) 81 | { 82 | m_time = rt.m_time; 83 | return *this; 84 | }; 85 | 86 | inline CRefTime& operator=(const LONGLONG ll) 87 | { 88 | m_time = ll; 89 | return *this; 90 | }; 91 | 92 | inline CRefTime& operator+=(const CRefTime& rt) 93 | { 94 | return (*this = *this + rt); 95 | }; 96 | 97 | inline CRefTime& operator-=(const CRefTime& rt) 98 | { 99 | return (*this = *this - rt); 100 | }; 101 | 102 | inline LONG Millisecs(void) 103 | { 104 | return (LONG)(m_time / (UNITS / MILLISECONDS)); 105 | }; 106 | 107 | inline LONGLONG GetUnits(void) 108 | { 109 | return m_time; 110 | }; 111 | }; 112 | 113 | const LONGLONG TimeZero = 0; 114 | 115 | #endif /* __REFTIME__ */ 116 | 117 | -------------------------------------------------------------------------------- /dll/src/baseclasses/schedule.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: Schedule.h 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1996-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #ifndef __CAMSchedule__ 11 | #define __CAMSchedule__ 12 | 13 | class CAMSchedule : private CBaseObject 14 | { 15 | public: 16 | virtual ~CAMSchedule(); 17 | // ev is the event we should fire if the advise time needs re-evaluating 18 | CAMSchedule( HANDLE ev ); 19 | 20 | DWORD GetAdviseCount(); 21 | REFERENCE_TIME GetNextAdviseTime(); 22 | 23 | // We need a method for derived classes to add advise packets, we return the cookie 24 | DWORD_PTR AddAdvisePacket( const REFERENCE_TIME & time1, const REFERENCE_TIME & time2, HANDLE h, BOOL periodic ); 25 | // And a way to cancel 26 | HRESULT Unadvise(DWORD_PTR dwAdviseCookie); 27 | 28 | // Tell us the time please, and we'll dispatch the expired events. We return the time of the next event. 29 | // NB: The time returned will be "useless" if you start adding extra Advises. But that's the problem of 30 | // whoever is using this helper class (typically a clock). 31 | REFERENCE_TIME Advise( const REFERENCE_TIME & rtTime ); 32 | 33 | // Get the event handle which will be set if advise time requires re-evaluation. 34 | HANDLE GetEvent() const { return m_ev; } 35 | 36 | private: 37 | // We define the nodes that will be used in our singly linked list 38 | // of advise packets. The list is ordered by time, with the 39 | // elements that will expire first at the front. 40 | class CAdvisePacket 41 | { 42 | public: 43 | CAdvisePacket() 44 | {} 45 | 46 | CAdvisePacket * m_next; 47 | DWORD_PTR m_dwAdviseCookie; 48 | REFERENCE_TIME m_rtEventTime; // Time at which event should be set 49 | REFERENCE_TIME m_rtPeriod; // Periodic time 50 | HANDLE m_hNotify; // Handle to event or semephore 51 | BOOL m_bPeriodic; // TRUE => Periodic event 52 | 53 | CAdvisePacket( __inout_opt CAdvisePacket * next, LONGLONG time ) : m_next(next), m_rtEventTime(time) 54 | {} 55 | 56 | void InsertAfter( __inout CAdvisePacket * p ) 57 | { 58 | p->m_next = m_next; 59 | m_next = p; 60 | } 61 | 62 | int IsZ() const // That is, is it the node that represents the end of the list 63 | { return m_next == 0; } 64 | 65 | CAdvisePacket * RemoveNext() 66 | { 67 | CAdvisePacket *const next = m_next; 68 | CAdvisePacket *const new_next = next->m_next; 69 | m_next = new_next; 70 | return next; 71 | } 72 | 73 | void DeleteNext() 74 | { 75 | delete RemoveNext(); 76 | } 77 | 78 | CAdvisePacket * Next() const 79 | { 80 | CAdvisePacket * result = m_next; 81 | if (result->IsZ()) result = 0; 82 | return result; 83 | } 84 | 85 | DWORD_PTR Cookie() const 86 | { return m_dwAdviseCookie; } 87 | }; 88 | 89 | // Structure is: 90 | // head -> elmt1 -> elmt2 -> z -> null 91 | // So an empty list is: head -> z -> null 92 | // Having head & z as links makes insertaion, 93 | // deletion and shunting much easier. 94 | CAdvisePacket head, z; // z is both a tail and a sentry 95 | 96 | volatile DWORD_PTR m_dwNextCookie; // Strictly increasing 97 | volatile DWORD m_dwAdviseCount; // Number of elements on list 98 | 99 | CCritSec m_Serialize; 100 | 101 | // AddAdvisePacket: adds the packet, returns the cookie (0 if failed) 102 | DWORD_PTR AddAdvisePacket( __inout CAdvisePacket * pPacket ); 103 | // Event that we should set if the packed added above will be the next to fire. 104 | const HANDLE m_ev; 105 | 106 | // A Shunt is where we have changed the first element in the 107 | // list and want it re-evaluating (i.e. repositioned) in 108 | // the list. 109 | void ShuntHead(); 110 | 111 | // Rather than delete advise packets, we cache them for future use 112 | CAdvisePacket * m_pAdviseCache; 113 | DWORD m_dwCacheCount; 114 | enum { dwCacheMax = 5 }; // Don't bother caching more than five 115 | 116 | void Delete( __inout CAdvisePacket * pLink );// This "Delete" will cache the Link 117 | 118 | // Attributes and methods for debugging 119 | public: 120 | #ifdef DEBUG 121 | void DumpLinkedList(); 122 | #else 123 | void DumpLinkedList() {} 124 | #endif 125 | 126 | }; 127 | 128 | #endif // __CAMSchedule__ 129 | -------------------------------------------------------------------------------- /dll/src/baseclasses/seekpt.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: SeekPT.cpp 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #include 11 | #include "seekpt.h" 12 | 13 | //================================================================== 14 | // CreateInstance 15 | // This goes in the factory template table to create new instances 16 | // If there is already a mapper instance - return that, else make one 17 | // and save it in a static variable so that forever after we can return that. 18 | //================================================================== 19 | 20 | CUnknown * CSeekingPassThru::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr) 21 | { 22 | return new CSeekingPassThru(NAME("Seeking PassThru"),pUnk, phr); 23 | } 24 | 25 | 26 | STDMETHODIMP CSeekingPassThru::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv) 27 | { 28 | if (riid == IID_ISeekingPassThru) { 29 | return GetInterface((ISeekingPassThru *) this, ppv); 30 | } else { 31 | if (m_pPosPassThru && 32 | (riid == IID_IMediaSeeking || 33 | riid == IID_IMediaPosition)) { 34 | return m_pPosPassThru->NonDelegatingQueryInterface(riid,ppv); 35 | } else { 36 | return CUnknown::NonDelegatingQueryInterface(riid, ppv); 37 | } 38 | } 39 | } 40 | 41 | 42 | CSeekingPassThru::CSeekingPassThru( __in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr ) 43 | : CUnknown(pName, pUnk, phr), 44 | m_pPosPassThru(NULL) 45 | { 46 | } 47 | 48 | 49 | CSeekingPassThru::~CSeekingPassThru() 50 | { 51 | delete m_pPosPassThru; 52 | } 53 | 54 | STDMETHODIMP CSeekingPassThru::Init(BOOL bRendererSeeking, IPin *pPin) 55 | { 56 | HRESULT hr = NOERROR; 57 | if (m_pPosPassThru) { 58 | hr = E_FAIL; 59 | } else { 60 | m_pPosPassThru = 61 | bRendererSeeking ? 62 | new CRendererPosPassThru( 63 | NAME("Render Seeking COM object"), 64 | (IUnknown *)this, 65 | &hr, 66 | pPin) : 67 | new CPosPassThru( 68 | NAME("Render Seeking COM object"), 69 | (IUnknown *)this, 70 | &hr, 71 | pPin); 72 | if (!m_pPosPassThru) { 73 | hr = E_OUTOFMEMORY; 74 | } else { 75 | if (FAILED(hr)) { 76 | delete m_pPosPassThru; 77 | m_pPosPassThru = NULL; 78 | } 79 | } 80 | } 81 | return hr; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /dll/src/baseclasses/seekpt.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: SeekPT.h 3 | // 4 | // Desc: DirectShow base classes. 5 | // 6 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 7 | //------------------------------------------------------------------------------ 8 | 9 | 10 | #ifndef __seekpt_h__ 11 | #define __seekpt_h__ 12 | 13 | 14 | class CSeekingPassThru : public ISeekingPassThru, public CUnknown 15 | { 16 | public: 17 | static CUnknown *CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); 18 | CSeekingPassThru(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); 19 | ~CSeekingPassThru(); 20 | 21 | DECLARE_IUNKNOWN; 22 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); 23 | 24 | STDMETHODIMP Init(BOOL bSupportRendering, IPin *pPin); 25 | 26 | private: 27 | CPosPassThru *m_pPosPassThru; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /dll/src/baseclasses/streams.cpp: -------------------------------------------------------------------------------- 1 | #include "streams.h" 2 | -------------------------------------------------------------------------------- /dll/src/baseclasses/sysclock.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: SysClock.cpp 3 | // 4 | // Desc: DirectShow base classes - implements a system clock based on 5 | // IReferenceClock. 6 | // 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | #include 12 | #include 13 | 14 | 15 | #ifdef FILTER_DLL 16 | 17 | /* List of class IDs and creator functions for the class factory. This 18 | provides the link between the OLE entry point in the DLL and an object 19 | being created. The class factory will call the static CreateInstance 20 | function when it is asked to create a CLSID_SystemClock object */ 21 | 22 | CFactoryTemplate g_Templates[1] = { 23 | {&CLSID_SystemClock, CSystemClock::CreateInstance} 24 | }; 25 | 26 | int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 27 | #endif 28 | 29 | /* This goes in the factory template table to create new instances */ 30 | CUnknown * WINAPI CSystemClock::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr) 31 | { 32 | return new CSystemClock(NAME("System reference clock"),pUnk, phr); 33 | } 34 | 35 | 36 | CSystemClock::CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr) : 37 | CBaseReferenceClock(pName, pUnk, phr) 38 | { 39 | } 40 | 41 | STDMETHODIMP CSystemClock::NonDelegatingQueryInterface( 42 | REFIID riid, 43 | __deref_out void ** ppv) 44 | { 45 | if (riid == IID_IPersist) 46 | { 47 | return GetInterface(static_cast(this), ppv); 48 | } 49 | else if (riid == IID_IAMClockAdjust) 50 | { 51 | return GetInterface(static_cast(this), ppv); 52 | } 53 | else 54 | { 55 | return CBaseReferenceClock::NonDelegatingQueryInterface(riid, ppv); 56 | } 57 | } 58 | 59 | /* Return the clock's clsid */ 60 | STDMETHODIMP 61 | CSystemClock::GetClassID(__out CLSID *pClsID) 62 | { 63 | CheckPointer(pClsID,E_POINTER); 64 | ValidateReadWritePtr(pClsID,sizeof(CLSID)); 65 | *pClsID = CLSID_SystemClock; 66 | return NOERROR; 67 | } 68 | 69 | 70 | STDMETHODIMP 71 | CSystemClock::SetClockDelta(REFERENCE_TIME rtDelta) 72 | { 73 | return SetTimeDelta(rtDelta); 74 | } 75 | -------------------------------------------------------------------------------- /dll/src/baseclasses/sysclock.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // File: SysClock.h 3 | // 4 | // Desc: DirectShow base classes - defines a system clock implementation of 5 | // IReferenceClock. 6 | // 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. 8 | //------------------------------------------------------------------------------ 9 | 10 | 11 | #ifndef __SYSTEMCLOCK__ 12 | #define __SYSTEMCLOCK__ 13 | 14 | // 15 | // Base clock. Uses timeGetTime ONLY 16 | // Uses most of the code in the base reference clock. 17 | // Provides GetTime 18 | // 19 | 20 | class CSystemClock : public CBaseReferenceClock, public IAMClockAdjust, public IPersist 21 | { 22 | public: 23 | // We must be able to create an instance of ourselves 24 | static CUnknown * WINAPI CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); 25 | CSystemClock(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr); 26 | 27 | DECLARE_IUNKNOWN 28 | 29 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv); 30 | 31 | // Yield up our class id so that we can be persisted 32 | // Implement required Ipersist method 33 | STDMETHODIMP GetClassID(__out CLSID *pClsID); 34 | 35 | // IAMClockAdjust methods 36 | STDMETHODIMP SetClockDelta(REFERENCE_TIME rtDelta); 37 | }; //CSystemClock 38 | 39 | #endif /* __SYSTEMCLOCK__ */ 40 | -------------------------------------------------------------------------------- /dll/src/bs2b.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {C59B751C-F10D-4DE0-B580-73CB03B27B6E} 23 | 24 | 25 | 26 | 27 | Unicode 28 | StaticLibrary 29 | 30 | 31 | true 32 | 33 | 34 | false 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Level3 47 | true 48 | true 49 | 50 | 51 | 52 | 53 | Disabled 54 | false 55 | 56 | 57 | 58 | 59 | MaxSpeed 60 | true 61 | true 62 | 63 | 64 | true 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /dll/src/fftw-config/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FFTW_DOUBLE 1 4 | #define HAVE_SSE2 1 5 | 6 | #define DISABLE_FORTRAN 1 7 | 8 | #define HAVE_ABORT 1 9 | #define HAVE_UINTPTR_T 1 10 | 11 | #define HAVE_INTTYPES_H 1 12 | #define HAVE_MALLOC_H 1 13 | #define HAVE_STDINT_H 1 14 | #define HAVE_STRING_H 1 15 | #define HAVE_SYS_TYPES_H 1 16 | 17 | #define FFTW_CC "cl" 18 | #define PACKAGE "fftw" 19 | #define VERSION "3.3.4" 20 | #define PACKAGE_VERSION VERSION 21 | -------------------------------------------------------------------------------- /dll/src/rubberband.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {863E6128-1F58-4371-A282-0FCC62DFF747} 23 | 24 | 25 | 26 | 27 | Unicode 28 | StaticLibrary 29 | 30 | 31 | true 32 | 33 | 34 | false 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | rubberband;rubberband\src;soxr\src;fftw\api 48 | __MSVC__;WIN32;NOMINMAX;_USE_MATH_DEFINES;NO_THREADING;PROCESS_SAMPLE_TYPE=double;HAVE_FFTW3;FFTW_DOUBLE_ONLY;HAVE_LIBSAMPLERATE;%(PreprocessorDefinitions) 49 | 50 | 51 | 52 | 53 | Disabled 54 | false 55 | 56 | 57 | 58 | 59 | MaxSpeed 60 | true 61 | true 62 | 63 | 64 | true 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ..\snprintf.h 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | true 87 | 88 | 89 | 90 | 91 | 92 | 93 | true 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /dll/src/rubberband.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | src 6 | 7 | 8 | src 9 | 10 | 11 | src 12 | 13 | 14 | src 15 | 16 | 17 | src 18 | 19 | 20 | src 21 | 22 | 23 | src 24 | 25 | 26 | src 27 | 28 | 29 | src 30 | 31 | 32 | src 33 | 34 | 35 | src 36 | 37 | 38 | src 39 | 40 | 41 | src 42 | 43 | 44 | src 45 | 46 | 47 | src 48 | 49 | 50 | src 51 | 52 | 53 | src 54 | 55 | 56 | src 57 | 58 | 59 | src 60 | 61 | 62 | src 63 | 64 | 65 | 66 | 67 | 68 | 69 | {5f1ad43f-a951-4728-878e-cae51eb9e82a} 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /dll/src/sanear-dll.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {E02FD084-ED17-46C7-B5A6-F0BBB7966BBB} 23 | 24 | 25 | 26 | 27 | Unicode 28 | DynamicLibrary 29 | 30 | 31 | true 32 | 33 | 34 | false 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | $(BinDir) 46 | .ax 47 | 48 | 49 | sanear 50 | 51 | 52 | sanear64 53 | 54 | 55 | 56 | Level3 57 | true 58 | true 59 | Use 60 | pch.h 61 | baseclasses 62 | 63 | 64 | sanear-dll/sanear.def 65 | Comctl32.lib;%(AdditionalDependencies) 66 | 67 | 68 | 69 | 70 | Disabled 71 | false 72 | 73 | 74 | 75 | 76 | MaxSpeed 77 | true 78 | true 79 | 80 | 81 | true 82 | true 83 | 84 | 85 | 86 | 87 | {bb2b61af-734a-4dad-9326-07f4f9ea088f} 88 | 89 | 90 | {b8375339-1932-4cc0-ae5b-257672078e41} 91 | 92 | 93 | {c59b751c-f10d-4de0-b580-73cb03b27b6e} 94 | 95 | 96 | {85a00e9e-c632-497e-8dcb-857487f4d940} 97 | 98 | 99 | {863e6128-1f58-4371-a282-0fcc62dff747} 100 | 101 | 102 | {3c1b816a-645c-4e1f-a006-5c47263e59c5} 103 | 104 | 105 | {2d2a92ff-1fb6-4926-affb-5e00d27939fc} 106 | 107 | 108 | 109 | 110 | 111 | 112 | Create 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /dll/src/sanear-dll.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Common 7 | 8 | 9 | Filter 10 | 11 | 12 | Filter 13 | 14 | 15 | Filter 16 | 17 | 18 | 19 | 20 | Resources 21 | 22 | 23 | Common 24 | 25 | 26 | Filter 27 | 28 | 29 | Filter 30 | 31 | 32 | Filter 33 | 34 | 35 | 36 | 37 | {c31c72e9-891d-4f49-8964-60ac80d2db08} 38 | 39 | 40 | {57b1ee7d-f0ca-499f-b0fd-57fe76f3012d} 41 | 42 | 43 | {8215adc6-5660-48db-98c3-2627cc6f8b77} 44 | 45 | 46 | {575443ed-3498-4000-aec4-9cfc20cb4a2f} 47 | 48 | 49 | 50 | 51 | Props 52 | 53 | 54 | Props 55 | 56 | 57 | Props 58 | 59 | 60 | Props 61 | 62 | 63 | 64 | 65 | 66 | Resources 67 | 68 | 69 | 70 | 71 | Resources 72 | 73 | 74 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/Entry.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "OuterFilter.h" 4 | #include "../../../src/MyPropertyPage.h" 5 | 6 | namespace 7 | { 8 | // {DF557071-C9FD-433A-9627-81E0D3640ED9} 9 | const GUID filterGuid = {0xdf557071, 0xc9fd, 0x433a, {0x96, 0x27, 0x81, 0xe0, 0xd3, 0x64, 0xe, 0xd9}}; 10 | const WCHAR filterName[] = L"Sanear Audio Renderer"; 11 | 12 | const GUID statusPageGuid = __uuidof(SaneAudioRenderer::MyPropertyPage); 13 | const WCHAR statusPageName[] = L"Sanear Status Page"; 14 | 15 | const AMOVIESETUP_MEDIATYPE pinTypes[] = { 16 | {&MEDIATYPE_Audio, &CLSID_NULL}, 17 | }; 18 | 19 | const AMOVIESETUP_PIN setupPin = { 20 | L"", TRUE, FALSE, FALSE, FALSE, &CLSID_NULL, nullptr, _countof(pinTypes), pinTypes, 21 | }; 22 | 23 | const AMOVIESETUP_FILTER setupFilter = { 24 | &filterGuid, filterName, MERIT_DO_NOT_USE, 1, &setupPin 25 | }; 26 | } 27 | 28 | CUnknown* WINAPI CreateFilterInstance(LPUNKNOWN, HRESULT*); 29 | CUnknown* WINAPI CreateStatusPageInstance(LPUNKNOWN, HRESULT*); 30 | 31 | CFactoryTemplate g_Templates[] = { 32 | {filterName, &filterGuid, CreateFilterInstance}, 33 | {statusPageName, &statusPageGuid, CreateStatusPageInstance}, 34 | }; 35 | 36 | int g_cTemplates = _countof(g_Templates); 37 | 38 | 39 | STDAPI RegisterAllServers(LPCWSTR szFileName, BOOL bRegister); 40 | 41 | namespace 42 | { 43 | struct CoFreeUnusedLibrariesHelper 44 | { 45 | ~CoFreeUnusedLibrariesHelper() { CoFreeUnusedLibraries(); }; 46 | }; 47 | 48 | HRESULT DllRegisterServer(bool reg) 49 | { 50 | wchar_t filename[MAX_PATH]; 51 | if (!GetModuleFileName(g_hInst, filename, MAX_PATH)) 52 | return AmGetLastErrorToHResult(); 53 | 54 | if (reg) 55 | ReturnIfFailed(RegisterAllServers(filename, TRUE)); 56 | 57 | { 58 | SaneAudioRenderer::CoInitializeHelper coInitializeHelper(COINIT_APARTMENTTHREADED); 59 | CoFreeUnusedLibrariesHelper coFreeUnusedLibrariesHelper; 60 | 61 | IFilterMapper2Ptr filterMapper; 62 | ReturnIfFailed(CoCreateInstance(CLSID_FilterMapper2, nullptr, 63 | CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&filterMapper))); 64 | { 65 | HRESULT result; 66 | 67 | result = filterMapper->UnregisterFilter(nullptr, nullptr, *setupFilter.clsID); 68 | 69 | if (FAILED(result)) 70 | ReturnIfNotEquals(result, 0x80070002); 71 | 72 | result = filterMapper->UnregisterFilter(&CLSID_AudioRendererCategory, nullptr, *setupFilter.clsID); 73 | 74 | if (FAILED(result)) 75 | ReturnIfNotEquals(result, 0x80070002); 76 | } 77 | 78 | if (reg) 79 | { 80 | const REGFILTER2 rf = { 81 | 1, 82 | setupFilter.dwMerit, 83 | setupFilter.nPins, 84 | setupFilter.lpPin, 85 | }; 86 | 87 | ReturnIfFailed(filterMapper->RegisterFilter(*setupFilter.clsID, setupFilter.strName, 88 | nullptr, &CLSID_AudioRendererCategory, nullptr, &rf)); 89 | 90 | ReturnIfFailed(filterMapper->RegisterFilter(*setupFilter.clsID, setupFilter.strName, 91 | nullptr, nullptr, nullptr, &rf)); 92 | } 93 | } 94 | 95 | if (!reg) 96 | ReturnIfFailed(RegisterAllServers(filename, FALSE)); 97 | 98 | return S_OK; 99 | } 100 | } 101 | 102 | CUnknown* WINAPI CreateFilterInstance(IUnknown* pUnknown, HRESULT* pResult) 103 | { 104 | CheckPointer(pResult, nullptr); 105 | 106 | auto pFilter = new(std::nothrow) SaneAudioRenderer::OuterFilter(pUnknown, filterGuid); 107 | 108 | if (!pFilter) 109 | *pResult = E_OUTOFMEMORY; 110 | 111 | return pFilter; 112 | } 113 | 114 | CUnknown* WINAPI CreateStatusPageInstance(IUnknown* pUnknown, HRESULT* pResult) 115 | { 116 | CheckPointer(pResult, nullptr); 117 | 118 | auto pFilter = new(std::nothrow) SaneAudioRenderer::MyPropertyPage(); 119 | 120 | if (!pFilter) 121 | *pResult = E_OUTOFMEMORY; 122 | 123 | return pFilter; 124 | } 125 | 126 | STDAPI DllRegisterServer() 127 | { 128 | if (!IsWindowsVistaOrGreater()) 129 | return E_FAIL; 130 | 131 | return DllRegisterServer(true); 132 | } 133 | 134 | STDAPI DllUnregisterServer() 135 | { 136 | return DllRegisterServer(false); 137 | } 138 | 139 | extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); 140 | 141 | BOOL WINAPI DllMain(HINSTANCE hDllHandle, DWORD dwReason, LPVOID pReserved) 142 | { 143 | return DllEntryPoint(hDllHandle, dwReason, pReserved); 144 | } 145 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/OuterFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "OuterFilter.h" 3 | 4 | #include "../../../src/Factory.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | namespace 9 | { 10 | const auto DeviceId = L"DeviceId"; 11 | const auto DeviceExclusive = L"DeviceExclusive"; 12 | const auto DeviceBufferDuration = L"DeviceBufferDuration"; 13 | const auto AllowBitstreaming = L"AllowBitstreaming"; 14 | const auto CrossfeedEnabled = L"CrossfeedEnabled"; 15 | const auto CrossfeedCutoffFrequency = L"CrossfeedCutoffFrequency"; 16 | const auto CrossfeedLevel = L"CrossfeedLevel"; 17 | const auto IgnoreSystemChannelMixer = L"IgnoreSystemChannelMixer"; 18 | } 19 | 20 | OuterFilter::OuterFilter(IUnknown* pUnknown, const GUID& guid) 21 | : CUnknown(L"SaneAudioRenderer::OuterFilter", pUnknown) 22 | , m_guid(guid) 23 | { 24 | } 25 | 26 | OuterFilter::~OuterFilter() 27 | { 28 | BOOL boolValue; 29 | WCHAR* stringValue; 30 | UINT32 uintValue1; 31 | UINT32 uintValue2; 32 | 33 | if (SUCCEEDED(m_settings->GetOuputDevice(&stringValue, &boolValue, &uintValue1))) 34 | { 35 | std::unique_ptr holder(stringValue); 36 | m_registryKey.SetString(DeviceId, stringValue); 37 | m_registryKey.SetUint(DeviceExclusive, boolValue); 38 | m_registryKey.SetUint(DeviceBufferDuration, uintValue1); 39 | } 40 | 41 | m_registryKey.SetUint(AllowBitstreaming, m_settings->GetAllowBitstreaming()); 42 | 43 | m_registryKey.SetUint(CrossfeedEnabled, m_settings->GetCrossfeedEnabled()); 44 | 45 | m_settings->GetCrossfeedSettings(&uintValue1, &uintValue2); 46 | m_registryKey.SetUint(CrossfeedCutoffFrequency, uintValue1); 47 | m_registryKey.SetUint(CrossfeedLevel, uintValue2); 48 | 49 | m_registryKey.SetUint(IgnoreSystemChannelMixer, m_settings->GetIgnoreSystemChannelMixer()); 50 | } 51 | 52 | STDMETHODIMP OuterFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv) 53 | { 54 | if (!m_initialized) 55 | ReturnIfFailed(Init()); 56 | 57 | if (riid == IID_IUnknown) 58 | return CUnknown::NonDelegatingQueryInterface(riid, ppv); 59 | 60 | if (riid == IID_ISpecifyPropertyPages) 61 | return m_innerFilter->QueryInterface(__uuidof(ISpecifyPropertyPages2), ppv); 62 | 63 | return m_innerFilter->QueryInterface(riid, ppv); 64 | } 65 | 66 | HRESULT OuterFilter::Init() 67 | { 68 | assert(!m_initialized); 69 | 70 | ReturnIfFailed(Factory::CreateSettings(&m_settings)) 71 | ReturnIfFailed(Factory::CreateFilterAggregated(GetOwner(), m_guid, m_settings, &m_innerFilter)); 72 | ReturnIfFailed(m_registryKey.Open(HKEY_CURRENT_USER, L"Software\\sanear")); 73 | ReturnIfFailed(m_trayWindow.Init(m_settings)); 74 | 75 | m_initialized = true; 76 | 77 | std::vector stringValue; 78 | uint32_t uintValue1; 79 | uint32_t uintValue2; 80 | 81 | if (m_registryKey.GetString(DeviceId, stringValue) && 82 | m_registryKey.GetUint(DeviceExclusive, uintValue1) && 83 | m_registryKey.GetUint(DeviceBufferDuration, uintValue2)) 84 | { 85 | m_settings->SetOuputDevice(stringValue.data(), uintValue1, uintValue2); 86 | } 87 | 88 | if (m_registryKey.GetUint(AllowBitstreaming, uintValue1)) 89 | m_settings->SetAllowBitstreaming(uintValue1); 90 | 91 | if (m_registryKey.GetUint(CrossfeedEnabled, uintValue1)) 92 | m_settings->SetCrossfeedEnabled(uintValue1); 93 | 94 | if (m_registryKey.GetUint(CrossfeedCutoffFrequency, uintValue1) && 95 | m_registryKey.GetUint(CrossfeedLevel, uintValue2)) 96 | { 97 | m_settings->SetCrossfeedSettings(uintValue1, uintValue2); 98 | } 99 | 100 | if (m_registryKey.GetUint(IgnoreSystemChannelMixer, uintValue1)) 101 | m_settings->SetIgnoreSystemChannelMixer(uintValue1); 102 | 103 | return S_OK; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/OuterFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RegistryKey.h" 4 | #include "TrayWindow.h" 5 | 6 | #include "../../../src/Interfaces.h" 7 | 8 | namespace SaneAudioRenderer 9 | { 10 | class OuterFilter final 11 | : public CUnknown 12 | { 13 | public: 14 | 15 | OuterFilter(IUnknown* pUnknown, const GUID& guid); 16 | ~OuterFilter(); 17 | OuterFilter(const OuterFilter&) = delete; 18 | OuterFilter& operator=(const OuterFilter&) = delete; 19 | 20 | DECLARE_IUNKNOWN 21 | 22 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv) override; 23 | 24 | private: 25 | 26 | HRESULT Init(); 27 | 28 | const GUID& m_guid; 29 | bool m_initialized = false; 30 | RegistryKey m_registryKey; 31 | ISettingsPtr m_settings; 32 | IUnknownPtr m_innerFilter; 33 | TrayWindow m_trayWindow; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/RegistryKey.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "RegistryKey.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | HRESULT RegistryKey::Open(HKEY key, const wchar_t* subkey) 7 | { 8 | Close(); 9 | 10 | return RegCreateKeyEx(key, subkey, 0, nullptr, REG_OPTION_NON_VOLATILE, 11 | KEY_READ | KEY_WRITE, nullptr, &m_hKey, nullptr); 12 | } 13 | 14 | void RegistryKey::Close() 15 | { 16 | if (m_hKey != NULL) 17 | { 18 | RegCloseKey(m_hKey); 19 | m_hKey = NULL; 20 | } 21 | } 22 | 23 | RegistryKey::~RegistryKey() 24 | { 25 | Close(); 26 | } 27 | 28 | bool RegistryKey::SetString(const wchar_t* key, const wchar_t* value) 29 | { 30 | const DWORD valueSize = (DWORD)(wcslen(value) + 1) * sizeof(wchar_t); 31 | return RegSetValueEx(m_hKey, key, 0, REG_SZ, (const BYTE*)value, valueSize) == ERROR_SUCCESS; 32 | } 33 | 34 | bool RegistryKey::GetString(const wchar_t* name, std::vector& value) 35 | { 36 | DWORD valueSize; 37 | DWORD valuetype; 38 | 39 | if (RegQueryValueEx(m_hKey, name, 0, &valuetype, nullptr, &valueSize) != ERROR_SUCCESS) 40 | return false; 41 | 42 | try 43 | { 44 | value.resize(valueSize / sizeof(wchar_t)); 45 | } 46 | catch (std::bad_alloc&) 47 | { 48 | return false; 49 | } 50 | 51 | if (RegQueryValueEx(m_hKey, name, 0, &valuetype, (BYTE*)value.data(), &valueSize) != ERROR_SUCCESS || 52 | valuetype != REG_SZ) 53 | { 54 | return false; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | bool RegistryKey::SetUint(const wchar_t* name, uint32_t value) 61 | { 62 | return RegSetValueEx(m_hKey, name, 0, REG_DWORD, (BYTE*)&value, sizeof(uint32_t)) == ERROR_SUCCESS; 63 | } 64 | 65 | bool RegistryKey::GetUint(const wchar_t* name, uint32_t& value) 66 | { 67 | DWORD valueSize = sizeof(uint32_t); 68 | DWORD valuetype; 69 | 70 | if (RegQueryValueEx(m_hKey, name, 0, &valuetype, (BYTE*)&value, &valueSize) != ERROR_SUCCESS || 71 | valuetype != REG_DWORD) 72 | { 73 | return false; 74 | } 75 | 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/RegistryKey.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SaneAudioRenderer 4 | { 5 | class RegistryKey final 6 | { 7 | public: 8 | 9 | RegistryKey() = default; 10 | ~RegistryKey(); 11 | RegistryKey(const RegistryKey&) = delete; 12 | RegistryKey& operator=(const RegistryKey&) = delete; 13 | 14 | HRESULT Open(HKEY key, const wchar_t* subkey); 15 | void Close(); 16 | 17 | bool SetString(const wchar_t* name, const wchar_t* value); 18 | bool GetString(const wchar_t* name, std::vector& value); 19 | 20 | bool SetUint(const wchar_t* name, uint32_t value); 21 | bool RegistryKey::GetUint(const wchar_t* name, uint32_t& value); 22 | 23 | private: 24 | 25 | HKEY m_hKey = NULL; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/TrayWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../../src/Interfaces.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class TrayWindow final 8 | { 9 | public: 10 | 11 | TrayWindow(); 12 | ~TrayWindow(); 13 | TrayWindow(const TrayWindow&) = delete; 14 | TrayWindow& operator=(const TrayWindow&) = delete; 15 | 16 | HRESULT Init(ISettings* pSettings); 17 | 18 | DWORD ThreadProc(); 19 | LRESULT WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 20 | 21 | private: 22 | 23 | void Destroy(); 24 | 25 | void AddIcon(); 26 | void RemoveIcon(); 27 | 28 | void AddMenu(); 29 | void RemoveMenu(); 30 | 31 | void OnTrayNotify(WPARAM wParam, LPARAM lParam); 32 | void OnCommand(WPARAM wParam, LPARAM lParam); 33 | 34 | UINT m_taskbarCreatedMessage = 0; 35 | NOTIFYICONDATA m_nid; 36 | 37 | ISettingsPtr m_settings; 38 | HANDLE m_hThread = NULL; 39 | HWND m_hWindow = NULL; 40 | HMENU m_hMenu = NULL; 41 | std::promise m_windowCreated; 42 | std::vector> m_devices; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../../src/pch.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace SaneAudioRenderer 9 | { 10 | template 11 | unsigned CALLBACK StaticThreadProc(LPVOID p) 12 | { 13 | return (static_cast(p)->*ThreadProc)(); 14 | } 15 | 16 | template 17 | LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 18 | { 19 | if (LONG_PTR userData = GetWindowLongPtr(hWnd, GWLP_USERDATA)) 20 | return (reinterpret_cast(userData)->*WindowProc)(hWnd, msg, wParam, lParam); 21 | 22 | if (msg == WM_NCCREATE) 23 | { 24 | CREATESTRUCT* pCreateStruct = reinterpret_cast(lParam); 25 | SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); 26 | return (static_cast(pCreateStruct->lpCreateParams)->*WindowProc)(hWnd, msg, wParam, lParam); 27 | } 28 | 29 | return DefWindowProc(hWnd, msg, wParam, lParam); 30 | } 31 | 32 | inline void RunMessageLoop() 33 | { 34 | MSG msg; 35 | while (GetMessage(&msg, NULL, 0, 0)) 36 | { 37 | TranslateMessage(&msg); 38 | DispatchMessage(&msg); 39 | } 40 | } 41 | } 42 | 43 | _COM_SMARTPTR_TYPEDEF(IFilterMapper2, __uuidof(IFilterMapper2)); 44 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by sanear.rc 4 | // 5 | #define IDI_ICON1 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/sanear.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllGetClassObject PRIVATE 3 | DllCanUnloadNow PRIVATE 4 | DllRegisterServer PRIVATE 5 | DllUnregisterServer PRIVATE 6 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/sanear.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpc-hc/sanear/f7612c7f71316f357b00e43f5a497d1d0289ffe6/dll/src/sanear-dll/sanear.ico -------------------------------------------------------------------------------- /dll/src/sanear-dll/sanear.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United Kingdom) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_ICON1 ICON "sanear.ico" 56 | 57 | ///////////////////////////////////////////////////////////////////////////// 58 | // 59 | // Version 60 | // 61 | 62 | VS_VERSION_INFO VERSIONINFO 63 | FILEVERSION 0,3,0,0 64 | PRODUCTVERSION 0,3,0,0 65 | FILEFLAGSMASK 0x3fL 66 | #ifdef _DEBUG 67 | FILEFLAGS 0x1L 68 | #else 69 | FILEFLAGS 0x0L 70 | #endif 71 | FILEOS 0x40004L 72 | FILETYPE 0x2L 73 | FILESUBTYPE 0x0L 74 | BEGIN 75 | BLOCK "StringFileInfo" 76 | BEGIN 77 | BLOCK "080904b0" 78 | BEGIN 79 | VALUE "FileDescription", "Sanear, a robust DirectShow audio renderer" 80 | VALUE "FileVersion", "0.3.0.0" 81 | VALUE "ProductName", "Sanear Audio Renderer" 82 | VALUE "ProductVersion", "0.3.0.0" 83 | END 84 | END 85 | BLOCK "VarFileInfo" 86 | BEGIN 87 | VALUE "Translation", 0x809, 1200 88 | END 89 | END 90 | 91 | #endif // English (United Kingdom) resources 92 | ///////////////////////////////////////////////////////////////////////////// 93 | 94 | 95 | 96 | #ifndef APSTUDIO_INVOKED 97 | ///////////////////////////////////////////////////////////////////////////// 98 | // 99 | // Generated from the TEXTINCLUDE 3 resource. 100 | // 101 | 102 | 103 | ///////////////////////////////////////////////////////////////////////////// 104 | #endif // not APSTUDIO_INVOKED 105 | 106 | -------------------------------------------------------------------------------- /dll/src/sanear-dll/sanear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 60 | 65 | 72 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /dll/src/snprintf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #define snprintf sprintf_s 4 | -------------------------------------------------------------------------------- /dll/src/soundtouch.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {3C1B816A-645C-4E1F-A006-5C47263E59C5} 23 | 24 | 25 | 26 | 27 | Unicode 28 | StaticLibrary 29 | 30 | 31 | true 32 | 33 | 34 | false 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Level3 47 | true 48 | true 49 | soundtouch\include 50 | 51 | 52 | 53 | 54 | Disabled 55 | false 56 | 57 | 58 | 59 | 60 | MaxSpeed 61 | true 62 | true 63 | 64 | 65 | true 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /dll/src/soxr-config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define HAVE_SINGLE_PRECISION 1 4 | #define HAVE_DOUBLE_PRECISION 1 5 | #define HAVE_SIMD 1 6 | #define HAVE_FENV_H 1 7 | #define HAVE_LRINT 1 8 | 9 | #define HAVE_AVFFT 0 10 | #define WORDS_BIGENDIAN 0 11 | 12 | #include 13 | #include 14 | -------------------------------------------------------------------------------- /dll/src/soxr.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {2D2A92FF-1FB6-4926-AFFB-5E00D27939FC} 23 | 24 | 25 | 26 | 27 | Unicode 28 | StaticLibrary 29 | 30 | 31 | true 32 | 33 | 34 | false 35 | true 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | TurnOffAllWarnings 47 | true 48 | . 49 | _USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;SOXR_LIB;%(PreprocessorDefinitions) 50 | 51 | 52 | 53 | 54 | Disabled 55 | false 56 | 57 | 58 | 59 | 60 | MaxSpeed 61 | true 62 | true 63 | 64 | 65 | true 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/AudioDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspChunk.h" 4 | #include "DspFormat.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | struct AudioDeviceBackend final 9 | { 10 | SharedString id; 11 | SharedString adapterName; 12 | SharedString endpointName; 13 | UINT32 endpointFormFactor; 14 | bool supportsSharedEventMode; 15 | bool supportsExclusiveEventMode; 16 | 17 | IAudioClientPtr audioClient; 18 | IAudioRenderClientPtr audioRenderClient; 19 | IAudioClockPtr audioClock; 20 | 21 | SharedWaveFormat mixFormat; 22 | 23 | SharedWaveFormat waveFormat; 24 | DspFormat dspFormat; 25 | 26 | uint32_t bufferDuration; 27 | 28 | REFERENCE_TIME deviceLatency; 29 | UINT32 deviceBufferSize; 30 | 31 | bool exclusive; 32 | bool bitstream; 33 | bool eventMode; 34 | bool realtime; 35 | 36 | bool ignoredSystemChannelMixer; 37 | }; 38 | 39 | class AudioDevice 40 | { 41 | public: 42 | 43 | virtual ~AudioDevice() = default; 44 | 45 | virtual void Push(DspChunk& chunk, CAMEvent* pFilledEvent) = 0; 46 | virtual REFERENCE_TIME Finish(CAMEvent* pFilledEvent) = 0; 47 | 48 | virtual int64_t GetPosition() = 0; 49 | virtual int64_t GetEnd() = 0; 50 | virtual int64_t GetSilence() = 0; 51 | 52 | virtual void Start() = 0; 53 | virtual void Stop() = 0; 54 | virtual void Reset() = 0; 55 | 56 | SharedString GetId() const { return m_backend->id; } 57 | SharedString GetAdapterName() const { return m_backend->adapterName; } 58 | SharedString GetEndpointName() const { return m_backend->endpointName; } 59 | 60 | IAudioClockPtr GetClock() { return m_backend->audioClock; } 61 | 62 | SharedWaveFormat GetMixFormat() const { return m_backend->mixFormat; } 63 | 64 | SharedWaveFormat GetWaveFormat() const { return m_backend->waveFormat; } 65 | uint32_t GetRate() const { return m_backend->waveFormat->nSamplesPerSec; } 66 | uint32_t GetChannelCount() const { return m_backend->waveFormat->nChannels; } 67 | DspFormat GetDspFormat() const { return m_backend->dspFormat; } 68 | uint32_t GetBufferDuration() const { return m_backend->bufferDuration; } 69 | REFERENCE_TIME GetStreamLatency() const { return m_backend->deviceLatency; } 70 | 71 | bool IsExclusive() const { return m_backend->exclusive; } 72 | bool IsRealtime() const { return m_backend->realtime; } 73 | 74 | bool IgnoredSystemChannelMixer() const { return m_backend->ignoredSystemChannelMixer; } 75 | 76 | using RenewBackendFunction = std::function&)>; 77 | virtual bool RenewInactive(const RenewBackendFunction& renewBackend, int64_t& position) = 0; 78 | 79 | protected: 80 | 81 | std::shared_ptr m_backend; 82 | 83 | template 84 | bool IsLastInstance(T& smartPointer) 85 | { 86 | bool ret = (smartPointer.GetInterfacePtr()->AddRef() == 2); 87 | smartPointer.GetInterfacePtr()->Release(); 88 | return ret; 89 | } 90 | 91 | bool CheckLastInstances() 92 | { 93 | if (!m_backend.unique()) 94 | return false; 95 | 96 | if (m_backend->audioClock && !IsLastInstance(m_backend->audioClock)) 97 | return false; 98 | 99 | m_backend->audioClock = nullptr; 100 | 101 | if (m_backend->audioRenderClient && !IsLastInstance(m_backend->audioRenderClient)) 102 | return false; 103 | 104 | m_backend->audioRenderClient = nullptr; 105 | 106 | if (m_backend->audioClient && !IsLastInstance(m_backend->audioClient)) 107 | return false; 108 | 109 | return true; 110 | } 111 | }; 112 | } 113 | -------------------------------------------------------------------------------- /src/AudioDeviceEvent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AudioDevice.h" 4 | #include "DspChunk.h" 5 | #include "DspFormat.h" 6 | 7 | namespace SaneAudioRenderer 8 | { 9 | class AudioDeviceEvent final 10 | : public AudioDevice 11 | { 12 | public: 13 | 14 | AudioDeviceEvent(std::shared_ptr backend); 15 | AudioDeviceEvent(const AudioDeviceEvent&) = delete; 16 | AudioDeviceEvent& operator=(const AudioDeviceEvent&) = delete; 17 | ~AudioDeviceEvent(); 18 | 19 | void Push(DspChunk& chunk, CAMEvent* pFilledEvent) override; 20 | REFERENCE_TIME Finish(CAMEvent* pFilledEvent) override; 21 | 22 | int64_t GetPosition() override; 23 | int64_t GetEnd() override; 24 | int64_t GetSilence() override; 25 | 26 | void Start() override; 27 | void Stop() override; 28 | void Reset() override; 29 | 30 | bool RenewInactive(const RenewBackendFunction& renewBackend, int64_t& position) override; 31 | 32 | private: 33 | 34 | void EventFeed(); 35 | 36 | void PushBufferToDevice(); 37 | void PushChunkToBuffer(DspChunk& chunk); 38 | 39 | std::atomic m_endOfStream = false; 40 | int64_t m_endOfStreamPos = 0; 41 | 42 | std::thread m_thread; 43 | CCritSec m_threadMutex; 44 | 45 | CAMEvent m_wake; 46 | std::atomic m_exit = false; 47 | std::atomic m_error = false; 48 | 49 | uint64_t m_sentFrames = 0; 50 | std::atomic m_receivedFrames = 0; 51 | std::atomic m_silenceFrames = 0; 52 | 53 | CCritSec m_bufferMutex; 54 | std::deque m_buffer; 55 | size_t m_bufferFrames = 0; 56 | 57 | bool m_queuedStart = false; 58 | 59 | bool m_observeInactivity = false; 60 | CAMEvent m_observeInactivityWake; 61 | int64_t m_activityPointCounter = 0; 62 | 63 | CCritSec m_renewMutex; 64 | bool m_awaitingRenew = false; 65 | int64_t m_renewPosition = 0; 66 | size_t m_renewSilenceFrames = 0; 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /src/AudioDeviceManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AudioDevice.h" 4 | #include "Interfaces.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | class AudioDeviceNotificationClient final 9 | : public CUnknown 10 | , public IMMNotificationClient 11 | { 12 | public: 13 | 14 | DECLARE_IUNKNOWN 15 | 16 | AudioDeviceNotificationClient(std::atomic& defaultDeviceSerial); 17 | AudioDeviceNotificationClient(const AudioDeviceNotificationClient&) = delete; 18 | AudioDeviceNotificationClient& operator=(const AudioDeviceNotificationClient&) = delete; 19 | 20 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv) override; 21 | 22 | STDMETHODIMP OnDeviceStateChanged(LPCWSTR, DWORD) override { return S_OK; } 23 | STDMETHODIMP OnDeviceAdded(LPCWSTR) override { return S_OK; } 24 | STDMETHODIMP OnDeviceRemoved(LPCWSTR) override { return S_OK; } 25 | STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR); 26 | STDMETHODIMP OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) override { return S_OK; } 27 | 28 | private: 29 | 30 | std::atomic& m_defaultDeviceSerial; 31 | }; 32 | 33 | class AudioDeviceManager final 34 | { 35 | public: 36 | 37 | AudioDeviceManager(HRESULT& result); 38 | AudioDeviceManager(const AudioDeviceManager&) = delete; 39 | AudioDeviceManager& operator=(const AudioDeviceManager&) = delete; 40 | ~AudioDeviceManager(); 41 | 42 | bool BitstreamFormatSupported(SharedWaveFormat format, ISettings* pSettings); 43 | std::unique_ptr CreateDevice(SharedWaveFormat format, bool realtime, ISettings* pSettings); 44 | bool RenewInactiveDevice(AudioDevice& device, int64_t& position); 45 | 46 | uint32_t GetDefaultDeviceSerial() { return m_defaultDeviceSerial; } 47 | std::unique_ptr GetDefaultDeviceId(); 48 | 49 | private: 50 | 51 | std::thread m_thread; 52 | std::atomic m_exit = false; 53 | CAMEvent m_wake; 54 | CAMEvent m_done; 55 | 56 | std::function m_function; 57 | HRESULT m_result = S_OK; 58 | 59 | IMMDeviceEnumeratorPtr m_enumerator; 60 | 61 | IMMNotificationClientPtr m_notificationClient; 62 | std::atomic m_defaultDeviceSerial = 0; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /src/AudioDevicePush.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AudioDevice.h" 4 | #include "DspChunk.h" 5 | #include "DspFormat.h" 6 | 7 | namespace SaneAudioRenderer 8 | { 9 | class AudioDevicePush final 10 | : public AudioDevice 11 | { 12 | public: 13 | 14 | AudioDevicePush(std::shared_ptr backend); 15 | AudioDevicePush(const AudioDevicePush&) = delete; 16 | AudioDevicePush& operator=(const AudioDevicePush&) = delete; 17 | ~AudioDevicePush(); 18 | 19 | void Push(DspChunk& chunk, CAMEvent* pFilledEvent) override; 20 | REFERENCE_TIME Finish(CAMEvent* pFilledEvent) override; 21 | 22 | int64_t GetPosition() override; 23 | int64_t GetEnd() override; 24 | int64_t GetSilence() override; 25 | 26 | void Start() override; 27 | void Stop() override; 28 | void Reset() override; 29 | 30 | bool RenewInactive(const RenewBackendFunction& renewBackend, int64_t& position) override; 31 | 32 | private: 33 | 34 | void SilenceFeed(); 35 | 36 | void PushChunkToDevice(DspChunk& chunk, CAMEvent* pFilledEvent); 37 | UINT32 PushSilenceToDevice(UINT32 frames); 38 | 39 | bool m_endOfStream = false; 40 | int64_t m_endOfStreamPos = 0; 41 | 42 | uint64_t m_pushedFrames = 0; 43 | std::atomic m_silenceFrames = 0; 44 | 45 | std::thread m_thread; 46 | CAMEvent m_wake; 47 | std::atomic m_exit = false; 48 | std::atomic m_error = false; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/AudioRenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AudioDevice.h" 4 | #include "AudioDeviceManager.h" 5 | #include "DspBalance.h" 6 | #include "DspCrossfeed.h" 7 | #include "DspDither.h" 8 | #include "DspLimiter.h" 9 | #include "DspMatrix.h" 10 | #include "DspRate.h" 11 | #include "DspTempo.h" 12 | #include "DspTempo2.h" 13 | #include "DspVolume.h" 14 | #include "Interfaces.h" 15 | #include "SampleCorrection.h" 16 | 17 | namespace SaneAudioRenderer 18 | { 19 | class MyClock; 20 | 21 | class AudioRenderer final 22 | : public CCritSec 23 | { 24 | public: 25 | 26 | AudioRenderer(ISettings* pSettings, MyClock& clock, HRESULT& result); 27 | AudioRenderer(const AudioRenderer&) = delete; 28 | AudioRenderer& operator=(const AudioRenderer&) = delete; 29 | ~AudioRenderer(); 30 | 31 | void SetClock(IReferenceClock* pClock); 32 | 33 | bool Push(IMediaSample* pSample, AM_SAMPLE2_PROPERTIES& sampleProps, CAMEvent* pFilledEvent); 34 | bool Finish(bool blockUntilEnd, CAMEvent* pFilledEvent); 35 | 36 | void BeginFlush(); 37 | void EndFlush(); 38 | 39 | bool CheckFormat(SharedWaveFormat inputFormat, bool live); 40 | void SetFormat(SharedWaveFormat inputFormat, bool live); 41 | 42 | void NewSegment(double rate); 43 | 44 | void Play(REFERENCE_TIME startTime); 45 | void Pause(); 46 | void Stop(); 47 | 48 | float GetVolume() const { return m_volume; } 49 | void SetVolume(float volume) { m_volume = volume; } 50 | float GetBalance() const { return m_balance; } 51 | void SetBalance(float balance) { m_balance = balance; } 52 | 53 | SharedWaveFormat GetInputFormat(); 54 | const AudioDevice* GetAudioDevice(); 55 | std::vector GetActiveProcessors(); 56 | 57 | void TakeGuidedReclock(REFERENCE_TIME offset) { m_guidedReclockOffset += offset; } 58 | 59 | bool OnExternalClock() const { return m_externalClock; } 60 | bool IsLive() const { return m_live; } 61 | bool IsBitstreaming() const { return m_bitstreaming; } 62 | 63 | bool OnGuidedReclock(); 64 | 65 | private: 66 | 67 | void CheckDeviceSettings(); 68 | void StartDevice(); 69 | void CreateDevice(); 70 | void ClearDevice(); 71 | 72 | REFERENCE_TIME EstimateSlavingJitter(); 73 | 74 | void PushReslavingJitter(); 75 | 76 | void ApplyClockCorrection(); 77 | 78 | void ApplyRateCorrection(DspChunk& chunk); 79 | 80 | void InitializeProcessors(); 81 | 82 | template 83 | void EnumerateProcessors(F f) 84 | { 85 | f(&m_dspMatrix); 86 | f(&m_dspRate); 87 | #ifdef SANEAR_GPL_PHASE_VOCODER 88 | f(&m_dspTempo1); 89 | f(&m_dspTempo2); 90 | #else 91 | f(&m_dspTempo); 92 | #endif 93 | f(&m_dspCrossfeed); 94 | f(&m_dspVolume); 95 | f(&m_dspBalance); 96 | f(&m_dspLimiter); 97 | f(&m_dspDither); 98 | } 99 | 100 | bool PushToDevice(DspChunk& chunk, CAMEvent* pFilledEvent); 101 | 102 | AudioDeviceManager m_deviceManager; 103 | std::unique_ptr m_device; 104 | 105 | FILTER_STATE m_state = State_Stopped; 106 | 107 | SampleCorrection m_sampleCorrection; 108 | REFERENCE_TIME m_clockCorrection = 0; 109 | 110 | MyClock& m_myClock; 111 | IReferenceClockPtr m_graphClock; 112 | 113 | std::atomic m_externalClock = false; 114 | std::atomic m_live = false; 115 | std::atomic m_bitstreaming = false; 116 | 117 | SharedWaveFormat m_inputFormat; 118 | 119 | REFERENCE_TIME m_startClockOffset = 0; 120 | REFERENCE_TIME m_startTime = 0; 121 | 122 | CAMEvent m_flush; 123 | 124 | DspMatrix m_dspMatrix; 125 | DspRate m_dspRate; 126 | #ifdef SANEAR_GPL_PHASE_VOCODER 127 | DspTempo m_dspTempo1; 128 | DspTempo2 m_dspTempo2; 129 | #else 130 | DspTempo m_dspTempo; 131 | #endif 132 | DspCrossfeed m_dspCrossfeed; 133 | DspVolume m_dspVolume; 134 | DspBalance m_dspBalance; 135 | DspLimiter m_dspLimiter; 136 | DspDither m_dspDither; 137 | 138 | ISettingsPtr m_settings; 139 | UINT32 m_deviceSettingsSerial = 0; 140 | 141 | uint32_t m_defaultDeviceSerial = 0; 142 | 143 | std::atomic m_volume = 1.0f; 144 | std::atomic m_balance = 0.0f; 145 | double m_rate = 1.0; 146 | 147 | std::atomic m_guidedReclockOffset = 0; 148 | bool m_guidedReclockActive = false; 149 | 150 | size_t m_dropNextFrames = 0; 151 | }; 152 | } 153 | -------------------------------------------------------------------------------- /src/DspBalance.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspBalance.h" 3 | 4 | #include "AudioRenderer.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | bool DspBalance::Active() 9 | { 10 | return m_renderer.GetBalance() != 0.0f; 11 | } 12 | 13 | void DspBalance::Process(DspChunk& chunk) 14 | { 15 | const float balance = m_renderer.GetBalance(); 16 | assert(balance >= -1.0f && balance <= 1.0f); 17 | 18 | if (balance == 0.0f || chunk.IsEmpty() || chunk.GetChannelCount() != 2) 19 | return; 20 | 21 | DspChunk::ToFloat(chunk); 22 | 23 | auto data = reinterpret_cast(chunk.GetData()); 24 | const float gain = std::abs(balance); 25 | for (size_t i = (balance < 0.0f ? 1 : 0), n = chunk.GetSampleCount(); i < n; i += 2) 26 | data[i] *= gain; 27 | } 28 | 29 | void DspBalance::Finish(DspChunk& chunk) 30 | { 31 | Process(chunk); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/DspBalance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class AudioRenderer; 8 | 9 | class DspBalance final 10 | : public DspBase 11 | { 12 | public: 13 | 14 | DspBalance(AudioRenderer& renderer) : m_renderer(renderer) {} 15 | DspBalance(const DspBalance&) = delete; 16 | DspBalance& operator=(const DspBalance&) = delete; 17 | 18 | bool Active() override; 19 | 20 | std::wstring Name() override { return L"Balance"; } 21 | 22 | void Process(DspChunk& chunk) override; 23 | void Finish(DspChunk& chunk) override; 24 | 25 | private: 26 | 27 | const AudioRenderer& m_renderer; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/DspBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspChunk.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class DspBase 8 | { 9 | public: 10 | 11 | virtual ~DspBase() = default; 12 | 13 | virtual std::wstring Name() = 0; 14 | 15 | virtual bool Active() = 0; 16 | 17 | virtual void Process(DspChunk& chunk) = 0; 18 | virtual void Finish(DspChunk& chunk) = 0; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/DspChunk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspFormat.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class DspChunk final 8 | { 9 | public: 10 | 11 | static void ToFormat(DspFormat format, DspChunk& chunk); 12 | static void ToFloat(DspChunk& chunk) { ToFormat(DspFormat::Float, chunk); } 13 | static void ToDouble(DspChunk& chunk) { ToFormat(DspFormat::Double, chunk); } 14 | 15 | static void MergeChunks(DspChunk& chunk, DspChunk& appendage); 16 | 17 | DspChunk(); 18 | DspChunk(DspFormat format, uint32_t channels, size_t frames, uint32_t rate); 19 | DspChunk(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps, const WAVEFORMATEX& sampleFormat); 20 | DspChunk(DspChunk&& other); 21 | DspChunk& operator=(DspChunk&& other); 22 | 23 | bool IsEmpty() const { return m_dataSize == 0; } 24 | 25 | DspFormat GetFormat() const { return m_format; } 26 | uint32_t GetFormatSize() const { return m_formatSize; } 27 | uint32_t GetChannelCount() const { return m_channels; } 28 | uint32_t GetFrameSize() const { return m_formatSize * m_channels; } 29 | uint32_t GetRate() const { return m_rate; } 30 | 31 | size_t GetSize() const { return m_dataSize; } 32 | size_t GetSampleCount() const { assert(m_formatSize); return m_dataSize / m_formatSize; } 33 | size_t GetFrameCount() const { assert(m_channels != 0); return GetSampleCount() / m_channels; } 34 | 35 | char* GetData() { return (m_mediaSample ? m_mediaData : m_data.get()) + m_dataOffset; } 36 | 37 | void PadTail(size_t padFrames); 38 | void PadHead(size_t padFrames); 39 | 40 | void ShrinkTail(size_t toFrames); 41 | void ShrinkHead(size_t toFrames); 42 | 43 | void FreeMediaSample(); 44 | 45 | private: 46 | 47 | void Allocate(); 48 | 49 | IMediaSamplePtr m_mediaSample; 50 | 51 | DspFormat m_format; 52 | uint32_t m_formatSize; 53 | uint32_t m_channels; 54 | uint32_t m_rate; 55 | 56 | size_t m_dataSize; 57 | char* m_mediaData; 58 | std::unique_ptr m_data; 59 | size_t m_dataOffset; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /src/DspCrossfeed.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspCrossfeed.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | void DspCrossfeed::Initialize(ISettings* pSettings, uint32_t rate, uint32_t channels, DWORD mask) 7 | { 8 | assert(pSettings); 9 | m_settings = pSettings; 10 | 11 | m_possible = (channels == 2 && 12 | mask == KSAUDIO_SPEAKER_STEREO && 13 | rate >= BS2B_MINSRATE && 14 | rate <= BS2B_MAXSRATE); 15 | 16 | if (m_possible) 17 | { 18 | m_bs2b.clear(); 19 | m_bs2b.set_srate(rate); 20 | UpdateSettings(); 21 | } 22 | } 23 | 24 | bool DspCrossfeed::Active() 25 | { 26 | return m_active; 27 | } 28 | 29 | void DspCrossfeed::Process(DspChunk& chunk) 30 | { 31 | if (m_settingsSerial != m_settings->GetSerial()) 32 | UpdateSettings(); 33 | 34 | if (!m_active || chunk.IsEmpty()) 35 | return; 36 | 37 | assert(chunk.GetChannelCount() == 2); 38 | 39 | DspChunk::ToFloat(chunk); 40 | 41 | m_bs2b.cross_feed((float*)chunk.GetData(), (int)chunk.GetFrameCount()); 42 | } 43 | 44 | void DspCrossfeed::Finish(DspChunk& chunk) 45 | { 46 | Process(chunk); 47 | } 48 | 49 | void DspCrossfeed::UpdateSettings() 50 | { 51 | m_settingsSerial = m_settings->GetSerial(); 52 | 53 | UINT32 cutoffFrequency; 54 | UINT32 crossfeedLevel; 55 | m_settings->GetCrossfeedSettings(&cutoffFrequency, &crossfeedLevel); 56 | 57 | bool wasActive = m_active; 58 | 59 | BOOL enabled = m_settings->GetCrossfeedEnabled(); 60 | 61 | m_active = m_possible && enabled; 62 | 63 | if (m_active) 64 | { 65 | m_bs2b.set_level_fcut(cutoffFrequency); 66 | m_bs2b.set_level_feed(crossfeedLevel); 67 | } 68 | else if (wasActive) 69 | { 70 | m_bs2b.clear(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/DspCrossfeed.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | #include "Interfaces.h" 5 | 6 | #include 7 | 8 | namespace SaneAudioRenderer 9 | { 10 | class DspCrossfeed final 11 | : public DspBase 12 | { 13 | public: 14 | 15 | DspCrossfeed() = default; 16 | DspCrossfeed(const DspCrossfeed&) = delete; 17 | DspCrossfeed& operator=(const DspCrossfeed&) = delete; 18 | 19 | void Initialize(ISettings* pSettings, uint32_t rate, uint32_t channels, DWORD mask); 20 | 21 | std::wstring Name() override { return L"Crossfeed"; } 22 | 23 | bool Active() override; 24 | 25 | void Process(DspChunk& chunk) override; 26 | void Finish(DspChunk& chunk) override; 27 | 28 | private: 29 | 30 | void UpdateSettings(); 31 | 32 | bs2b_base m_bs2b; 33 | 34 | ISettingsPtr m_settings; 35 | UINT32 m_settingsSerial = 0; 36 | 37 | bool m_possible = false; 38 | bool m_active = false; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /src/DspDither.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspDither.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | void DspDither::Initialize(DspFormat outputFormat) 7 | { 8 | m_enabled = (outputFormat == DspFormat::Pcm16); 9 | m_active = m_enabled; 10 | 11 | for (size_t i = 0; i < 18; i++) 12 | { 13 | m_previous[i] = 0.0f; 14 | m_generator[i].seed((uint32_t)(GetPerformanceCounter() + i)); 15 | m_distributor[i] = std::uniform_real_distribution(0, 1.0f); 16 | } 17 | } 18 | 19 | bool DspDither::Active() 20 | { 21 | return m_enabled && m_active; 22 | } 23 | 24 | void DspDither::Process(DspChunk& chunk) 25 | { 26 | if (!m_enabled || chunk.IsEmpty() || chunk.GetFormatSize() <= DspFormatSize(DspFormat::Pcm16)) 27 | { 28 | m_active = false; 29 | return; 30 | } 31 | 32 | m_active = true; 33 | 34 | DspChunk::ToFloat(chunk); 35 | 36 | DspChunk output(DspFormat::Pcm16, chunk.GetChannelCount(), chunk.GetFrameCount(), chunk.GetRate()); 37 | 38 | auto inputData = reinterpret_cast(chunk.GetData()); 39 | auto outputData = reinterpret_cast(output.GetData()); 40 | const size_t channels = chunk.GetChannelCount(); 41 | 42 | for (size_t frame = 0, frames = chunk.GetFrameCount(); frame < frames; frame++) 43 | { 44 | for (size_t channel = 0; channel < channels; channel++) 45 | { 46 | float inputSample = inputData[frame * channels + channel] * (INT16_MAX - 1); 47 | 48 | // High-pass TPDF, 2 LSB amplitude. 49 | float r = m_distributor[channel](m_generator[channel]); 50 | float noise = r - m_previous[channel]; 51 | m_previous[channel] = r; 52 | 53 | float outputSample = std::round(inputSample + noise); 54 | assert(outputSample >= INT16_MIN && outputSample <= INT16_MAX); 55 | outputData[frame * channels + channel] = (int16_t)outputSample; 56 | } 57 | } 58 | 59 | chunk = std::move(output); 60 | } 61 | 62 | void DspDither::Finish(DspChunk& chunk) 63 | { 64 | Process(chunk); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/DspDither.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class DspDither final 8 | : public DspBase 9 | { 10 | public: 11 | 12 | DspDither() = default; 13 | DspDither(const DspDither&) = delete; 14 | DspDither& operator=(const DspDither&) = delete; 15 | 16 | void Initialize(DspFormat outputFormat); 17 | 18 | std::wstring Name() override { return L"Dither"; } 19 | 20 | bool Active() override; 21 | 22 | void Process(DspChunk& chunk) override; 23 | void Finish(DspChunk& chunk) override; 24 | 25 | private: 26 | 27 | bool m_enabled = false; 28 | bool m_active = false; 29 | std::array m_previous; 30 | std::array m_generator; 31 | std::array, 18> m_distributor; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/DspFormat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SaneAudioRenderer 4 | { 5 | enum class DspFormat 6 | { 7 | Unknown, 8 | Pcm8, 9 | Pcm16, 10 | Pcm24, 11 | Pcm24in32, 12 | Pcm32, 13 | Float, 14 | Double, 15 | }; 16 | 17 | template 18 | struct DspFormatTraits; 19 | 20 | template <> 21 | struct DspFormatTraits 22 | { 23 | typedef int8_t SampleType; 24 | }; 25 | 26 | template <> 27 | struct DspFormatTraits 28 | { 29 | typedef int16_t SampleType; 30 | }; 31 | 32 | #pragma pack(push, 1) 33 | typedef struct { int8_t d[3]; } int24_t; 34 | #pragma pack(pop) 35 | 36 | static_assert(sizeof(int24_t) == 3, "Failed to pack the struct properly"); 37 | 38 | template <> 39 | struct DspFormatTraits 40 | { 41 | typedef int24_t SampleType; 42 | }; 43 | 44 | template <> 45 | struct DspFormatTraits 46 | { 47 | typedef int32_t SampleType; 48 | }; 49 | 50 | template <> 51 | struct DspFormatTraits 52 | { 53 | typedef int32_t SampleType; 54 | }; 55 | 56 | template <> 57 | struct DspFormatTraits 58 | { 59 | typedef float SampleType; 60 | }; 61 | 62 | template <> 63 | struct DspFormatTraits 64 | { 65 | typedef double SampleType; 66 | }; 67 | 68 | static_assert(sizeof(float) == 4, "Floats are not IEEE compliant"); 69 | static_assert(sizeof(double) == 8, "Floats are not IEEE compliant"); 70 | 71 | inline uint32_t DspFormatSize(DspFormat format) 72 | { 73 | return (format == DspFormat::Unknown) ? 0 : 74 | (format == DspFormat::Pcm8) ? 1 : 75 | (format == DspFormat::Pcm16) ? 2 : 76 | (format == DspFormat::Pcm24) ? 3 : 77 | (format == DspFormat::Double) ? 8 : 4; 78 | } 79 | 80 | inline DspFormat DspFormatFromWaveFormat(const WAVEFORMATEX& format) 81 | { 82 | if (format.nSamplesPerSec == 0) 83 | return DspFormat::Unknown; 84 | 85 | if (format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) 86 | { 87 | switch (format.wBitsPerSample) 88 | { 89 | case 32: return DspFormat::Float; 90 | case 64: return DspFormat::Double; 91 | } 92 | } 93 | else if (format.wFormatTag == WAVE_FORMAT_PCM) 94 | { 95 | switch (format.wBitsPerSample) 96 | { 97 | case 8: return DspFormat::Pcm8; 98 | case 16: return DspFormat::Pcm16; 99 | case 24: return DspFormat::Pcm24; 100 | case 32: return DspFormat::Pcm32; 101 | } 102 | } 103 | else if (format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) 104 | { 105 | const WAVEFORMATEXTENSIBLE& formatExtensible = (const WAVEFORMATEXTENSIBLE&)format; 106 | 107 | if (formatExtensible.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) 108 | { 109 | switch (format.wBitsPerSample) 110 | { 111 | case 32: return DspFormat::Float; 112 | case 64: return DspFormat::Double; 113 | } 114 | } 115 | else if (formatExtensible.SubFormat == KSDATAFORMAT_SUBTYPE_PCM) 116 | { 117 | switch (format.wBitsPerSample) 118 | { 119 | case 8: return DspFormat::Pcm8; 120 | case 16: return DspFormat::Pcm16; 121 | case 24: return DspFormat::Pcm24; 122 | case 32: return formatExtensible.Samples.wValidBitsPerSample == 24 ? DspFormat::Pcm24in32 : 123 | DspFormat::Pcm32; 124 | } 125 | } 126 | } 127 | 128 | return DspFormat::Unknown; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/DspLimiter.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspLimiter.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | namespace 7 | { 8 | const float slope = 1.0f - 1.0f / 20.0f; // 20:1 ratio 9 | 10 | template 11 | T GetPeak(const T* data, size_t n) 12 | { 13 | T peak = 0; 14 | 15 | for (size_t i = 0; i < n; i++) 16 | peak = std::max(peak, std::abs(data[i])); 17 | 18 | return peak; 19 | } 20 | 21 | template 22 | void ApplyLimiter(T* data, size_t n, T threshold) 23 | { 24 | for (size_t i = 0; i < n; i++) 25 | { 26 | T& sample = data[i]; 27 | const T absSample = std::abs(sample); 28 | 29 | if (absSample > threshold) 30 | sample *= std::pow(threshold / absSample, slope); 31 | 32 | assert(std::abs(sample) <= 1); 33 | } 34 | } 35 | } 36 | 37 | void DspLimiter::Initialize(uint32_t rate, uint32_t channels, bool exclusive) 38 | { 39 | m_exclusive = exclusive; 40 | m_rate = rate; 41 | m_channels = channels; 42 | 43 | m_active = false; 44 | m_holdWindow = 0; 45 | m_peak = 0.0f; 46 | m_threshold = 0.0f; 47 | } 48 | 49 | bool DspLimiter::Active() 50 | { 51 | return m_active; 52 | } 53 | 54 | void DspLimiter::Process(DspChunk& chunk) 55 | { 56 | if (chunk.IsEmpty()) 57 | return; 58 | 59 | if (!m_exclusive || (chunk.GetFormat() != DspFormat::Float && 60 | chunk.GetFormat() != DspFormat::Double)) 61 | { 62 | m_active = false; 63 | return; 64 | } 65 | 66 | m_active = true; 67 | 68 | // Analyze samples 69 | float peak; 70 | if (chunk.GetFormat() == DspFormat::Double) 71 | { 72 | double largePeak = GetPeak((double*)chunk.GetData(), chunk.GetSampleCount()); 73 | peak = std::nexttoward((float)largePeak, largePeak); 74 | } 75 | else 76 | { 77 | assert(chunk.GetFormat() == DspFormat::Float); 78 | peak = GetPeak((float*)chunk.GetData(), chunk.GetSampleCount()); 79 | } 80 | 81 | // Configure limiter 82 | if (peak > 1.0f) 83 | { 84 | if (m_holdWindow <= 0) 85 | { 86 | NewTreshold(std::max(peak, 1.4f)); 87 | } 88 | else if (peak > m_peak) 89 | { 90 | NewTreshold(peak); 91 | } 92 | 93 | m_holdWindow = (int64_t)m_rate * m_channels * 10; // 10 seconds 94 | } 95 | 96 | // Apply limiter 97 | if (m_holdWindow > 0) 98 | { 99 | if (chunk.GetFormat() == DspFormat::Double) 100 | { 101 | ApplyLimiter((double*)chunk.GetData(), chunk.GetSampleCount(), m_threshold); 102 | } 103 | else 104 | { 105 | assert(chunk.GetFormat() == DspFormat::Float); 106 | ApplyLimiter((float*)chunk.GetData(), chunk.GetSampleCount(), m_threshold); 107 | } 108 | 109 | m_holdWindow -= chunk.GetSampleCount(); 110 | } 111 | } 112 | 113 | void DspLimiter::Finish(DspChunk& chunk) 114 | { 115 | Process(chunk); 116 | } 117 | 118 | void DspLimiter::NewTreshold(float peak) 119 | { 120 | m_peak = peak; 121 | m_threshold = std::pow(1.0f / peak, 1.0f / slope - 1.0f) - 0.0001f; 122 | DebugOut(ClassName(this), "active with", m_peak, "peak and", m_threshold, "threshold"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/DspLimiter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class DspLimiter final 8 | : public DspBase 9 | { 10 | public: 11 | 12 | DspLimiter() = default; 13 | DspLimiter(const DspLimiter&) = delete; 14 | DspLimiter& operator=(const DspLimiter&) = delete; 15 | 16 | void Initialize(uint32_t rate, uint32_t channels, bool exclusive); 17 | 18 | std::wstring Name() override { return L"Limiter"; } 19 | 20 | bool Active() override; 21 | 22 | void Process(DspChunk& chunk) override; 23 | void Finish(DspChunk& chunk) override; 24 | 25 | private: 26 | 27 | void NewTreshold(float peak); 28 | 29 | bool m_exclusive = false; 30 | uint32_t m_rate = 0; 31 | uint32_t m_channels = 0; 32 | 33 | bool m_active = false; 34 | int64_t m_holdWindow = 0; 35 | float m_peak = 0.0f; 36 | float m_threshold = 0.0f; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/DspMatrix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class DspMatrix final 8 | : public DspBase 9 | { 10 | public: 11 | 12 | DspMatrix() = default; 13 | DspMatrix(const DspMatrix&) = delete; 14 | DspMatrix& operator=(const DspMatrix&) = delete; 15 | 16 | void Initialize(uint32_t inputChannels, DWORD inputMask, 17 | uint32_t outputChannels, DWORD outputMask); 18 | 19 | std::wstring Name() override { return L"Matrix"; } 20 | 21 | bool Active() override; 22 | 23 | void Process(DspChunk& chunk) override; 24 | void Finish(DspChunk& chunk) override; 25 | 26 | static DWORD GetChannelMask(const WAVEFORMATEX& format); 27 | static bool IsStereoFormat(const WAVEFORMATEX& format); 28 | 29 | private: 30 | 31 | std::array m_matrix; 32 | bool m_active = false; 33 | uint32_t m_inputChannels = 0; 34 | uint32_t m_outputChannels = 0; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/DspRate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | #include 6 | 7 | namespace SaneAudioRenderer 8 | { 9 | class DspRate final 10 | : public DspBase 11 | { 12 | public: 13 | 14 | DspRate() = default; 15 | DspRate(const DspRate&) = delete; 16 | DspRate& operator=(const DspRate&) = delete; 17 | ~DspRate(); 18 | 19 | void Initialize(bool variable, uint32_t inputRate, uint32_t outputRate, uint32_t channels); 20 | 21 | std::wstring Name() override { return L"Rate"; } 22 | 23 | bool Active() override; 24 | 25 | void Process(DspChunk& chunk) override; 26 | void Finish(DspChunk& chunk) override; 27 | 28 | void Adjust(REFERENCE_TIME time); 29 | 30 | private: 31 | 32 | enum class State 33 | { 34 | Passthrough, 35 | Constant, 36 | Variable, 37 | }; 38 | 39 | DspChunk ProcessChunk(soxr_t soxr, DspChunk& chunk); 40 | DspChunk ProcessEosChunk(soxr_t soxr, DspChunk& chunk); 41 | 42 | void FinishStateTransition(DspChunk& processedChunk, DspChunk& unprocessedChunk, bool eos); 43 | 44 | void CreateBackend(); 45 | soxr_t GetBackend(); 46 | void DestroyBackends(); 47 | 48 | soxr_t m_soxrc = nullptr; 49 | soxr_t m_soxrv = nullptr; 50 | 51 | State m_state = State::Passthrough; 52 | 53 | bool m_inStateTransition = false; 54 | std::pair m_transitionCorrelation; 55 | std::pair m_transitionChunks; 56 | 57 | uint32_t m_inputRate = 0; 58 | uint32_t m_outputRate = 0; 59 | uint32_t m_channels = 0; 60 | 61 | uint64_t m_variableInputFrames = 0; 62 | uint64_t m_variableOutputFrames = 0; 63 | uint64_t m_variableDelay = 0; // In input samples. 64 | 65 | REFERENCE_TIME m_adjustTime = 0; // Negative time - less samples, positive time - more samples. 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/DspTempo.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspTempo.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | void DspTempo::Initialize(double tempo, uint32_t rate, uint32_t channels) 7 | { 8 | m_stouch.clear(); 9 | 10 | m_active = false; 11 | 12 | m_rate = rate; 13 | m_channels = channels; 14 | 15 | m_tempo = tempo; 16 | m_ftempo1 = (float)tempo; 17 | m_ftempo2 = std::nexttoward(m_ftempo1, tempo); 18 | m_ftempo = m_ftempo1; 19 | m_outSamples1 = 0; 20 | m_outSamples2 = 0; 21 | 22 | if (tempo != 1.0) 23 | { 24 | m_stouch.setSampleRate(rate); 25 | m_stouch.setChannels(channels); 26 | 27 | m_stouch.setTempo(m_ftempo); 28 | 29 | //m_stouch.setSetting(SETTING_SEQUENCE_MS, 40); 30 | //m_stouch.setSetting(SETTING_SEEKWINDOW_MS, 15); 31 | //m_stouch.setSetting(SETTING_OVERLAP_MS, 8); 32 | 33 | m_active = true; 34 | } 35 | } 36 | 37 | bool DspTempo::Active() 38 | { 39 | return m_active; 40 | } 41 | 42 | void DspTempo::Process(DspChunk& chunk) 43 | { 44 | if (!m_active || chunk.IsEmpty()) 45 | return; 46 | 47 | assert(chunk.GetRate() == m_rate); 48 | assert(chunk.GetChannelCount() == m_channels); 49 | 50 | // DirectShow speed is in double precision, SoundTouch operates in single. 51 | // We have to adjust it dynamically. 52 | AdjustTempo(); 53 | 54 | DspChunk::ToFloat(chunk); 55 | 56 | m_stouch.putSamples((const float*)chunk.GetData(), (uint32_t)chunk.GetFrameCount()); 57 | 58 | DspChunk output(DspFormat::Float, m_channels, m_stouch.numSamples(), m_rate); 59 | 60 | uint32_t done = m_stouch.receiveSamples((float*)output.GetData(), (uint32_t)output.GetFrameCount()); 61 | assert(done == output.GetFrameCount()); 62 | output.ShrinkTail(done); 63 | 64 | auto& outSamples = (m_ftempo == m_ftempo1) ? m_outSamples1 : m_outSamples2; 65 | outSamples += done; 66 | 67 | chunk = std::move(output); 68 | } 69 | 70 | void DspTempo::Finish(DspChunk& chunk) 71 | { 72 | if (!m_active) 73 | return; 74 | 75 | Process(chunk); 76 | 77 | m_stouch.flush(); 78 | uint32_t undone = m_stouch.numSamples(); 79 | 80 | if (undone > 0) 81 | { 82 | DspChunk output(DspFormat::Float, m_channels, chunk.GetFrameCount() + undone, m_rate); 83 | 84 | if (!chunk.IsEmpty()) 85 | memcpy(output.GetData(), chunk.GetData(), chunk.GetSize()); 86 | 87 | m_stouch.flush(); 88 | 89 | uint32_t done = m_stouch.receiveSamples((float*)output.GetData() + chunk.GetSampleCount(), undone); 90 | assert(done == undone); 91 | output.ShrinkTail(chunk.GetFrameCount() + done); 92 | 93 | chunk = std::move(output); 94 | } 95 | } 96 | 97 | void DspTempo::AdjustTempo() 98 | { 99 | if (m_tempo != m_ftempo) 100 | { 101 | assert(m_tempo != m_ftempo1); 102 | assert(m_tempo != m_ftempo2); 103 | 104 | double ratio21 = std::abs((m_tempo - m_ftempo1) / (m_tempo - m_ftempo2)); 105 | 106 | if (m_ftempo != m_ftempo2 && 107 | m_outSamples1 * ratio21 - m_outSamples2 > 60 * m_rate) 108 | { 109 | DebugOut(ClassName(this), "adjusting for float/double imprecision (2), ratio", ratio21); 110 | m_ftempo = m_ftempo2; 111 | m_stouch.setTempo(m_ftempo); 112 | } 113 | else if (m_ftempo != m_ftempo1 && 114 | m_outSamples2 - m_outSamples1 * ratio21 > 60 * m_rate) 115 | { 116 | DebugOut(ClassName(this), "adjusting for float/double imprecision (1), ratio", ratio21); 117 | m_ftempo = m_ftempo1; 118 | m_stouch.setTempo(m_ftempo); 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/DspTempo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | #include 6 | 7 | namespace SaneAudioRenderer 8 | { 9 | class DspTempo final 10 | : public DspBase 11 | { 12 | public: 13 | 14 | DspTempo() = default; 15 | DspTempo(const DspTempo&) = delete; 16 | DspTempo& operator=(const DspTempo&) = delete; 17 | 18 | void Initialize(double tempo, uint32_t rate, uint32_t channels); 19 | 20 | std::wstring Name() override { return L"Tempo"; } 21 | 22 | bool Active() override; 23 | 24 | void Process(DspChunk& chunk) override; 25 | void Finish(DspChunk& chunk) override; 26 | 27 | private: 28 | 29 | void AdjustTempo(); 30 | 31 | soundtouch::SoundTouch m_stouch; 32 | 33 | bool m_active = false; 34 | 35 | uint32_t m_rate = 0; 36 | uint32_t m_channels = 0; 37 | 38 | double m_tempo = 1.0; 39 | float m_ftempo1 = 1.0f; 40 | float m_ftempo2 = 1.0f; 41 | float m_ftempo = 1.0f; 42 | uint64_t m_outSamples1 = 0; 43 | uint64_t m_outSamples2 = 0; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/DspTempo2.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspTempo2.h" 3 | 4 | #ifndef SANEAR_GPL_PHASE_VOCODER 5 | namespace SaneAudioRenderer { void DspTempo2::ShutNoPublicSymbolsWarning() {} } 6 | #else 7 | 8 | namespace SaneAudioRenderer 9 | { 10 | void DspTempo2::Initialize(double tempo, uint32_t rate, uint32_t channels) 11 | { 12 | m_stretcher = nullptr; 13 | 14 | m_active = false; 15 | m_finish = false; 16 | 17 | m_rate = rate; 18 | m_channels = channels; 19 | 20 | if (tempo != 1.0) 21 | { 22 | try 23 | { 24 | auto options = RubberBand::RubberBandStretcher::OptionTransientsMixed | 25 | RubberBand::RubberBandStretcher::OptionProcessRealTime; 26 | 27 | m_stretcher = std::make_unique(rate, channels, options, 1.0 / tempo); 28 | 29 | m_stretcher->setMaxProcessSize(rate); 30 | 31 | m_active = true; 32 | } 33 | catch (std::bad_alloc&) 34 | { 35 | } 36 | } 37 | } 38 | 39 | bool DspTempo2::Active() 40 | { 41 | return m_active; 42 | } 43 | 44 | void DspTempo2::Process(DspChunk& chunk) 45 | { 46 | if (!m_active || chunk.IsEmpty()) 47 | return; 48 | 49 | assert(chunk.GetRate() == m_rate); 50 | assert(chunk.GetChannelCount() == m_channels); 51 | 52 | DspChunk::ToFloat(chunk); 53 | m_stretcher->process(Deinterleave(chunk).data(), chunk.GetFrameCount(), m_finish); 54 | 55 | size_t outputFrames = m_stretcher->available(); 56 | 57 | if (outputFrames > 0) 58 | { 59 | 60 | DspChunk output(DspFormat::Float, m_channels, outputFrames, m_rate); 61 | 62 | size_t outputDone = m_stretcher->retrieve(MarkData(output).data(), outputFrames); 63 | assert(outputDone == outputFrames); 64 | 65 | Interleave(output); 66 | 67 | chunk = std::move(output); 68 | } 69 | else 70 | { 71 | chunk = DspChunk(); 72 | } 73 | } 74 | 75 | void DspTempo2::Finish(DspChunk& chunk) 76 | { 77 | if (!m_active) 78 | return; 79 | 80 | assert(!m_finish); 81 | m_finish = true; 82 | 83 | Process(chunk); 84 | } 85 | 86 | DspTempo2::DeinterleavedData DspTempo2::MarkData(DspChunk& chunk) 87 | { 88 | assert(!chunk.IsEmpty()); 89 | assert(chunk.GetFormat() == DspFormat::Float); 90 | 91 | DeinterleavedData data = {}; 92 | 93 | for (size_t i = 0; i < m_channels; i++) 94 | data[i] = (float*)(chunk.GetData() + chunk.GetFormatSize() * chunk.GetFrameCount() * i); 95 | 96 | return data; 97 | } 98 | 99 | DspTempo2::DeinterleavedData DspTempo2::Deinterleave(DspChunk& chunk) 100 | { 101 | assert(!chunk.IsEmpty()); 102 | assert(chunk.GetFormat() == DspFormat::Float); 103 | 104 | DspChunk output(DspFormat::Float, m_channels, chunk.GetFrameCount(), m_rate); 105 | DeinterleavedData outputData = MarkData(output); 106 | 107 | float* inputData = (float*)chunk.GetData(); 108 | 109 | for (size_t channel = 0; channel < m_channels; channel++) 110 | for (size_t i = 0, n = chunk.GetFrameCount(); i < n; i++) 111 | outputData[channel][i] = inputData[channel + i * m_channels]; 112 | 113 | chunk = std::move(output); 114 | 115 | return outputData; 116 | } 117 | 118 | void DspTempo2::Interleave(DspChunk& chunk) 119 | { 120 | assert(!chunk.IsEmpty()); 121 | assert(chunk.GetFormat() == DspFormat::Float); 122 | 123 | DspChunk output(DspFormat::Float, m_channels, chunk.GetFrameCount(), m_rate); 124 | float* outputData = (float*)output.GetData(); 125 | 126 | DeinterleavedData inputData = MarkData(chunk); 127 | 128 | for (size_t channel = 0; channel < m_channels; channel++) 129 | for (size_t i = 0, n = chunk.GetFrameCount(); i < n; i++) 130 | outputData[channel + i * m_channels] = inputData[channel][i]; 131 | 132 | chunk = std::move(output); 133 | } 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /src/DspTempo2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef SANEAR_GPL_PHASE_VOCODER 4 | namespace SaneAudioRenderer { struct DspTempo2 final { void ShutNoPublicSymbolsWarning(); }; } 5 | #else 6 | 7 | #include "DspBase.h" 8 | 9 | #include 10 | 11 | namespace SaneAudioRenderer 12 | { 13 | class DspTempo2 final 14 | : public DspBase 15 | { 16 | public: 17 | 18 | DspTempo2() = default; 19 | DspTempo2(const DspTempo2&) = delete; 20 | DspTempo2& operator=(const DspTempo2&) = delete; 21 | 22 | void Initialize(double tempo, uint32_t rate, uint32_t channels); 23 | 24 | std::wstring Name() override { return L"Tempo"; } 25 | 26 | bool Active() override; 27 | 28 | void Process(DspChunk& chunk) override; 29 | void Finish(DspChunk& chunk) override; 30 | 31 | private: 32 | 33 | using DeinterleavedData = std::array; 34 | 35 | DeinterleavedData MarkData(DspChunk& chunk); 36 | DeinterleavedData Deinterleave(DspChunk& chunk); 37 | void Interleave(DspChunk& chunk); 38 | 39 | std::unique_ptr m_stretcher; 40 | 41 | bool m_active = false; 42 | bool m_finish = false; 43 | 44 | uint32_t m_rate = 0; 45 | uint32_t m_channels = 0; 46 | }; 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/DspVolume.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "DspVolume.h" 3 | 4 | #include "AudioRenderer.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | bool DspVolume::Active() 9 | { 10 | return m_renderer.GetVolume() != 1.0f; 11 | } 12 | 13 | void DspVolume::Process(DspChunk& chunk) 14 | { 15 | const float volume = m_renderer.GetVolume(); 16 | assert(volume >= 0.0f && volume <= 1.0f); 17 | 18 | if (volume == 1.0f || chunk.IsEmpty()) 19 | return; 20 | 21 | DspChunk::ToFloat(chunk); 22 | 23 | auto data = reinterpret_cast(chunk.GetData()); 24 | for (size_t i = 0, n = chunk.GetSampleCount(); i < n; i++) 25 | data[i] *= volume; 26 | } 27 | 28 | void DspVolume::Finish(DspChunk& chunk) 29 | { 30 | Process(chunk); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DspVolume.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspBase.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class AudioRenderer; 8 | 9 | class DspVolume final 10 | : public DspBase 11 | { 12 | public: 13 | 14 | DspVolume(AudioRenderer& renderer) : m_renderer(renderer) {} 15 | DspVolume(const DspVolume&) = delete; 16 | DspVolume& operator=(const DspVolume&) = delete; 17 | 18 | std::wstring Name() override { return L"Volume"; } 19 | 20 | bool Active() override; 21 | 22 | void Process(DspChunk& chunk) override; 23 | void Finish(DspChunk& chunk) override; 24 | 25 | private: 26 | 27 | const AudioRenderer& m_renderer; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/Factory.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Factory.h" 3 | 4 | #include "MyFilter.h" 5 | #include "Settings.h" 6 | 7 | namespace SaneAudioRenderer 8 | { 9 | HRESULT Factory::CreateSettings(ISettings** ppOut) 10 | { 11 | IUnknownPtr unknown; 12 | ReturnIfFailed(CreateSettingsAggregated(nullptr, &unknown)); 13 | return unknown->QueryInterface(IID_PPV_ARGS(ppOut)); 14 | } 15 | 16 | HRESULT Factory::CreateSettingsAggregated(IUnknown* pOwner, IUnknown** ppOut) 17 | { 18 | CheckPointer(ppOut, E_POINTER); 19 | 20 | *ppOut = nullptr; 21 | 22 | auto pSettings = new(std::nothrow) Settings(pOwner); 23 | 24 | if (!pSettings) 25 | return E_OUTOFMEMORY; 26 | 27 | pSettings->NonDelegatingAddRef(); 28 | 29 | HRESULT result = pSettings->NonDelegatingQueryInterface(IID_PPV_ARGS(ppOut)); 30 | 31 | pSettings->NonDelegatingRelease(); 32 | 33 | return result; 34 | } 35 | 36 | HRESULT Factory::CreateFilter(ISettings* pSettings, IBaseFilter** ppOut) 37 | { 38 | IUnknownPtr unknown; 39 | ReturnIfFailed(CreateFilterAggregated(nullptr, GetFilterGuid(), pSettings, &unknown)); 40 | return unknown->QueryInterface(IID_PPV_ARGS(ppOut)); 41 | } 42 | 43 | HRESULT Factory::CreateFilterAggregated(IUnknown* pOwner, const GUID& guid, 44 | ISettings* pSettings, IUnknown** ppOut) 45 | { 46 | CheckPointer(ppOut, E_POINTER); 47 | CheckPointer(pSettings, E_POINTER); 48 | 49 | *ppOut = nullptr; 50 | 51 | auto pFilter = new(std::nothrow) MyFilter(pOwner, guid); 52 | 53 | if (!pFilter) 54 | return E_OUTOFMEMORY; 55 | 56 | pFilter->NonDelegatingAddRef(); 57 | 58 | HRESULT result = pFilter->Init(pSettings); 59 | 60 | if (SUCCEEDED(result)) 61 | result = pFilter->NonDelegatingQueryInterface(IID_PPV_ARGS(ppOut)); 62 | 63 | pFilter->NonDelegatingRelease(); 64 | 65 | return result; 66 | } 67 | 68 | const GUID& Factory::GetFilterGuid() 69 | { 70 | static const GUID guid = {0x2AE00773, 0x819A, 0x40FB, {0xA5, 0x54, 0x54, 0x82, 0x7E, 0x11, 0x63, 0x59}}; 71 | return guid; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Interfaces.h" 7 | 8 | namespace SaneAudioRenderer 9 | { 10 | class Factory final 11 | { 12 | public: 13 | 14 | Factory() = delete; 15 | 16 | static HRESULT CreateSettings(ISettings** ppOut); 17 | static HRESULT CreateSettingsAggregated(IUnknown* pOwner, IUnknown** ppOut); 18 | 19 | static HRESULT CreateFilter(ISettings* pSettings, IBaseFilter** ppOut); 20 | static HRESULT CreateFilterAggregated(IUnknown* pOwner, const GUID& guid, 21 | ISettings* pSettings, IUnknown** ppOut); 22 | 23 | static const GUID& GetFilterGuid(); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/Interfaces.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | struct __declspec(uuid("ED41579C-C96A-4D8C-9813-856AB99F405E")) 9 | ISettings : IUnknown 10 | { 11 | STDMETHOD_(UINT32, GetSerial)() = 0; 12 | 13 | enum 14 | { 15 | OUTPUT_DEVICE_BUFFER_MIN_MS = 60, 16 | OUTPUT_DEVICE_BUFFER_MAX_MS = 1000, 17 | OUTPUT_DEVICE_BUFFER_DEFAULT_MS = 200, 18 | }; 19 | STDMETHOD(SetOuputDevice)(LPCWSTR pDeviceId, BOOL bExclusive, UINT32 uBufferMS) = 0; 20 | STDMETHOD(GetOuputDevice)(LPWSTR* ppDeviceId, BOOL* pbExclusive, UINT32* puBufferMS) = 0; 21 | 22 | STDMETHOD_(void, SetAllowBitstreaming)(BOOL bAllowBitstreaming) = 0; 23 | STDMETHOD_(BOOL, GetAllowBitstreaming)() = 0; 24 | 25 | STDMETHOD_(void, SetCrossfeedEnabled)(BOOL bEnable) = 0; 26 | STDMETHOD_(BOOL, GetCrossfeedEnabled)() = 0; 27 | 28 | enum 29 | { 30 | CROSSFEED_CUTOFF_FREQ_MIN = 300, 31 | CROSSFEED_CUTOFF_FREQ_MAX = 2000, 32 | CROSSFEED_CUTOFF_FREQ_CMOY = 700, 33 | CROSSFEED_CUTOFF_FREQ_JMEIER = 650, 34 | CROSSFEED_LEVEL_MIN = 10, 35 | CROSSFEED_LEVEL_MAX = 150, 36 | CROSSFEED_LEVEL_CMOY = 60, 37 | CROSSFEED_LEVEL_JMEIER = 95, 38 | }; 39 | STDMETHOD(SetCrossfeedSettings)(UINT32 uCutoffFrequency, UINT32 uCrossfeedLevel) = 0; 40 | STDMETHOD_(void, GetCrossfeedSettings)(UINT32* puCutoffFrequency, UINT32* puCrossfeedLevel) = 0; 41 | 42 | STDMETHOD_(void, SetIgnoreSystemChannelMixer)(BOOL bEnable) = 0; 43 | STDMETHOD_(BOOL, GetIgnoreSystemChannelMixer)() = 0; 44 | 45 | enum 46 | { 47 | TIMESTRETCH_METHOD_SOLA = 0, 48 | TIMESTRETCH_METHOD_PHASE_VOCODER = 1, 49 | }; 50 | STDMETHOD(SetTimestretchSettings)(UINT32 uTimestretchMethod) = 0; 51 | STDMETHOD_(void, GetTimestretchSettings)(UINT32* puTimestretchMethod) = 0; 52 | }; 53 | _COM_SMARTPTR_TYPEDEF(ISettings, __uuidof(ISettings)); 54 | 55 | struct __declspec(uuid("03481710-D73E-4674-839F-03EDE2D60ED8")) 56 | ISpecifyPropertyPages2 : ISpecifyPropertyPages 57 | { 58 | STDMETHOD(CreatePage)(const GUID& guid, IPropertyPage** ppPage) = 0; 59 | }; 60 | _COM_SMARTPTR_TYPEDEF(ISpecifyPropertyPages2, __uuidof(ISpecifyPropertyPages2)); 61 | } 62 | -------------------------------------------------------------------------------- /src/MyBasicAudio.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "MyBasicAudio.h" 3 | 4 | #include "AudioRenderer.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | MyBasicAudio::MyBasicAudio(IUnknown* pUnknown, AudioRenderer& renderer) 9 | : CBasicAudio(L"SaneAudioRenderer::MyBasicAudio", pUnknown) 10 | , m_renderer(renderer) 11 | { 12 | } 13 | 14 | STDMETHODIMP MyBasicAudio::put_Volume(long volume) 15 | { 16 | if (volume < -10000 || volume > 0) 17 | return E_FAIL; 18 | 19 | float f = (volume == 0) ? 20 | 1.0f : pow(10.0f, (float)volume / 2000.0f); 21 | 22 | m_renderer.SetVolume(f); 23 | 24 | return S_OK; 25 | } 26 | 27 | STDMETHODIMP MyBasicAudio::get_Volume(long* pVolume) 28 | { 29 | CheckPointer(pVolume, E_POINTER); 30 | 31 | float f = m_renderer.GetVolume(); 32 | 33 | *pVolume = (f == 1.0f) ? 34 | 0 : (long)(log10(f) * 2000.0f); 35 | 36 | assert(*pVolume <= 0 && *pVolume >= -10000); 37 | 38 | return S_OK; 39 | } 40 | 41 | STDMETHODIMP MyBasicAudio::put_Balance(long balance) 42 | { 43 | if (balance < -10000 || balance > 10000) 44 | return E_FAIL; 45 | 46 | float f = (balance == 0) ? 47 | 0.0f : pow(10.0f, (float)abs(balance) / -2000.0f); 48 | 49 | m_renderer.SetBalance(copysign(f, (float)balance)); 50 | 51 | return S_OK; 52 | } 53 | 54 | STDMETHODIMP MyBasicAudio::get_Balance(long* pBalance) 55 | { 56 | CheckPointer(pBalance, E_POINTER); 57 | 58 | float f = m_renderer.GetBalance(); 59 | 60 | *pBalance = (f == 0.0f) ? 61 | 0 : (long)(copysign(log10(abs(f)), f) * 2000.0f); 62 | 63 | assert(*pBalance >= -10000 && *pBalance <= 10000); 64 | 65 | return S_OK; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/MyBasicAudio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SaneAudioRenderer 4 | { 5 | class AudioRenderer; 6 | 7 | class MyBasicAudio final 8 | : public CBasicAudio 9 | { 10 | public: 11 | 12 | MyBasicAudio(IUnknown* pUnknown, AudioRenderer& renderer); 13 | MyBasicAudio(const MyBasicAudio&) = delete; 14 | MyBasicAudio& operator=(const MyBasicAudio&) = delete; 15 | 16 | STDMETHODIMP put_Volume(long volume) override; 17 | STDMETHODIMP get_Volume(long* pVolume) override; 18 | STDMETHODIMP put_Balance(long balance) override; 19 | STDMETHODIMP get_Balance(long* pBalance) override; 20 | 21 | private: 22 | 23 | AudioRenderer& m_renderer; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/MyClock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../IGuidedReclock.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class AudioRenderer; 8 | 9 | class MyClock final 10 | : public CBaseReferenceClock 11 | , public IGuidedReclock 12 | { 13 | public: 14 | 15 | MyClock(IUnknown* pUnknown, const std::unique_ptr& renderer, HRESULT& result); 16 | MyClock(const MyClock&) = delete; 17 | MyClock& operator=(const MyClock&) = delete; 18 | 19 | DECLARE_IUNKNOWN 20 | 21 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv) override; 22 | 23 | REFERENCE_TIME GetPrivateTime() override; 24 | 25 | void SlaveClockToAudio(IAudioClock* pAudioClock, int64_t audioStart); 26 | void UnslaveClockFromAudio(); 27 | void OffsetAudioClock(REFERENCE_TIME offsetTime); 28 | HRESULT GetAudioClockTime(REFERENCE_TIME* pAudioTime, REFERENCE_TIME* pCounterTime); 29 | HRESULT GetAudioClockStartTime(REFERENCE_TIME* pStartTime); 30 | 31 | STDMETHODIMP SlaveClock(DOUBLE multiplier) override; 32 | STDMETHODIMP UnslaveClock() override; 33 | STDMETHODIMP OffsetClock(LONGLONG offset) override; 34 | STDMETHODIMP GetImmediateTime(LONGLONG* pTime) override; 35 | 36 | private: 37 | 38 | bool CanDoGuidedReclock(); 39 | 40 | int64_t GetCounterTime() { return llMulDiv(GetPerformanceCounter(), OneSecond, m_performanceFrequency, 0); } 41 | 42 | const std::unique_ptr& m_renderer; 43 | 44 | const int64_t m_performanceFrequency; 45 | 46 | IAudioClockPtr m_audioClock; 47 | int64_t m_audioStart = 0; 48 | uint64_t m_audioInitialPosition = 0; 49 | int64_t m_audioOffset = 0; 50 | int64_t m_counterOffset = 0; 51 | 52 | bool m_guidedReclockSlaving = false; 53 | double m_guidedReclockMultiplier = 1.0; 54 | int64_t m_guidedReclockStartTime = 0; 55 | int64_t m_guidedReclockStartClock = 0; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/MyFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Interfaces.h" 4 | #include "MyPropertyPage.h" 5 | 6 | namespace SaneAudioRenderer 7 | { 8 | class MyClock; 9 | class AudioRenderer; 10 | class MyBasicAudio; 11 | class MyPin; 12 | 13 | class MyFilter final 14 | : public CCritSec 15 | , public CBaseFilter 16 | , public ISpecifyPropertyPages2 17 | , public IStatusPageData 18 | { 19 | public: 20 | 21 | MyFilter(IUnknown* pUnknown, REFIID guid); 22 | MyFilter(const MyFilter&) = delete; 23 | MyFilter& operator=(const MyFilter&) = delete; 24 | 25 | HRESULT Init(ISettings* pSettings); 26 | 27 | DECLARE_IUNKNOWN 28 | 29 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv) override; 30 | 31 | int GetPinCount() override; 32 | CBasePin* GetPin(int n) override; 33 | 34 | STDMETHODIMP Stop() override; 35 | STDMETHODIMP Pause() override; 36 | STDMETHODIMP Run(REFERENCE_TIME startTime) override; 37 | 38 | STDMETHODIMP GetState(DWORD timeoutMilliseconds, FILTER_STATE* pState) override; 39 | 40 | STDMETHODIMP SetSyncSource(IReferenceClock* pClock) override; 41 | 42 | STDMETHODIMP GetPages(CAUUID* pPages) override; 43 | STDMETHODIMP CreatePage(const GUID& guid, IPropertyPage** ppPage) override; 44 | 45 | STDMETHODIMP GetPageData(bool resize, std::vector& data) override; 46 | 47 | private: 48 | 49 | template 50 | STDMETHODIMP ChangeState(PinFunction pinFunction); 51 | 52 | std::unique_ptr m_clock; 53 | //IReferenceClockPtr m_testClock; 54 | CAMEvent m_bufferFilled; 55 | std::unique_ptr m_renderer; 56 | std::unique_ptr m_basicAudio; 57 | std::unique_ptr m_pin; 58 | IUnknownPtr m_seeking; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/MyPin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SaneAudioRenderer 4 | { 5 | class AudioRenderer; 6 | 7 | class MyPin final 8 | : public CCritSec 9 | , public CBaseInputPin 10 | { 11 | public: 12 | 13 | MyPin(AudioRenderer& renderer, CBaseFilter* pFilter, HRESULT& result); 14 | MyPin(const MyPin&) = delete; 15 | MyPin& operator=(const MyPin&) = delete; 16 | 17 | HRESULT CheckMediaType(const CMediaType* pmt) override; 18 | HRESULT SetMediaType(const CMediaType* pmt) override; 19 | HRESULT CheckConnect(IPin* pPin) override; 20 | 21 | STDMETHODIMP NewSegment(REFERENCE_TIME startTime, REFERENCE_TIME stopTime, double rate) override; 22 | STDMETHODIMP ReceiveCanBlock() override { return S_OK; } 23 | STDMETHODIMP Receive(IMediaSample* pSample) override; 24 | STDMETHODIMP EndOfStream() override; 25 | 26 | STDMETHODIMP BeginFlush() override; 27 | STDMETHODIMP EndFlush() override; 28 | 29 | HRESULT Active() override; 30 | HRESULT Run(REFERENCE_TIME startTime) override; 31 | HRESULT Inactive() override; 32 | 33 | bool StateTransitionFinished(uint32_t timeoutMilliseconds); 34 | 35 | private: 36 | 37 | bool CheckLive(IPin* pPin); 38 | 39 | FILTER_STATE m_state = State_Stopped; 40 | bool m_eosUp = false; 41 | bool m_eosDown = false; 42 | 43 | bool m_live = false; 44 | 45 | CCritSec m_receiveMutex; 46 | HANDLE m_hReceiveThread = NULL; 47 | 48 | CAMEvent m_bufferFilled; 49 | AudioRenderer& m_renderer; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/MyPropertyPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SaneAudioRenderer 4 | { 5 | class AudioDevice; 6 | 7 | // NOTE: This is internal interface and shouldn't be used outside of Sanear. 8 | struct __declspec(uuid("361657BC-CC1E-420A-BE7B-21C34E3D9F76")) 9 | IStatusPageData : IUnknown 10 | { 11 | STDMETHOD(GetPageData)(bool resize, std::vector& data) = 0; 12 | }; 13 | _COM_SMARTPTR_TYPEDEF(IStatusPageData, __uuidof(IStatusPageData)); 14 | 15 | class _declspec(uuid("7EEEDEC8-8B8E-4220-AF12-08BC0CE844F0")) 16 | MyPropertyPage final 17 | : public CUnknown 18 | , public IPropertyPage 19 | { 20 | public: 21 | 22 | static std::vector CreateDialogData(bool resize, SharedWaveFormat inputFormat, const AudioDevice* device, 23 | std::vector processors, bool externalClock, bool live, 24 | bool guidedReclock); 25 | 26 | MyPropertyPage(); 27 | MyPropertyPage(HRESULT& result, IStatusPageData* pData); 28 | MyPropertyPage(const MyPropertyPage&) = delete; 29 | MyPropertyPage& operator=(const MyPropertyPage&) = delete; 30 | 31 | DECLARE_IUNKNOWN 32 | 33 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv) override; 34 | 35 | STDMETHODIMP SetPageSite(IPropertyPageSite* pPageSite) override; 36 | STDMETHODIMP Activate(HWND hParent, LPCRECT pRect, BOOL bModal) override; 37 | STDMETHODIMP Deactivate() override; 38 | STDMETHODIMP GetPageInfo(PROPPAGEINFO* pPageInfo) override; 39 | STDMETHODIMP SetObjects(ULONG nObjects, IUnknown** ppUnk) override; 40 | STDMETHODIMP Show(UINT cmdShow) override; 41 | STDMETHODIMP Move(LPCRECT pRect) override; 42 | STDMETHODIMP IsPageDirty() override { return S_FALSE; } 43 | STDMETHODIMP Apply() override { return S_OK; } 44 | STDMETHODIMP Help(LPCOLESTR) override { return E_NOTIMPL; } 45 | STDMETHODIMP TranslateAccelerator(MSG*) override { return E_NOTIMPL; } 46 | 47 | private: 48 | 49 | const bool m_delayedData; 50 | std::vector m_dialogData; 51 | IPropertyPageSitePtr m_pageSite; 52 | HWND m_hWindow = NULL; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/MyTestClock.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "MyTestClock.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | MyTestClock::MyTestClock(IUnknown* pUnknown, HRESULT& result) 7 | : CBaseReferenceClock(L"SaneAudioRenderer::MyTestClock", pUnknown, &result) 8 | , m_performanceFrequency(GetPerformanceFrequency()) 9 | { 10 | } 11 | 12 | REFERENCE_TIME MyTestClock::GetPrivateTime() 13 | { 14 | return llMulDiv(GetPerformanceCounter(), OneSecond, m_performanceFrequency, 0); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MyTestClock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SaneAudioRenderer 4 | { 5 | class MyTestClock final 6 | : public CBaseReferenceClock 7 | { 8 | public: 9 | 10 | MyTestClock(IUnknown* pUnknown, HRESULT& result); 11 | MyTestClock(const MyTestClock&) = delete; 12 | MyTestClock& operator=(const MyTestClock&) = delete; 13 | 14 | REFERENCE_TIME GetPrivateTime() override; 15 | 16 | private: 17 | 18 | const int64_t m_performanceFrequency; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/SampleCorrection.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "SampleCorrection.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | void SampleCorrection::NewFormat(SharedWaveFormat format) 7 | { 8 | assert(format); 9 | assert(format->nSamplesPerSec > 0); 10 | 11 | if (m_format) 12 | { 13 | m_segmentTimeInPreviousFormats += FramesToTime(m_segmentFramesInCurrentFormat); 14 | m_segmentFramesInCurrentFormat = 0; 15 | } 16 | 17 | m_format = format; 18 | m_bitstream = (DspFormatFromWaveFormat(*m_format) == DspFormat::Unknown); 19 | } 20 | 21 | void SampleCorrection::NewSegment(double rate) 22 | { 23 | assert(rate > 0.0); 24 | 25 | m_rate = rate; 26 | 27 | m_segmentTimeInPreviousFormats = 0; 28 | m_segmentFramesInCurrentFormat = 0; 29 | 30 | m_lastFrameEnd = 0; 31 | 32 | m_timeDivergence = 0; 33 | } 34 | 35 | void SampleCorrection::NewDeviceBuffer() 36 | { 37 | m_freshBuffer = true; 38 | } 39 | 40 | DspChunk SampleCorrection::ProcessSample(IMediaSample* pSample, AM_SAMPLE2_PROPERTIES& sampleProps, bool realtimeDevice) 41 | { 42 | assert(m_format); 43 | 44 | DspChunk chunk(pSample, sampleProps, *m_format); 45 | 46 | if (m_bitstream) 47 | { 48 | if (m_freshBuffer && !(sampleProps.dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) 49 | { 50 | // Drop the sample. 51 | DebugOut(ClassName(this), "drop [", sampleProps.tStart, sampleProps.tStop, "]"); 52 | chunk = DspChunk(); 53 | assert(chunk.IsEmpty()); 54 | } 55 | } 56 | else if (!realtimeDevice && (m_lastFrameEnd == 0 || (sampleProps.dwSampleFlags & AM_SAMPLE_TIMEDISCONTINUITY))) 57 | { 58 | if ((sampleProps.dwSampleFlags & AM_SAMPLE_STOPVALID) && sampleProps.tStop <= m_lastFrameEnd) 59 | { 60 | // Drop the sample. 61 | DebugOut(ClassName(this), "drop [", sampleProps.tStart, sampleProps.tStop, "]"); 62 | chunk = DspChunk(); 63 | assert(chunk.IsEmpty()); 64 | } 65 | else if ((sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) && sampleProps.tStart < m_lastFrameEnd) 66 | { 67 | // Crop the sample. 68 | const size_t cropFrames = (size_t)TimeToFrames(m_lastFrameEnd - sampleProps.tStart); 69 | 70 | if (cropFrames > 0) 71 | { 72 | DebugOut(ClassName(this), "crop", cropFrames, "frames from [", 73 | sampleProps.tStart, sampleProps.tStop, "]"); 74 | 75 | chunk.ShrinkHead(chunk.GetFrameCount() > cropFrames ? chunk.GetFrameCount() - cropFrames : 0); 76 | 77 | sampleProps.tStart += FramesToTime(cropFrames); 78 | } 79 | } 80 | else if ((sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) && sampleProps.tStart > m_lastFrameEnd) 81 | { 82 | // Zero-pad the sample. 83 | const size_t padFrames = (size_t)TimeToFrames(sampleProps.tStart - m_lastFrameEnd); 84 | 85 | if (padFrames > 0 && 86 | FramesToTime(padFrames) < 10 * OneSecond) 87 | { 88 | DebugOut(ClassName(this), "pad", padFrames, "frames before [", 89 | sampleProps.tStart, sampleProps.tStop, "]"); 90 | 91 | chunk.PadHead(padFrames); 92 | 93 | sampleProps.tStart -= FramesToTime(padFrames); 94 | } 95 | } 96 | } 97 | 98 | AccumulateTimings(sampleProps, chunk.GetFrameCount()); 99 | 100 | return chunk; 101 | } 102 | 103 | uint64_t SampleCorrection::TimeToFrames(REFERENCE_TIME time) 104 | { 105 | assert(m_format); 106 | assert(m_rate > 0.0); 107 | return (size_t)(llMulDiv(time, m_format->nSamplesPerSec, OneSecond, 0) * m_rate); 108 | } 109 | 110 | REFERENCE_TIME SampleCorrection::FramesToTime(uint64_t frames) 111 | { 112 | assert(m_format); 113 | assert(m_rate > 0.0); 114 | return (REFERENCE_TIME)(llMulDiv(frames, OneSecond, m_format->nSamplesPerSec, 0) / m_rate); 115 | } 116 | 117 | void SampleCorrection::AccumulateTimings(AM_SAMPLE2_PROPERTIES& sampleProps, size_t frames) 118 | { 119 | assert(m_format); 120 | assert(m_rate > 0.0); 121 | 122 | if (frames == 0) 123 | return; 124 | 125 | if (sampleProps.dwSampleFlags & AM_SAMPLE_TIMEVALID) 126 | m_timeDivergence = sampleProps.tStart - m_lastFrameEnd; 127 | 128 | m_segmentFramesInCurrentFormat += frames; 129 | 130 | m_lastFrameEnd = m_segmentTimeInPreviousFormats + FramesToTime(m_segmentFramesInCurrentFormat); 131 | 132 | m_freshBuffer = false; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/SampleCorrection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DspChunk.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class SampleCorrection final 8 | { 9 | public: 10 | 11 | SampleCorrection() = default; 12 | 13 | void NewFormat(SharedWaveFormat format); 14 | void NewSegment(double rate); 15 | void NewDeviceBuffer(); 16 | 17 | DspChunk ProcessSample(IMediaSample* pSample, AM_SAMPLE2_PROPERTIES& sampleProps, bool realtimeDevice); 18 | 19 | REFERENCE_TIME GetLastFrameEnd() const { return m_lastFrameEnd; } 20 | REFERENCE_TIME GetTimeDivergence() const { return m_timeDivergence; } 21 | 22 | private: 23 | 24 | void AccumulateTimings(AM_SAMPLE2_PROPERTIES& sampleProps, size_t frames); 25 | 26 | uint64_t TimeToFrames(REFERENCE_TIME time); 27 | REFERENCE_TIME FramesToTime(uint64_t frames); 28 | 29 | SharedWaveFormat m_format; 30 | bool m_bitstream = false; 31 | 32 | double m_rate = 1.0; 33 | 34 | REFERENCE_TIME m_segmentTimeInPreviousFormats = 0; 35 | uint64_t m_segmentFramesInCurrentFormat = 0; 36 | 37 | REFERENCE_TIME m_lastFrameEnd = 0; 38 | 39 | REFERENCE_TIME m_timeDivergence = 0; 40 | 41 | bool m_freshBuffer = true; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/Settings.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Settings.h" 3 | 4 | namespace SaneAudioRenderer 5 | { 6 | Settings::Settings(IUnknown* pUnknown) 7 | : CUnknown("Audio Renderer Settings", pUnknown) 8 | { 9 | } 10 | 11 | STDMETHODIMP Settings::NonDelegatingQueryInterface(REFIID riid, void** ppv) 12 | { 13 | return (riid == __uuidof(ISettings)) ? 14 | GetInterface(static_cast(this), ppv) : 15 | CUnknown::NonDelegatingQueryInterface(riid, ppv); 16 | } 17 | 18 | STDMETHODIMP_(UINT32) Settings::GetSerial() 19 | { 20 | return m_serial; 21 | } 22 | 23 | STDMETHODIMP Settings::SetOuputDevice(LPCWSTR pDeviceId, BOOL bExclusive, UINT32 uBufferMS) 24 | { 25 | if (uBufferMS < OUTPUT_DEVICE_BUFFER_MIN_MS || uBufferMS > OUTPUT_DEVICE_BUFFER_MAX_MS) 26 | return E_INVALIDARG; 27 | 28 | CAutoLock lock(this); 29 | 30 | if (m_exclusive != bExclusive || 31 | m_buffer != uBufferMS || 32 | (pDeviceId && m_deviceId != pDeviceId) || 33 | (!pDeviceId && !m_deviceId.empty())) 34 | { 35 | try 36 | { 37 | m_deviceId = pDeviceId ? pDeviceId : L""; 38 | m_exclusive = bExclusive; 39 | m_buffer = uBufferMS; 40 | m_serial++; 41 | } 42 | catch (std::bad_alloc&) 43 | { 44 | return E_OUTOFMEMORY; 45 | } 46 | } 47 | 48 | return S_OK; 49 | } 50 | 51 | STDMETHODIMP Settings::GetOuputDevice(LPWSTR* ppDeviceId, BOOL* pbExclusive, UINT32* puBufferMS) 52 | { 53 | CAutoLock lock(this); 54 | 55 | if (pbExclusive) 56 | *pbExclusive = m_exclusive; 57 | 58 | if (ppDeviceId) 59 | { 60 | size_t size = sizeof(wchar_t) * (m_deviceId.length() + 1); 61 | 62 | *ppDeviceId = static_cast(CoTaskMemAlloc(size)); 63 | 64 | if (!*ppDeviceId) 65 | return E_OUTOFMEMORY; 66 | 67 | memcpy(*ppDeviceId, m_deviceId.c_str(), size); 68 | } 69 | 70 | if (puBufferMS) 71 | *puBufferMS = m_buffer; 72 | 73 | return S_OK; 74 | } 75 | 76 | STDMETHODIMP_(void) Settings::SetAllowBitstreaming(BOOL bAllowBitstreaming) 77 | { 78 | CAutoLock lock(this); 79 | 80 | if (m_allowBitstreaming != bAllowBitstreaming) 81 | { 82 | m_allowBitstreaming = bAllowBitstreaming; 83 | m_serial++; 84 | } 85 | } 86 | 87 | STDMETHODIMP_(BOOL) Settings::GetAllowBitstreaming() 88 | { 89 | CAutoLock lock(this); 90 | 91 | return m_allowBitstreaming; 92 | } 93 | 94 | STDMETHODIMP_(void) Settings::SetCrossfeedEnabled(BOOL bEnable) 95 | { 96 | CAutoLock lock(this); 97 | 98 | if (m_crossfeedEnabled != bEnable) 99 | { 100 | m_crossfeedEnabled = bEnable; 101 | m_serial++; 102 | } 103 | } 104 | 105 | STDMETHODIMP_(BOOL) Settings::GetCrossfeedEnabled() 106 | { 107 | CAutoLock lock(this); 108 | 109 | return m_crossfeedEnabled; 110 | } 111 | 112 | STDMETHODIMP Settings::SetCrossfeedSettings(UINT32 uCutoffFrequency, UINT32 uCrossfeedLevel) 113 | { 114 | if (uCutoffFrequency < CROSSFEED_CUTOFF_FREQ_MIN || 115 | uCutoffFrequency > CROSSFEED_CUTOFF_FREQ_MAX || 116 | uCrossfeedLevel < CROSSFEED_LEVEL_MIN || 117 | uCrossfeedLevel > CROSSFEED_LEVEL_MAX) 118 | { 119 | return E_INVALIDARG; 120 | } 121 | 122 | CAutoLock lock(this); 123 | 124 | if (m_crossfeedCutoffFrequency != uCutoffFrequency || 125 | m_crossfeedLevel != uCrossfeedLevel) 126 | { 127 | m_crossfeedCutoffFrequency = uCutoffFrequency; 128 | m_crossfeedLevel = uCrossfeedLevel; 129 | m_serial++; 130 | } 131 | 132 | return S_OK; 133 | } 134 | 135 | STDMETHODIMP_(void) Settings::GetCrossfeedSettings(UINT32* puCutoffFrequency, UINT32* puCrossfeedLevel) 136 | { 137 | CAutoLock lock(this); 138 | 139 | if (puCutoffFrequency) 140 | *puCutoffFrequency = m_crossfeedCutoffFrequency; 141 | 142 | if (puCrossfeedLevel) 143 | *puCrossfeedLevel = m_crossfeedLevel; 144 | } 145 | 146 | STDMETHODIMP_(void) Settings::SetIgnoreSystemChannelMixer(BOOL bEnable) 147 | { 148 | CAutoLock lock(this); 149 | 150 | if (m_ignoreSystemChannelMixer != bEnable) 151 | { 152 | m_ignoreSystemChannelMixer = bEnable; 153 | m_serial++; 154 | } 155 | } 156 | 157 | STDMETHODIMP_(BOOL) Settings::GetIgnoreSystemChannelMixer() 158 | { 159 | CAutoLock lock(this); 160 | 161 | return m_ignoreSystemChannelMixer; 162 | } 163 | 164 | STDMETHODIMP Settings::SetTimestretchSettings(UINT32 uTimestretchMethod) 165 | { 166 | if (uTimestretchMethod != TIMESTRETCH_METHOD_SOLA && 167 | uTimestretchMethod != TIMESTRETCH_METHOD_PHASE_VOCODER) 168 | { 169 | return E_INVALIDARG; 170 | } 171 | 172 | #ifndef SANEAR_GPL_PHASE_VOCODER 173 | if (uTimestretchMethod == TIMESTRETCH_METHOD_PHASE_VOCODER) 174 | return E_NOTIMPL; 175 | #endif 176 | 177 | CAutoLock lock(this); 178 | 179 | if (uTimestretchMethod != m_timestretchMethod) 180 | { 181 | m_timestretchMethod = uTimestretchMethod; 182 | m_serial++; 183 | } 184 | 185 | return S_OK; 186 | } 187 | 188 | STDMETHODIMP_(void) Settings::GetTimestretchSettings(UINT32* puTimestretchMethod) 189 | { 190 | CAutoLock lock(this); 191 | 192 | if (puTimestretchMethod) 193 | *puTimestretchMethod = m_timestretchMethod; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Interfaces.h" 4 | 5 | namespace SaneAudioRenderer 6 | { 7 | class Settings final 8 | : public CUnknown 9 | , public ISettings 10 | , private CCritSec 11 | { 12 | public: 13 | 14 | DECLARE_IUNKNOWN 15 | 16 | Settings(IUnknown* pUnknown); 17 | Settings(const Settings&) = delete; 18 | Settings& operator=(const Settings&) = delete; 19 | 20 | STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv) override; 21 | 22 | STDMETHODIMP_(UINT32) GetSerial() override; 23 | 24 | STDMETHODIMP SetOuputDevice(LPCWSTR pDeviceId, BOOL bExclusive, UINT32 uBufferMS) override; 25 | STDMETHODIMP GetOuputDevice(LPWSTR* ppDeviceId, BOOL* pbExclusive, UINT32* puBufferMS) override; 26 | 27 | STDMETHODIMP_(void) SetAllowBitstreaming(BOOL bAllowBitstreaming) override; 28 | STDMETHODIMP_(BOOL) GetAllowBitstreaming() override; 29 | 30 | STDMETHODIMP_(void) SetCrossfeedEnabled(BOOL bEnable) override; 31 | STDMETHODIMP_(BOOL) GetCrossfeedEnabled() override; 32 | 33 | STDMETHODIMP SetCrossfeedSettings(UINT32 uCutoffFrequency, UINT32 uCrossfeedLevel) override; 34 | STDMETHODIMP_(void) GetCrossfeedSettings(UINT32* puCutoffFrequency, UINT32* puCrossfeedLevel) override; 35 | 36 | STDMETHODIMP_(void) SetIgnoreSystemChannelMixer(BOOL bEnable) override; 37 | STDMETHODIMP_(BOOL) GetIgnoreSystemChannelMixer() override; 38 | 39 | STDMETHODIMP SetTimestretchSettings(UINT32 uTimestretchMethod) override; 40 | STDMETHODIMP_(void) GetTimestretchSettings(UINT32* puTimestretchMethod) override; 41 | 42 | private: 43 | 44 | std::atomic m_serial = 0; 45 | 46 | std::wstring m_deviceId; 47 | BOOL m_exclusive = FALSE; 48 | UINT32 m_buffer = OUTPUT_DEVICE_BUFFER_DEFAULT_MS; 49 | 50 | BOOL m_allowBitstreaming = TRUE; 51 | 52 | BOOL m_sharedModePeakLimiterEnabled = FALSE; 53 | 54 | BOOL m_crossfeedEnabled = FALSE; 55 | UINT32 m_crossfeedCutoffFrequency = CROSSFEED_CUTOFF_FREQ_CMOY; 56 | UINT32 m_crossfeedLevel = CROSSFEED_LEVEL_CMOY; 57 | 58 | BOOL m_ignoreSystemChannelMixer = TRUE; 59 | 60 | UINT32 m_timestretchMethod = 61 | #ifdef SANEAR_GPL_PHASE_VOCODER 62 | TIMESTRETCH_METHOD_PHASE_VOCODER; 63 | #else 64 | TIMESTRETCH_METHOD_SOLA; 65 | #endif 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /src/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /src/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NOMINMAX 4 | # define NOMINMAX 5 | #endif 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "Utils.h" 34 | 35 | namespace SaneAudioRenderer 36 | { 37 | _COM_SMARTPTR_TYPEDEF(IGlobalInterfaceTable, __uuidof(IGlobalInterfaceTable)); 38 | 39 | _COM_SMARTPTR_TYPEDEF(IMMDeviceEnumerator, __uuidof(IMMDeviceEnumerator)); 40 | _COM_SMARTPTR_TYPEDEF(IMMDeviceCollection, __uuidof(IMMDeviceCollection)); 41 | _COM_SMARTPTR_TYPEDEF(IMMDevice, __uuidof(IMMDevice)); 42 | _COM_SMARTPTR_TYPEDEF(IMMNotificationClient, __uuidof(IMMNotificationClient)); 43 | 44 | _COM_SMARTPTR_TYPEDEF(IAudioClient, __uuidof(IAudioClient)); 45 | _COM_SMARTPTR_TYPEDEF(IAudioRenderClient, __uuidof(IAudioRenderClient)); 46 | _COM_SMARTPTR_TYPEDEF(IAudioClock, __uuidof(IAudioClock)); 47 | _COM_SMARTPTR_TYPEDEF(IPropertyStore, __uuidof(IPropertyStore)); 48 | 49 | _COM_SMARTPTR_TYPEDEF(IMediaSample, __uuidof(IMediaSample)); 50 | _COM_SMARTPTR_TYPEDEF(IPropertyPageSite, __uuidof(IPropertyPageSite)); 51 | _COM_SMARTPTR_TYPEDEF(IReferenceClock, __uuidof(IReferenceClock)); 52 | _COM_SMARTPTR_TYPEDEF(IAMGraphStreams, __uuidof(IAMGraphStreams)); 53 | _COM_SMARTPTR_TYPEDEF(IAMPushSource, __uuidof(IAMPushSource)); 54 | } 55 | --------------------------------------------------------------------------------