iOS设备上回声消除的例子

工业上的声音处理中,回声消除是一个重要的话题,重要性不亚于噪声消除、人声放大、自动增益等,尤其是在VoIP功能上,回声消除是每一个做VoIP功能团队的必修课。QQ、Skype等等,回声消除的效果是一个重要的考查指标。

具体的回声消除算法比较复杂,我现在还没有研究的很明白。简单来说,就是在即将播放出来的声音中,将回声的那部分减去。其中一个关键,是如何估计回声大小,这需要用到自适应算法。研究不透,多说无益。有兴趣的同学可以一起学习。

Apple在Core Audio中提供了回声消除的接口,我写了一个测试APP,测试了其效果。链接:https://github.com/lixing123/iOSEchoCancellation

下面讲一下如何实现。

  1. 将声音输出route到speaker,这样声音比较大,回声明显:

    AVAudioSession* session = [AVAudioSession sharedInstance];
    [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
    [session setActive:YES error:nil];
    
  2. 初始化一个AUGraph,创建一个AUNode,并将之添加到graph上。一般来说,沟通麦克风/扬声器的AUNode,其类型应该是RemoteIO,但是RemoteIO不带回声消除功能,VoiceProcessingIO类型的才带。
    AudioComponentDescription inputcd = {0};
    inputcd.componentType = kAudioUnitType_Output;
    //inputcd.componentSubType = kAudioUnitSubType_RemoteIO;
    //we can access the system‘s echo cancellation by using kAudioUnitSubType_VoiceProcessingIO subtype
    inputcd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
    inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
    
  3. 配置AudioUnit的属性,打开与麦克风/扬声器的连接(这个比较难以理解,可以参考Apple文档:https://developer.apple.com/library/ios/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/UsingSpecificAudioUnits/UsingSpecificAudioUnits.html),并配置client data format(仅支持Linear PCM格式);配置回调函数。
    //Open input of the bus 1(input mic)
    UInt32 enableFlag = 1;
    CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,
                                kAudioOutputUnitProperty_EnableIO,
                                kAudioUnitScope_Input,
                                1,
                                &enableFlag,
                                sizeof(enableFlag)),
           "Open input of bus 1 failed");
    
    //Open output of bus 0(output speaker)
    CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,
                                kAudioOutputUnitProperty_EnableIO,
                                kAudioUnitScope_Output,
                                0,
                                &enableFlag,
                                sizeof(enableFlag)),
           "Open output of bus 0 failed");
    
    //Set up stream format for input and output
    streamFormat.mFormatID = kAudioFormatLinearPCM;
    streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    streamFormat.mSampleRate = 44100;
    streamFormat.mFramesPerPacket = 1;
    streamFormat.mBytesPerFrame = 2;
    streamFormat.mBytesPerPacket = 2;
    streamFormat.mBitsPerChannel = 16;
    streamFormat.mChannelsPerFrame = 1;
    
    CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Input,
                                0,
                                &streamFormat,
                                sizeof(streamFormat)),
           "kAudioUnitProperty_StreamFormat of bus 0 failed");
    
    CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                1,
                                &streamFormat,
                                sizeof(streamFormat)),
           "kAudioUnitProperty_StreamFormat of bus 1 failed");
    
    //Set up input callback
    AURenderCallbackStruct input;
    input.inputProc = InputCallback;
    input.inputProcRefCon = myStruct;
    CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,
                                kAudioUnitProperty_SetRenderCallback,
                                kAudioUnitScope_Global,
                                0,//input mic
                                &input,
                                sizeof(input)),
           "kAudioUnitProperty_SetRenderCallback failed");
    
  4. 在回调函数inputCallback中,用AudioUnitRender() 函数获取麦克风的声音,存在一个bufferList中。这个bufferList是一个ring结构,存储最新的声音,然后播放旧声音。这样,声音的输入和输出之间,就有了0.5s(可调节)左右的延迟,形成了明显的回声。
  5. 给回声消除添加一个开关。VoiceProcessingIO有一个属性可用来打开/关闭回声消除功能:kAUVoiceIOProperty_BypassVoiceProcessing
    UInt32 echoCancellation;
    UInt32 size = sizeof(echoCancellation);
    CheckError(AudioUnitGetProperty(myStruct.remoteIOUnit,
                                kAUVoiceIOProperty_BypassVoiceProcessing,
                                kAudioUnitScope_Global,
                                0,
                                &echoCancellation,
                                &size),
           "kAUVoiceIOProperty_BypassVoiceProcessing failed");
    
  6. 现在可以开始graph了:
    CheckError(AUGraphInitialize(graph),
           "AUGraphInitialize failed");
    CheckError(AUGraphStart(graph),
           "AUGraphStart failed");
    

    在示例中,有一个简单的开关按钮,可以明显感觉到打开/关闭回声消除的区别。

    在实测中,打开回声消除功能时,仍然能听到一点点的回声,不过很小,一般情况下足够使用了。

