我们在编写多媒体播放器程序时,经常会遇到不知怎么让双声道切换到左声道或右声道音频的问题,而使用MCI接口或媒体播放器控件往往只能使用调节声道左右均衡的方法达到切换声道的目的,但这样只会有一只喇叭发出声音,且某些VCD格式的歌曲甚至不能用这种方法切换声道.
  但我们在使用媒体播放器播放VCD格式的文件时,如果你仔细观察,会发现可以在播放时通过 属性->高级->选中Mpeg Audio Decoder过滤器->属性->频道 的方法分别切换到左右声道或立体声状态,媒体播放器是怎样实现这个功能但我们为什么又不能直接在VB中实现它呢?,其实这都是因为媒体播放器是基于DirectShow技术的原因.
  同理,我们也可以直接调用DirectShow达到播放多媒体文件的目的,但要实现切换声道还得首先弄懂音频数据的格式,下面以16位音频数据为例(8位音频只是少了一个字节的数据)简单说一下解压后的音频数据在即将播放时的声道分布格式.
  当音频数据通过各种解码器解码后,在将要播放时都会先送到声卡数据缓冲区里,其数据为Wave格式,例如:aa bb cc dd,其中aa-左声道,bb-右声道,cc-左声道,dd-右声道,很简单的是吗:)
  如果要切换到左声道就要用左声道的数据填充到右声道里,即使上面的音频数据变为:aa aa cc cc这种形式,知道它的原理,下一步的工作就是利用DirectShow技术写一个Filter插入到声卡reanderer的前面,这样只要是双声道格式的音频数据,我们都可以对它进行声道切换了.下面列出我写的一个用于16音频声道切换的Filter源代码(其实很简单的,但你得对COM有一点了解)

//

//

//  FileName    :   MyFilter.cpp

//  Author      :   damo

//  Date        :   2004.06.01

//  Comment     :

//

//

#include <streams.h>     // DirectShow (includes windows.h)

#include <initguid.h>    // declares DEFINE_GUID to declare an EXTERN_C const.

#include "IZQAudio.h"    // 自定义接口

// {CFBE95E1-5DB4-11d8-929C-92B98A07327D}

DEFINE_GUID(CLSID_ZQAudio,

0xcfbe95e1, 0x5db4, 0x11d8, 0x92, 0x9c, 0x92, 0xb9, 0x8a, 0x7, 0x32, 0x7d);

const AMOVIESETUP_MEDIATYPE sudPinTypes =

{ &MEDIATYPE_Audio        // clsMajorType

, &MEDIASUBTYPE_PCM };    // clsMinorType

const AMOVIESETUP_PIN psudPins[] =

{ { L"Input"            // strName

, FALSE               // bRendered

, FALSE               // bOutput

, FALSE               // bZero

, FALSE               // bMany

, &CLSID_NULL         // clsConnectsToFilter

, L""                 // strConnectsToPin

, 1                   // nTypes

, &sudPinTypes        // lpTypes

}

, { L"Output"           // strName

, FALSE               // bRendered

, TRUE                // bOutput

, FALSE               // bZero

, FALSE               // bMany

, &CLSID_NULL         // clsConnectsToFilter

, L""                 // strConnectsToPin

, 1                   // nTypes

, &sudPinTypes        // lpTypes

}

};

const AMOVIESETUP_FILTER sudNullNull =

{ &CLSID_ZQAudio                  // clsID

, L"zhang qiang Filter"                 // strName

, MERIT_DO_NOT_USE                // dwMerit

, 2                               // nPins

, psudPins };                     // lpPin

// CZQAudio

//

class CZQAudio:

public CTransInPlaceFilter,

public IZQAudioInterface

{

public:

//COM 函数声明

static CUnknown *WINAPI CreateInstance(LPUNKNOWN punk, HRESULT *phr);

STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);

DECLARE_IUNKNOWN;

STDMETHODIMP put_AudioMode(int inAudio_Channel_Mode);

STDMETHODIMP get_AudioMode(int *outAudio_Channel_Mode);

private:

int Audio_Channel_Mode;              //声道模式设置

//int s;

CCritSec m_Mylock;                  //锁定对象

