iOS设备上回声消除的例子

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

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

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

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

    1. AVAudioSession* session = [AVAudioSession sharedInstance];

    2. [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];

    3. [session setActive:YES error:nil];

  2. 初始化一个AUGraph,创建一个AUNode,并将之添加到graph上。一般来说,沟通麦克风/扬声器的AUNode,其类型应该是RemoteIO,但是RemoteIO不带回声消除功能,VoiceProcessingIO类型的才带。

    1. AudioComponentDescription inputcd = {0};

    2. inputcd.componentType = kAudioUnitType_Output;

    3. //inputcd.componentSubType = kAudioUnitSubType_RemoteIO;

    4. //we can access the system's echo cancellation by using kAudioUnitSubType_VoiceProcessingIO subtype

    5. inputcd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;

    6. 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格式);配置回调函数。

    1. //Open input of the bus 1(input mic)

    2. UInt32 enableFlag = 1;

    3. CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,

    4. kAudioOutputUnitProperty_EnableIO,

    5. kAudioUnitScope_Input,

    6. 1,

    7. &enableFlag,

    8. sizeof(enableFlag)),

    9. "Open input of bus 1 failed");

    10. //Open output of bus 0(output speaker)

    11. CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,

    12. kAudioOutputUnitProperty_EnableIO,

    13. kAudioUnitScope_Output,

    14. 0,

    15. &enableFlag,

    16. sizeof(enableFlag)),

    17. "Open output of bus 0 failed");

    18. //Set up stream format for input and output

    19. streamFormat.mFormatID = kAudioFormatLinearPCM;

    20. streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;

    21. streamFormat.mSampleRate = 44100;

    22. streamFormat.mFramesPerPacket = 1;

    23. streamFormat.mBytesPerFrame = 2;

    24. streamFormat.mBytesPerPacket = 2;

    25. streamFormat.mBitsPerChannel = 16;

    26. streamFormat.mChannelsPerFrame = 1;

    27. CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,

    28. kAudioUnitProperty_StreamFormat,

    29. kAudioUnitScope_Input,

    30. 0,

    31. &streamFormat,

    32. sizeof(streamFormat)),

    33. "kAudioUnitProperty_StreamFormat of bus 0 failed");

    34. CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,

    35. kAudioUnitProperty_StreamFormat,

    36. kAudioUnitScope_Output,

    37. 1,

    38. &streamFormat,

    39. sizeof(streamFormat)),

    40. "kAudioUnitProperty_StreamFormat of bus 1 failed");

    41. //Set up input callback

    42. AURenderCallbackStruct input;

    43. input.inputProc = InputCallback;

    44. input.inputProcRefCon = myStruct;

    45. CheckError(AudioUnitSetProperty(myStruct->remoteIOUnit,

    46. kAudioUnitProperty_SetRenderCallback,

    47. kAudioUnitScope_Global,

    48. 0,//input mic

    49. &input,

    50. sizeof(input)),

    51. "kAudioUnitProperty_SetRenderCallback failed");

  4. 在回调函数inputCallback中,用AudioUnitRender() 函数获取麦克风的声音,存在一个bufferList中。这个bufferList是一个ring结构,存储最新的声音,然后播放旧声音。这样,声音的输入和输出之间,就有了0.5s(可调节)左右的延迟,形成了明显的回声。

  5. 给回声消除添加一个开关。VoiceProcessingIO有一个属性可用来打开/关闭回声消除功能:kAUVoiceIOProperty_BypassVoiceProcessing

    1. UInt32 echoCancellation;

    2. UInt32 size = sizeof(echoCancellation);

    3. CheckError(AudioUnitGetProperty(myStruct.remoteIOUnit,

    4. kAUVoiceIOProperty_BypassVoiceProcessing,

    5. kAudioUnitScope_Global,

    6. 0,

    7. &echoCancellation,

    8. &size),

    9. "kAUVoiceIOProperty_BypassVoiceProcessing failed");

  6. 现在可以开始graph了:

    1. CheckError(AUGraphInitialize(graph),

    2. "AUGraphInitialize failed");

    3. CheckError(AUGraphStart(graph),

    4. "AUGraphStart failed");

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

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

工业上的声音处理中,回声消除是一个重要的话题,重要性不亚于噪声消除、人声放大、自动增益等,尤其是在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];
    
    • 1
    • 2
    • 3
  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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  6. 现在可以开始graph了:

    CheckError(AUGraphInitialize(graph),"AUGraphInitialize failed");
    CheckError(AUGraphStart(graph),"AUGraphStart failed");
    
    • 1
    • 2
    • 3
    • 4

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

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

iOS 音频-AVAudioSession
iOS音频掌柜-- AVAudioSession

在WebRTC应用中,AudioUnit 使用的是Voice-Processing I / O unit (subtype kAudioUnitSubType_VoiceProcessingIO),使用内置的aec和agc等功能, AVAudioSession状态:
Category = AVAudioSessionCategoryPlayAndRecord,Mode = AVAudioSessionModeVoiceChat,无须显式的setMode,使用 Voice-Processing I / O unit 会自动切换为 VoiceChat,至于Options依实际业务情景需求设置。


  1. Category 切换成 AVAudioSessionCategoryPlayback:
    那么AudioUnit record cb 有回调,但是获取到的是静音数据。
  2. 使用AVAudioPlayer播放音频文件,音量非常低:
    可以通过调整mode解决, 播放音频文件时设置为 AVAudioSessionModeDefault,待播放完毕之后再设置为 AVAudioSessionModeVoiceChat。但是这个缺点是,切换成 default mode 后 失去了回音消除功能了。
    然而,像狼人杀的场景,需要一直播放背景音乐,又需要对话过程中回音消除,那么就需要好好的维护好 mode 和 option了。

