1 /*-========================================================================-_
2  |                                 - XAPO -                                 |
3  |        Copyright (c) Microsoft Corporation.  All rights reserved.        |
4  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
5  |PROJECT: XAPO                         MODEL:   Unmanaged User-mode        |
6  |VERSION: 1.0                          EXCEPT:  No Exceptions              |
7  |CLASS:   N / A                        MINREQ:  WinXP, Xbox360             |
8  |BASE:    N / A                        DIALECT: MSC++ 14.00                |
9  |>------------------------------------------------------------------------<|
10  | DUTY: XAPO base classes                                                  |
11  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
12   NOTES:
13     1.  See XAPO.h for the rules governing XAPO interface behaviour.        */
14 
15 #ifdef _MSC_VER
16 #pragma once
17 #endif
18 
19 //--------------<D-E-F-I-N-I-T-I-O-N-S>-------------------------------------//
20 #include "XAPO.h"
21 
22 // default audio format ranges supported, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS.pFormat
23 #define XAPOBASE_DEFAULT_FORMAT_TAG           WAVE_FORMAT_IEEE_FLOAT // 32-bit float only, applies to WAVEFORMATEX.wFormatTag or WAVEFORMATEXTENSIBLE.SubFormat when used
24 #define XAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS  XAPO_MIN_CHANNELS      // minimum channel count, applies to WAVEFORMATEX.nChannels
25 #define XAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS  XAPO_MAX_CHANNELS      // maximum channel count, applies to WAVEFORMATEX.nChannels
26 #define XAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE XAPO_MIN_FRAMERATE     // minimum framerate, applies to WAVEFORMATEX.nSamplesPerSec
27 #define XAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE XAPO_MAX_FRAMERATE     // maximum framerate, applies to WAVEFORMATEX.nSamplesPerSec
28 #define XAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE 32                     // 32-bit float only, applies to WAVEFORMATEX.wBitsPerSample and WAVEFORMATEXTENSIBLE.wValidBitsPerSample when used
29 
30 // default XAPO property flags supported, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS
31 #define XAPOBASE_DEFAULT_FLAG (XAPO_FLAG_CHANNELS_MUST_MATCH | XAPO_FLAG_FRAMERATE_MUST_MATCH | XAPO_FLAG_BITSPERSAMPLE_MUST_MATCH | XAPO_FLAG_BUFFERCOUNT_MUST_MATCH | XAPO_FLAG_INPLACE_SUPPORTED)
32 
33 // default number of input and output buffers supported, applies to XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS
34 #define XAPOBASE_DEFAULT_BUFFER_COUNT 1
35 
36 //--------------<M-A-C-R-O-S>-----------------------------------------------//
37 // assertion
38 #if !defined(XAPOASSERT)
39     #if XAPODEBUG
40         #define XAPOASSERT(exp) if (!(exp)) { OutputDebugStringA("XAPO ASSERT: " #exp ", {" __FUNCTION__ "}\n"); __debugbreak(); }
41     #else
42         #define XAPOASSERT(exp) __assume(exp)
43     #endif
44 #endif
45 #if !defined(XAPOASSERT_NO_OUTPUT)
46     #if XAPODEBUG
47         #define XAPOASSERT_NO_OUTPUT(exp) if (!(exp)) { __debugbreak(); }
48     #else
49         #define XAPOASSERT_NO_OUTPUT(exp) __assume(exp)
50     #endif
51 #endif
52 
53 //--------------<D-A-T-A---T-Y-P-E-S>---------------------------------------//
54 #pragma pack(push, 8) // set packing alignment to ensure consistency across arbitrary build environments, and ensure synchronization variables used by Interlocked functionality are correctly aligned
55 
56 // primitive types
57 typedef float FLOAT32; // 32-bit IEEE float
58 
59   ////
60   // DESCRIPTION:
61   //  Default implementation of the IXAPO and IUnknown interfaces.
62   //  Provides overridable implementations for all methods save IXAPO::Process.
63   ////
64 class __declspec(novtable) CXAPOBase: public IXAPO {
65 private:
66     const XAPO_REGISTRATION_PROPERTIES* m_pRegistrationProperties; // pointer to registration properties of the XAPO, set via constructor
67 
68     void*    m_pfnMatrixMixFunction;    // optimal matrix function pointer, used for thru processing
69     FLOAT32* m_pfl32MatrixCoefficients; // matrix coefficient table, used for thru processing
70     UINT32   m_nSrcFormatType;          // input format type, used for thru processing
71     BOOL     m_fIsScalarMatrix;         // TRUE if m_pfl32MatrixCoefficients is diagonal matrix with all main diagonal entries equal, i.e. m_pfnMatrixMixFunction only used for type conversion (no channel conversion), used for thru processing
72     BOOL     m_fIsLocked;               // TRUE if XAPO locked via CXAPOBase.LockForProcess
73 
74 protected:
75     LONG m_lReferenceCount; // COM reference count, must be aligned for atomic operations
76 
77       ////
78       // DESCRIPTION:
79       //  Verifies an audio format falls within the default ranges supported.
80       //
81       // REMARKS:
82       //  If pFormat is unsupported, and fOverwrite is TRUE,
83       //  pFormat is overwritten with the nearest format supported.
84       //  Nearest meaning closest bit depth, framerate, and channel count,
85       //  in that order of importance.
86       //
87       // PARAMETERS:
88       //  pFormat    - [in/out] audio format to examine
89       //  fOverwrite - [in]     TRUE to overwrite pFormat if audio format unsupported
90       //
91       // RETURN VALUE:
92       //  COM error code, including:
93       //    S_OK                      - audio format supported, pFormat left untouched
94       //    XAPO_E_FORMAT_UNSUPPORTED - audio format unsupported, pFormat overwritten with nearest audio format supported if fOverwrite TRUE
95       //    E_INVALIDARG              - audio format invalid, pFormat left untouched
96       ////
97     virtual HRESULT ValidateFormatDefault (_Inout_ WAVEFORMATEX* pFormat, BOOL fOverwrite);
98 
99       ////
100       // DESCRIPTION:
101       //  Verifies that an input/output format pair configuration is supported
102       //  with respect to the XAPO property flags.
103       //
104       // REMARKS:
105       //  If pRequestedFormat is unsupported, and fOverwrite is TRUE,
106       //  pRequestedFormat is overwritten with the nearest format supported.
107       //  Nearest meaning closest bit depth, framerate, and channel count,
108       //  in that order of importance.
109       //
110       // PARAMETERS:
111       //  pSupportedFormat - [in]     audio format known to be supported
112       //  pRequestedFormat - [in/out] audio format to examine, must be WAVEFORMATEXTENSIBLE if fOverwrite TRUE
113       //  fOverwrite       - [in]     TRUE to overwrite pRequestedFormat if input/output configuration unsupported
114       //
115       // RETURN VALUE:
116       //  COM error code, including:
117       //    S_OK                      - input/output configuration supported, pRequestedFormat left untouched
118       //    XAPO_E_FORMAT_UNSUPPORTED - input/output configuration unsupported, pRequestedFormat overwritten with nearest audio format supported if fOverwrite TRUE
119       //    E_INVALIDARG              - either audio format invalid, pRequestedFormat left untouched
120       ////
121     HRESULT ValidateFormatPair (const WAVEFORMATEX* pSupportedFormat, _Inout_ WAVEFORMATEX* pRequestedFormat, BOOL fOverwrite);
122 
123       ////
124       // DESCRIPTION:
125       //  This method may be called by an IXAPO::Process implementation
126       //  for thru processing.  It copies/mixes data from source to
127       //  destination, making as few changes as possible to the audio data.
128       //
129       // REMARKS:
130       //  However, this method is capable of channel upmix/downmix and uses
131       //  the same matrix coefficient table used by windows Vista to do so.
132       //
133       //  For in-place processing (input buffer == output buffer)
134       //  this method does nothing.
135       //
136       //  This method should be called only if the XAPO is locked and
137       //  XAPO_FLAG_FRAMERATE_MUST_MATCH is used.
138       //
139       // PARAMETERS:
140       //  pInputBuffer       - [in]  input buffer, format may be INT8, INT16, INT20 (contained in 24 or 32 bits), INT24 (contained in 24 or 32 bits), INT32, or FLOAT32
141       //  pOutputBuffer      - [out] output buffer, format must be FLOAT32
142       //  FrameCount         - [in]  number of frames to process
143       //  InputChannelCount  - [in]  number of input channels
144       //  OutputChannelCount - [in]  number of output channels
145       //  MixWithOutput      - [in]  TRUE to mix with output, FALSE to overwrite output
146       //
147       // RETURN VALUE:
148       //  void
149       ////
150     void ProcessThru (const void* pInputBuffer, _Inout_updates_(FrameCount*OutputChannelCount) FLOAT32* pOutputBuffer, UINT32 FrameCount, UINT32 InputChannelCount, UINT32 OutputChannelCount, BOOL MixWithOutput);
151 
152     // accessors
GetRegistrationPropertiesInternal()153     const XAPO_REGISTRATION_PROPERTIES* GetRegistrationPropertiesInternal () { return m_pRegistrationProperties; }
IsLocked()154     BOOL IsLocked  () { return m_fIsLocked; }
155 
156 public:
157     CXAPOBase (const XAPO_REGISTRATION_PROPERTIES* pRegistrationProperties);
158     virtual ~CXAPOBase ();
159 
160     // IUnknown methods:
161     // retrieves the requested interface pointer if supported
STDMETHOD(QueryInterface)162     STDMETHOD(QueryInterface) (REFIID riid, _Outptr_ void** ppInterface)
163     {
164         XAPOASSERT(ppInterface != NULL);
165         HRESULT hr = S_OK;
166 
167         if (riid == __uuidof(IXAPO)) {
168             *ppInterface = static_cast<IXAPO*>(this);
169             AddRef();
170         } else if (riid == __uuidof(IUnknown)) {
171             *ppInterface = static_cast<IUnknown*>(this);
172             AddRef();
173         } else {
174             *ppInterface = NULL;
175             hr = E_NOINTERFACE;
176         }
177 
178         return hr;
179     }
180 
181     // increments reference count
STDMETHOD_(ULONG,AddRef)182     STDMETHOD_(ULONG, AddRef) ()
183     {
184         return (ULONG)InterlockedIncrement(&m_lReferenceCount);
185     }
186 
187     // decrements reference count and deletes the object if the reference count falls to zero
STDMETHOD_(ULONG,Release)188     STDMETHOD_(ULONG, Release) ()
189     {
190         ULONG uTmpReferenceCount = (ULONG)InterlockedDecrement(&m_lReferenceCount);
191         if (uTmpReferenceCount == 0) {
192             delete this;
193         }
194         return uTmpReferenceCount;
195     }
196 
197     // IXAPO methods:
198     // Allocates a copy of the registration properties of the XAPO.
199     // This default implementation returns a copy of the registration
200     // properties given to the constructor, allocated via XAPOAlloc.
201     STDMETHOD(GetRegistrationProperties) (_Outptr_ XAPO_REGISTRATION_PROPERTIES** ppRegistrationProperties);
202 
203     // Queries if a specific input format is supported for a given output format.
204     // This default implementation assumes only the format described by the
205     // XAPOBASE_DEFAULT_FORMAT values are supported for both input and output.
206     STDMETHOD(IsInputFormatSupported) (const WAVEFORMATEX* pOutputFormat, const WAVEFORMATEX* pRequestedInputFormat, _Outptr_opt_ WAVEFORMATEX** ppSupportedInputFormat);
207 
208     // Queries if a specific output format is supported for a given input format.
209     // This default implementation assumes only the format described by the
210     // XAPOBASE_DEFAULT_FORMAT values are supported for both input and output.
211     STDMETHOD(IsOutputFormatSupported) (const WAVEFORMATEX* pInputFormat, const WAVEFORMATEX* pRequestedOutputFormat, _Outptr_opt_ WAVEFORMATEX** ppSupportedOutputFormat);
212 
213     // Performs any effect-specific initialization.
214     // This default implementation is a no-op and only returns S_OK.
STDMETHOD(Initialize)215     STDMETHOD(Initialize) (_In_reads_bytes_opt_(DataByteSize) const void*, UINT32 DataByteSize)
216     {
217         UNREFERENCED_PARAMETER(DataByteSize);
218         return S_OK;
219     }
220 
221     // Resets variables dependent on frame history.
222     // This default implementation is a no-op: this base class contains no
223     // relevant state to reset.
STDMETHOD_(void,Reset)224     STDMETHOD_(void, Reset) () { return; }
225 
226     // Notifies XAPO of buffer formats Process() will be given.
227     // This default implementation performs basic input/output format
228     // validation against the XAPO's registration properties.
229     // Derived XAPOs should call the base implementation first.
230     STDMETHOD(LockForProcess) (_Pre_equal_to_(OutputLockedParameterCount) UINT32 InputLockedParameterCount, _In_reads_opt_(InputLockedParameterCount) const XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS* pInputLockedParameters, _Pre_equal_to_(InputLockedParameterCount) UINT32 OutputLockedParameterCount, _In_reads_opt_(OutputLockedParameterCount) const XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS* pOutputLockedParameters);
231 
232     // Opposite of LockForProcess.
233     // Derived XAPOs should call the base implementation first.
234     STDMETHOD_(void, UnlockForProcess) ();
235 
236     // Returns the number of input frames required to generate the requested number of output frames.
237     // By default, this method returns the same number of frames it was passed.
STDMETHOD_(UINT32,CalcInputFrames)238     STDMETHOD_(UINT32, CalcInputFrames) (UINT32 OutputFrameCount) { return OutputFrameCount; }
239 
240     // Returns the number of output frames generated for the requested number of input frames.
241     // By default, this method returns the same number of frames it was passed.
STDMETHOD_(UINT32,CalcOutputFrames)242     STDMETHOD_(UINT32, CalcOutputFrames) (UINT32 InputFrameCount) { return InputFrameCount; }
243 };
244 
245 //--------------------------------------------------------------------------//
246   ////
247   // DESCRIPTION:
248   //  Extends CXAPOBase, providing a default implementation of the
249   //  IXAPOParameters interface with appropriate synchronization to
250   //  protect variables shared between IXAPOParameters::GetParameters
251   //  and IXAPOParameters::SetParameters/IXAPO::Process.
252   //
253   //  This class is for parameter blocks whose size is larger than 4 bytes.
254   //  For smaller parameter blocks, use atomic operations directly
255   //  on the parameters for synchronization.
256   ////
257 class __declspec(novtable) CXAPOParametersBase: public CXAPOBase, public IXAPOParameters {
258 private:
259     BYTE*  m_pParameterBlocks;           // three contiguous process parameter blocks used for synchronization, user responsible for initialization of parameter blocks before IXAPO::Process/SetParameters/GetParameters called
260     BYTE*  m_pCurrentParameters;         // pointer to current process parameters, must be aligned for atomic operations
261     BYTE*  m_pCurrentParametersInternal; // pointer to current process parameters (temp pointer read by SetParameters/BeginProcess/EndProcess)
262     UINT32 m_uCurrentParametersIndex;    // index of current process parameters
263     UINT32 m_uParameterBlockByteSize;    // size of a single parameter block in bytes, must be > 0
264     BOOL   m_fNewerResultsReady;         // TRUE if there exists new processing results not yet picked up by GetParameters(), must be aligned for atomic operations
265     BOOL   m_fProducer;                  // TRUE if IXAPO::Process produces data to be returned by GetParameters(), SetParameters() and ParametersChanged() disallowed
266 
267 public:
268       ////
269       // PARAMETERS:
270       //  pRegistrationProperties - [in] registration properties of the XAPO
271       //  pParameterBlocks        - [in] three contiguous process parameter blocks used for synchronization
272       //  uParameterBlockByteSize - [in] size of one of the parameter blocks, must be > 0, should be > 4
273       //  fProducer               - [in] TRUE if IXAPO::Process produces data to be returned by GetParameters() (SetParameters() and ParametersChanged() disallowed)
274       ////
275     CXAPOParametersBase (const XAPO_REGISTRATION_PROPERTIES* pRegistrationProperties, _In_reads_bytes_opt_(3*uParameterBlockByteSize) BYTE* pParameterBlocks, UINT32 uParameterBlockByteSize, BOOL fProducer);
276     virtual ~CXAPOParametersBase ();
277 
278     // IUnknown methods:
279     // retrieves the requested interface pointer if supported
STDMETHOD(QueryInterface)280     STDMETHOD(QueryInterface) (REFIID riid, _Outptr_result_maybenull_ void** ppInterface)
281     {
282         XAPOASSERT(ppInterface != NULL);
283         HRESULT hr = S_OK;
284 
285         if (riid == __uuidof(IXAPOParameters)) {
286             *ppInterface = static_cast<IXAPOParameters*>(this);
287             CXAPOBase::AddRef();
288         } else {
289             hr = CXAPOBase::QueryInterface(riid, ppInterface);
290         }
291 
292         return hr;
293     }
294 
295     // increments reference count
STDMETHOD_(ULONG,AddRef)296     STDMETHOD_(ULONG, AddRef)() { return CXAPOBase::AddRef(); }
297 
298     // decrements reference count and deletes the object if the reference count falls to zero
STDMETHOD_(ULONG,Release)299     STDMETHOD_(ULONG, Release)() { return CXAPOBase::Release(); }
300 
301     // IXAPOParameters methods:
302     // Sets effect-specific parameters.
303     // This method may only be called on the realtime audio processing thread.
304     STDMETHOD_(void, SetParameters) (_In_reads_bytes_(ParameterByteSize) const void* pParameters, UINT32 ParameterByteSize);
305 
306     // Gets effect-specific parameters.
307     // This method may block and should not be called from the realtime thread.
308     // Get the current parameters via BeginProcess.
309     STDMETHOD_(void, GetParameters) (_Out_writes_bytes_(ParameterByteSize) void* pParameters, UINT32 ParameterByteSize);
310 
311     // Called by SetParameters() to allow for user-defined parameter validation.
312     // SetParameters validates that ParameterByteSize == m_uParameterBlockByteSize
313     // so the user may assume/assert ParameterByteSize == m_uParameterBlockByteSize.
314     // This method should not block as it is called from the realtime thread.
OnSetParameters(_In_reads_bytes_ (ParameterByteSize)const void * pParameters,UINT32 ParameterByteSize)315     virtual void OnSetParameters (_In_reads_bytes_(ParameterByteSize) const void* pParameters, UINT32 ParameterByteSize)
316     {
317         XAPOASSERT(m_uParameterBlockByteSize > 0);
318         XAPOASSERT(pParameters != NULL);
319         XAPOASSERT(ParameterByteSize == m_uParameterBlockByteSize);
320     }
321 
322     // Returns TRUE if SetParameters() has been called since the last processing pass.
323     // May only be used within the XAPO's IXAPO::Process implementation,
324     // before BeginProcess is called.
325     BOOL ParametersChanged ();
326 
327     // Returns latest process parameters.
328     // XAPOs must call this method within their IXAPO::Process
329     // implementation to access latest process parameters in threadsafe manner.
330     BYTE* BeginProcess ();
331 
332     // Notifies CXAPOParametersBase that the XAPO has finished accessing
333     // the latest process parameters.
334     // XAPOs must call this method within their IXAPO::Process
335     // implementation to access latest process parameters in threadsafe manner.
336     void EndProcess ();
337 };
338 
339 #pragma pack(pop) // revert packing alignment
340 //---------------------------------<-EOF->----------------------------------//
341