//构造函数,同时调用基类的构造函数

CZQAudio(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr)

: CTransInPlaceFilter (tszName, punk, CLSID_ZQAudio, phr)

{ Audio_Channel_Mode=0; }

//主要执行函数

HRESULT Transform(IMediaSample *pSample);

//检查输入类型

HRESULT CheckInputType(const CMediaType *mtIn);

//检查输入与输出类型是否一致

//HRESULT CheckTransform(const CMediaType *mtIn,const CMediaType *mtOut){ return S_OK; };

//取得类型

HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);

};

// Needed for the CreateInstance mechanism

CFactoryTemplate g_Templates[]=

{   { L"zhang qiang Filter"

, &CLSID_ZQAudio

, CZQAudio::CreateInstance

, NULL

, &sudNullNull }

};

int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);

//

// CreateInstance

//

// 创建一个对象实例

CUnknown * WINAPI CZQAudio::CreateInstance(LPUNKNOWN punk, HRESULT *phr) {

CZQAudio *pNewObject = new CZQAudio(NAME("Zhang qiang audio filter"), punk, phr );

if (pNewObject == NULL) {

*phr = E_OUTOFMEMORY;

}

return pNewObject;

} // CreateInstance

HRESULT CZQAudio::Transform(IMediaSample *pSample)

{

BYTE *pOutData;

int i=0,w_pos=0,r_pos=0,SampleSize=0,n=0;

//进行声道选择

switch(Audio_Channel_Mode)

{

case 1:r_pos=0;

w_pos=2;

break;             //左声道

case 2:r_pos=2;

w_pos=0;

break;             //右声道

default:return NOERROR;           //不处理(双声道模式)

}

pSample->GetPointer(&pOutData);    //取得缓冲区指针

SampleSize=pSample->GetActualDataLength();     //取得有效数据大小

n=SampleSize/4;                    //循环次数

//执行声道切换操作

for(i=0;i<n;i++){

memcpy(pOutData+w_pos,pOutData+r_pos,2);

w_pos+=4;

r_pos+=4;

}

return NOERROR;

}

HRESULT CZQAudio::GetMediaType(int iPosition,CMediaType *pMediaType)

{

return NOERROR;

}

HRESULT CZQAudio::CheckInputType(const CMediaType *mtIn)

{

//检查是否在停止状态,且是否是音频数据类型

if(IsStopped() && *mtIn->Type()==MEDIATYPE_Audio)

{

//检查是否是PCM格式的数据

if(*mtIn->Subtype()==MEDIASUBTYPE_PCM ||

*mtIn->Subtype()==MEDIASUBTYPE_WAVE)

{

return S_OK;     //成功

}

}

return E_INVALIDARG;     //无效类型

}

//设置声道

STDMETHODIMP CZQAudio::put_AudioMode(int inAudio_Channel_Mode)

{

//判断参数是否合法

if(inAudio_Channel_Mode>=0 && inAudio_Channel_Mode<=2){

Audio_Channel_Mode=inAudio_Channel_Mode;

return S_OK;

}

return E_INVALIDARG;       //参数无效

}

//取得声道模式设置

STDMETHODIMP CZQAudio::get_AudioMode(int *outAudio_Channel_Mode)

{

//取得声道模式设置

*outAudio_Channel_Mode=Audio_Channel_Mode;

return S_OK;

}

//暴露接口

STDMETHODIMP CZQAudio::NonDelegatingQueryInterface(REFIID riid,void **ppv)

{

CheckPointer(ppv,E_POINTER);

if(riid==IID_IZQAudioInterface){

return GetInterface((IZQAudioInterface *) this,ppv);

}else{

return CTransInPlaceFilter::NonDelegatingQueryInterface(riid,ppv);

}

}

/******************************全局函数******************************/

* 注册及反注册函数

/**************************************************************************/

STDAPI DllRegisterServer()

{

return AMovieDllRegisterServer2( TRUE );

}

STDAPI DllUnregisterServer()

{

return AMovieDllRegisterServer2( FALSE );

}

//屏蔽 4514 警告

#pragma warning( disable:4514)

//IZQAudio.h