时间: 2024-10-06 22:30:47

iOS设备上回声消除的例子的相关文章

使IFRAME在iOS设备上支持滚动

原文链接: Scroll IFRAMEs on iOS原文日期: 2014年07月02日 翻译日期: 2014年07月10日翻译人员: 铁锚 很长时间以来, iOS设备上Safari中超出边界的元素将不能滚动,处理这种问题对开发人员来说一直是种折磨. 对原文作者的博客来说,这特别让人无奈,因为demo程序都是在沙箱IFRAMEs中运行的,这是为了不影响博客网站的AJAX页面加载策略.  通过一些研究发现,只要设置两个CSS属性,以及加上一个元素,这个问题就解决了. Here we go!(译者注

在ios设备上调试

ZJAppleID2014 1.创建开发者证书 每个开发者人员一次仅允许使用一个开发者证书,证书的管理可以登录 ios开发中心的配置门户网站(https://developer.apple.com/devcenter/ios/manage/overview/index.action) 登录该网站时,需要苹果的ios开发者帐号,登录成功后的界面如下: 点击IOS Apps下的Certificates(证书)导航菜单,得到的证书管理界面如下图所示,在此处下载证书和删除证书. 创建证书的过程分成两部:

如何在Android和iOS设备上录制游戏?

内容简介 如何在Android和iOS设备上录制游戏? 如何在Android和iOS设备上录制游戏? Xbox One,PS4,Windows 10等都集成了游戏录制功能,而且可以把录制的视频分享到网上. 现在,Android和iOS设备也可以实现游戏录制了. 怎么做呢?我们分为Android和iOS平台来介绍. Android平台 首先下载安装一个App,叫做<Google Play Games>. 运行此App,在菜单中点击<My Game>(英语<我的游戏>的意思

怎样将游戏从Unity导到iOS设备上

当我开始开发自己的iOS游戏时,我会考虑的第一件事便是如何将其导出到设备中,如此有效地测试我的游戏.最初,该过程看似很长且复杂,我所遇到的主要问题是,尽管存在许多资源,但是它们并非完全来自同样的地方,所以我不得不通过各种教程将其组合并匹配在一起.所以我决定通过本文一步步地总结这一过程.这是我到目前为止所遵循的一些方式;希望这也能够带给处于同样开发阶段的人更多帮助. 1.获得unity和iOS导出器 如果你还未这么做,你首先需要在官网下载最新版本的Unity.已经出现了2个版本,分别是免费版(ht

基于设备的回声消除

一.Win7下的实现: http://download.csdn.net/detail/xuwei17385/6426783 二.WinXP下的实现: http://download.csdn.net/detail/xuwei17385/4060561 基于设备的回声消除,布布扣,bubuko.com

不通过AppStore,在iOS设备上直接安装应用程序的原理

本文转载至  http://mobile.51cto.com/hot-439095.htm 通过itms-services协议,可以通过safari浏览器直接在iOS设备上安装应用程序.利用这种方式,只要在内网布置一个服务器,测试人员只需要通过测试设备的safari浏览器访问特定的url既可以实现安装,然后测试了. AD:WOT2015 互联网运维与开发者大会 热销抢票 通过itms-services协议,可以通过safari浏览器直接在iOS设备上安装应用程序.利用这种方式,只要在内网布置一个

在Android和iOS设备上调用C++代码

不少Android和iOS项目中,因为种种原因不得不调用C/C++代码.这篇文章主要讲述如何通过Objective-C++.NDK技术在iOS及Android设备上调用C/C++代码. 主要工作原理 主要工作原理,如上图所示.Google Android提供NDK以便支持C/C++代码,而iOS可以通过Objective-C++(*.mm)来支持C/C++代码的编译运行.如果有朋友钻研过Cocos2d-x的话,应该对上述这些内容非常熟悉.好了,废话不多说,开始提供干货. 需要调用的C++代码 我

【转】使IFRAME在iOS设备上支持滚动

原文链接: Scroll IFRAMEs on iOS原文日期: 2014年07月02日 翻译日期: 2014年07月10日翻译人员: 铁锚 很长时间以来, iOS设备上Safari中超出边界的元素将不能滚动,处理这种问题对开发人员来说一直是种折磨. 对原文作者的博客来说,这特别让人无奈,因为demo程序都是在沙箱IFRAMEs中运行的,这是为了不影响博客网站的AJAX页面加载策略.  通过一些研究发现,只要设置两个CSS属性,以及加上一个元素,这个问题就解决了. Here we go!(译者注

iOS企业版app部署到自己服务器,不通过AppStore,在iOS设备上直接安装应用程序

iOS企业版app部署到服务器 .正对ios升级得ios7 以后,plist文件必须放到 https得服务器上了,http不可以用了 .plist参照模板如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyLis