音效或声效(Sound effects 或 Audio effects)是人工制造或加强的声音,用来增强对电影、电子游戏、音乐或其他媒体的艺术或其他内容的声音处理。
常见的音效技术有:回声、合唱、均衡(EQ)、过滤、变调、移相、压缩 / 拉伸、3D、调制和共鸣等等。
DShow 中插件是以 Filter 的形式创建的,需要继承自下面两个接口之一:
Transform filter 举例:
class CClipFilter : public CTransformFilter
{
public:
DECLARE_IUNKNOWN;
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
// Called as part of connecting.
HRESULT CheckInputType(const CMediaType *mtIn);
HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut);
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties);
HRESULT SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt);
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
HRESULT Transform(IMediaSample *pIn, IMediaSample *pOut);
};
InPlace filter 举例:
class CGargle
: public CTransInPlaceFilter // Main DirectShow interfaces
, public ISpecifyPropertyPages // Needed for properties only
{
public:
DECLARE_IUNKNOWN;
// Basic COM - used here to reveal our property interface.
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
// --- CTransInPlaceFilter Overrides --
HRESULT CheckInputType(const CMediaType *mtIn);
// Called as part of connecting.
HRESULT SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt);
// This is where the "real work" is done.
HRESULT Transform(IMediaSample *pSample);
// --- ISpecifyPropertyPages ---
// return our property pages
STDMETHODIMP GetPages(CAUUID * pPages);
};
下面我们以比较简单的 InPlace filter 说明。
代码包含在 Windows SDK 7.x 的 samples\multimedia\directshow\filters\ 目录下。
顾名思义,检查输入类型是否被该 filter 支持。
HRESULT CGargle::CheckInputType(const CMediaType *pmt)
{
CheckPointer(pmt,E_POINTER);
WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->pbFormat;
// Reject non-Audio types.
if (pmt->majortype != MEDIATYPE_Audio)
return VFW_E_TYPE_NOT_ACCEPTED;
// Reject invalid format blocks
if (pmt->formattype != FORMAT_WaveFormatEx)
return VFW_E_TYPE_NOT_ACCEPTED;
// Reject compressed audio
if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
return VFW_E_TYPE_NOT_ACCEPTED;
// Accept only 8 or 16 bit
if (pwfx->wBitsPerSample != 8 && pwfx->wBitsPerSample != 16)
return VFW_E_TYPE_NOT_ACCEPTED;
return NOERROR;
}
尝试设置输入或输出 Pin 的媒体类型,这里要求输入和输出类型一致。
HRESULT CGargle::SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt)
{
CheckPointer(pmt,E_POINTER);
// Record what we need for doing the actual transform
WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->Format();
m_Channels = pwfx->nChannels;
m_SamplesPerSec = pwfx->nSamplesPerSec;
m_BytesPerSample = pwfx->wBitsPerSample / 8;
// Call the base class to do its thing
CTransInPlaceFilter::SetMediaType(direction, pmt);
// Reconnect where necessary.
if( m_pInput->IsConnected() && m_pOutput->IsConnected() ) {
FILTER_INFO fInfo;
QueryFilterInfo( &fInfo );
if (direction == PINDIR_OUTPUT && *pmt != m_pInput->CurrentMediaType() )
fInfo.pGraph->Reconnect( m_pInput );
QueryFilterInfoReleaseGraph( fInfo );
ASSERT(!(direction == PINDIR_INPUT && *pmt != m_pOutput->CurrentMediaType()));
}
return NOERROR;
}
真正 filter sample 的地方,咋整都成,只要不 buffer overflow ●~*
HRESULT CGargle::Transform( IMediaSample *pSample )
{
CheckPointer(pSample, E_POINTER);
HRESULT hr = NOERROR;
// Get the details of the data (address, length)
BYTE *pSampleBuffer;
int iSize = pSample->GetActualDataLength();
hr = pSample->GetPointer(&pSampleBuffer);
RETURN_IF_FAILED(hr);
// Actually transform the data
HRESULT hr = _internalProcess( pSampleBuffer, iSize );
RETURN_IF_FAILED(hr);
return NOERROR;
}
返回一个可以设置 filter 参数的界面(CLSID)。
STDMETHODIMP CGargle::GetPages(CAUUID * pPages)
{
CheckPointer(pPages, E_POINTER);
pPages->cElems = 1;
pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID));
if (pPages->pElems == NULL)
return E_OUTOFMEMORY;
*(pPages->pElems) = CLSID_GargProp;
return NOERROR;
}
class CGargleProperties : public CBasePropertyPage
{
public:
// Overrides from CBasePropertyPage
HRESULT OnConnect(IUnknown * punk);
HRESULT OnDisconnect(void);
HRESULT OnDeactivate(void);
CGargleProperties(LPUNKNOWN lpunk, HRESULT *phr);
private:
INT_PTR OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
– EOF –