//声道切换的自定义接口

#ifndef _H_IZQAudioInterface_

#define _H_IZQAudioInterface_

#ifdef _cplusplus

extern "C"  {

#endif

//IZQAudioChannel's GUID

// {83BA1141-6135-11d8-929C-A23780A5EB7C}

DEFINE_GUID(IID_IZQAudioInterface,

0x83ba1141, 0x6135, 0x11d8, 0x92, 0x9c, 0xa2, 0x37, 0x80, 0xa5, 0xeb, 0x7c);

// 双声道模式参数声明

#define AM_AUDIO_DUAL_MERGE 0         //双声道

#define AM_AUDIO_DUAL_LEFT  1         //左声道

#define AM_AUDIO_DUAL_RIGHT 2         //右声道

//IZQAudioInterface 接口定义

DECLARE_INTERFACE_(IZQAudioInterface,IUnknown)

{

//设置声道:0-不处理声道,1-左声道,2-右声道

STDMETHOD (put_AudioMode) (THIS_

int inAudio_Channel_Mode

) PURE;

//返回声道的设置

STDMETHOD (get_AudioMode) (THIS_

int *outAudio_Channel_Mode

) PURE;

};

#ifdef _cplusplus

}

#endif

#endif //_H_IZQAudio_

//****************************************以下为IZQAutio.h文件的源代码**************************************************

//IZQAudio.h

//声道切换的自定义接口

#ifndef _H_IZQAudioInterface_
#define _H_IZQAudioInterface_

#ifdef _cplusplus
extern "C"  {
#endif

//IZQAudioChannel's GUID
// {83BA1141-6135-11d8-929C-A23780A5EB7C}
DEFINE_GUID(IID_IZQAudioInterface, 
0x83ba1141, 0x6135, 0x11d8, 0x92, 0x9c, 0xa2, 0x37, 0x80, 0xa5, 0xeb, 0x7c);

// 双声道模式参数声明
#define AM_AUDIO_DUAL_MERGE 0         //双声道
#define AM_AUDIO_DUAL_LEFT  1         //左声道
#define AM_AUDIO_DUAL_RIGHT 2         //右声道

//IZQAudioInterface 接口定义
DECLARE_INTERFACE_(IZQAudioInterface,IUnknown)
{
//设置声道:0-不处理声道,1-左声道,2-右声道
STDMETHOD (put_AudioMode) (THIS_
int inAudio_Channel_Mode
) PURE;
//返回声道的设置
STDMETHOD (get_AudioMode) (THIS_
int *outAudio_Channel_Mode
) PURE;
};

#ifdef _cplusplus
}
#endif

#endif //_H_IZQAudio_

//*************************以下为ZQAudio.def文件的内容****************************

LIBRARY         ZQAudio.Ax
EXPORTS
               DllGetClassObject PRIVATE
               DllCanUnloadNow PRIVATE
               DllRegisterServer PRIVATE
               DllUnregisterServer PRIVATE

将以上文件加入到VC工程里,并安装DirectX8.1 SDK,设置好相关的文件搜索路径(具体可参考<DirectShow开发指南>)编译后将得到一个Filter,现在你可在程序中用它来切换声道了,如果你想在VB中使用它,须写一个DLL将各种播放操作封装,然后供VB进行调用.
另外,用它对某些VCD格式的歌曲会无效,原因在于Mpeg Audio Decoder解码器的设置问题,你可在程序中包含MpegType.h文件来切换声道.
以上均是我摸索实验的结果,如有错误还请指出!