0人点赞

日记本

作者:暴走大牙
链接:https://www.jianshu.com/p/5addd678c64e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

苹果ios音频的回声消除处理相关推荐

  1. 音频应用(如sip与Voip),编解码API(Ringtone,SoundPool,MediaPlayer),回声消除等(Lame Speex等),OpenSL ES

    3套音频播放API:MediaPlayer,SoundPool,AudioTrack. android录音项目,用单例模式集成了record,并实时转码mp3- https://github.com/ ...

  2. 音频前处理:回声消除、噪声抑制等(音视频SDK高级功能六)

    音频前处理技术一般用于去除语音中的干扰.本篇文章介绍即构科技音视频SDK高级功能第六篇,ZegoLiveRoom SDK 为开发者提供了音频前处理的功能,还是以iOS环境为例. 关于如何使用SDK,请 ...

  3. 可视监控对讲、楼宇对讲等领域中的回声消除、音频降噪

    在实际生活中,对讲是比较常用的功能,如小区单元的视频对讲.监控场所的实时对讲,还有现在很火的智能门铃中的视频对讲,对讲这个功能确实方便了双方的沟通,但这功能的实现并不是那么容易,因为有个技术是较难实现 ...

  4. 微处理器(STM32 wifi芯片)实现音频回声消除

    一.什么是回声消除(AEC) 在回答回声消除之前,我们看这幅图片,如下图所示: 当远端Far-end有说话者讲话时,声音会传到近端(Near-end)的扬声器,然后声音通过空间延时和传输延时重新回到了 ...

  5. 音视频处理三剑客之 AEC:回声产生原因及回声消除原理

    在上一期课程<音视频开发者进阶 -- 音频要素>中,我们从声音三要素.音频模拟信号的数字化和音频数字信号特征等方面,重新认识了"声音"这个老朋友.今天,我们会进一步聊聊 ...

  6. 【iOS】iOS语音通话回音消除(AEC)技术实现

    一.前言 在语音聊天.语音通话.互动直播.语音转文字类应用或者游戏中,需要采集用户的麦克风音频数据,然后将音频数据发送给其它终端或者语音识别服务.如果直接使用采集的麦克风数据,就会存在回音问题.所谓回 ...

  7. 硬货专栏 |深入浅出 WebRTC AEC(声学回声消除)

    前言:近年来,音视频会议产品提升着工作协同的效率,在线教育产品突破着传统教育形式的种种限制,娱乐互动直播产品丰富着生活社交的多样性,背后都离不开音视频通信技术的优化与创新,其中音频信息内容传递的流畅性 ...

  8. iOS音频采集过程中的音效实现

    1.背景 在移动直播中, 声音是主播和观众互动的重要途径之一, 为了丰富直播的内容,大家都会想要在声音上做一些文章, 在采集录音的基础上玩一些花样. 比如演唱类的直播间中, 主播伴随着背景音乐演唱. ...

  9. 苹果iOS 15发布:关机也能定位,ASMR重度用户狂喜,这波库克又“去苹果化”了...

    梦晨 萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 万众瞩目的苹果iOS 15正式版终于来了! 最受关注的更新,要属"实况文本",现在复制图中的文字就像复制文本一样简 ...

最新文章

  1. 中国治理蝗灾是生物防治的成就,根本不是靠鸡靠鸭靠吃货换来的!
  2. signavio-core-components ant build-all-in-one-war failed
  3. R语言实现多线性回归模型预测时间序列数据 MLR models in R
  4. ubuntu服务器在虚拟机中的配置
  5. Panda和numpy库和matplotlib库的安装
  6. sv队列和动态数组的区别_systemverilog学习(4)动态数组
  7. IT餐馆—第二十五回 结对
  8. 虚拟机CentOS8 网络配置
  9. 谷歌中国开发者大会,感悟,激动
  10. 管理口SSH服务存在拒绝服务漏洞(CVE-2016-6515)
  11. 关于jquery mobile 页面闪烁与抖动问题
  12. 如何把DEBIAN变成UBUNTU-DESKTOP最少化安装
  13. 数字图像隐写术之卡方分布
  14. 数仓建模—数据资产管理
  15. html让字数超过多少,css强制省略号 css设置超过多少个字显示省略号
  16. (八)高德地图之添加marker标记点
  17. SpringMVC常用方法大全
  18. linux中python怎么退出_linux 怎么退出python
  19. mysql数据库 去除回车符,换行符,和空格
  20. miix4linux双系统,联想MIIX4笔记本U盘重装win10系统教程

热门文章

  1. selenium自动化测试-鼠标键盘操作
  2. 龙少的Linux配置大全
  3. 解决wordcloud导出图片不清楚
  4. 信创舆情一线--工信部开展APP侵害用户权益专项整治行动
  5. 【Android】App开发-动画效果篇
  6. 第十四届蓝桥杯第三期模拟赛 C/C++ B组 原题与详解
  7. pytorch中的膨胀卷积
  8. Linux 内核配置项详解 myimx6
  9. [web开发]建立本地php MySQL Apache测试服务器
  10. sms 7bit编码