音频特效滤镜 via DirectShow Filter

音频特效定义

音效或声效(Sound effects 或 Audio effects)是人工制造或加强的声音,用来增强对电影、电子游戏、音乐或其他媒体的艺术或其他内容的声音处理。

常见的音效技术有:回声、合唱、均衡(EQ)、过滤、变调、移相、压缩 / 拉伸、3D、调制和共鸣等等。

DirectShow Filter

DShow 中插件是以 Filter 的形式创建的,需要继承自下面两个接口之一:

  • CTransformFilter:拷贝原始 sample 的 buffer 再修改,一般输入输出格式不一样
  • CTransInPlaceFilter:直接修改原始 sample 的 buffer,输入输出格式一样。该接口其实继承自上面的接口。

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\ 目录下。

CTransformFilter::CheckInputType 函数

顾名思义,检查输入类型是否被该 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;
}

CTransformFilter::SetMediaType 函数

尝试设置输入或输出 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;
}

CTransInPlaceFilter::Transform 函数

真正 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;
}

ISpecifyPropertyPages::GetPages 函数

返回一个可以设置 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;
}

CGargleProperties 类

参数设置界面在此。
gargle property
类定义:

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);
}; 

其他框架的滤镜

  • 关于 FFmpeg 的音频滤镜请参考 这里
  • 关于 Media Foundation 的音频滤镜请参考 这里

Blueware
EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值