使用DirectShow技术切换双声道音频声道的方法相关推荐

  1. android安卓切换音频声道-耳机-外放-蓝牙-实用功能系列

    切换音频声道-耳机-外放-蓝牙 功能简介 实现步骤 java代码 Android技术生活交流 完整代码ZIP:下载 更多其他页面-自定义View-实用功能合集:点击查看 功能简介 实现切换音频通道 蓝 ...

  2. java 双声道音频_Android 播放音频如何实现双声道效果

    1 使用场景 Android 无法直接采集双声道,用户如果想实现播放的音频具有双声道效果,可以参考本文. 2 实现流程 双声道效果实现的主流程为: 下文将分别讲述各个步骤. 2.1 输入双声道数据 主 ...

  3. java 双声道音频_一种多声道录音的实现方法(语音识别功能)

    前段时间,在firefly3399开发板上实现了基于思必驰的语音识别功能.关于这个开发板的资料请查看官方网站http://www.t-firefly.com/product/rk3399.html. ...

  4. wenet实战系列-双声道音频语音识别

    本文介绍如何对双声道音频进行语音识别,可以在通话场景下使用,如客服通话.电话录音等.asr模型选择wenet,vad模型选择WebRTC VAD,并给出一个简单实现的demo: Dual_Audio_ ...

  5. 多声道音频指南(二):那些年,那些技术,那些名词

    原文地址 家庭影院应该算是操作最复杂的家电之一了.各种音效,各种技术,各种接口,是否让你云里雾里?且看本篇慢慢道来. 要把声音播放出来,总共分几步? 不考虑具体的电路原理细节的话,大概可以分以下5步: ...

  6. 教你用Sound Forge声道切换功能编辑音频

    现今大多数流行歌曲都会采用立体声设置,让歌曲听起来更为丰满.单声道音频是比较原始的声音通道设置方式,将不同方向的声音都混合在一个声道中,有些单耳听力障碍用户会使用到单声道音频. 而在立体声设置中,有些 ...

  7. java 双声道音频_java实现切割wav音频文件的方法详解【附外部jar包下载】

    本文实例讲述了java实现切割wav音频文件的方法.分享给大家供大家参考,具体如下: import it.sauronsoftware.jave.Encoder; import it.sauronso ...

  8. 多声道音频指南(一):被声音包围的感觉

    多声道音频指南:被声音包围的感觉 原文地址 家庭影院这玩意,至今仍给广大群众一种高端大气上档次的感觉.确实,有些土豪发烧友在这方面一掷千金,在如今高耸入云的房价面前,单算一间听音室的花费都得写一长串零 ...

  9. 左右声道测试音频_关于制作左右声道音频的剪辑软件推荐

    一款优秀的视频剪辑软件,不仅有高水平的视频制作功能,它的音频编辑功能也是必不可少的.Vegas就是这么一款软件,同时具备视频制作特效制作的同时,还能帮助制作轨道音频效果. 下面,就让小编带大家去学习, ...

最新文章

  1. R语言ggplot2可视化:置信区间与分组具有相同色彩、自定义置信区间带的色彩、Make confidence intervals the same color as line by group
  2. Transformer又出新变体∞-former:无限长期记忆,任意长度上下文
  3. HDU2029:Palindromes _easy version
  4. 一天搞定CSS: CSS选择器优先级--08
  5. 10个保持注意力的技巧
  6. 淘宝大牛们——晒一晒淘宝网技术内幕
  7. Tomcat Linux下自启动
  8. paip.c++ qt C:\iwmake\build_mingw_opensource _Unwind_Resume的问题
  9. 在Linux中使用飞信发送手机短信
  10. 让他们有事可做( 云中漫步zai)
  11. Unity 3D模型展示框架篇之项目整理
  12. 75寸电视长宽多少厘米
  13. RK3588 调试 phy
  14. Dev-C++5.11游戏创作之简易小炸弹
  15. zend及Slim 漏洞合集
  16. iOS15可以多开APP?其实是专注模式的功能
  17. weblogic 启动问题
  18. 怎样将网页保存为html,如何将网页保存为书签
  19. ac86u格式化jffs_【新手教程】20180828 ASUS固件使用操作指引华硕AC86U
  20. Windows窗口、控件和对话框

热门文章

  1. 计算机网络基础基本知识
  2. 关于DOS-BOX的使用方法
  3. 【GCC】2: RTCP cc-feeback 抓包对比协议
  4. 从蒙牛到小度,跨年晚会中的国民品牌变迁
  5. 主流的企业级虚拟化解决方案
  6. word文件自动变成只读模式,怎么办?
  7. 鸽巢原理(初识)(纯算法)
  8. 浪潮之巅读书笔记(二)
  9. 2020年浙大计算机考研答疑
  10. The Design of design