iOS第三方音频框架TheAmazingAudioEngine使

作者: AntonyWong | 来源:发表于2016-01-28 16:51 被阅读9626次

    2017年3月8日更新:

    TheAmazingAudioEngine这个Framework,作者Michael由于工作和生活(要当爹了)等原因,已经很少更新、维护(seldomly receive updates)。作者建议使用AudioKit(暂时没有用过)。所以各位客官,自行甄别是否使用。具体详见

    另外,之前有部分朋友发来简信交流提问,因为一直在忙,没有一一回复,非常抱歉。不过,我建议提问的朋友,把你们具体遇到的问题,表述清楚,减少沟通成本,我也方便回复。邮箱比较常用:aeq2005@163.com,谢谢大家。


    本文适读对象:

    • 第一次用TheAmazingAudioEngine实现音效的读者。
    • 第一次用TheAmazingAudioEngine实现音频播放、录制的读者。
    • 想了解iOS音频开发框架概况的读者。

    概述

    TheAmazingAudioEngineMichael Tyson开源的iOS第三方音频框架。很多音频类APP应用这个框架作开发。

    应用这个框架,可以比较方便地实现iOS音频开发中的各种音效的实现。

    iOS开发中的音频框架

    开始之前,制作了这张图,或许可以更清楚地了解iOS开发中各种音频框架以及其结构关系。(基于官方文档 Using Audio 及objc中国 音频API一览 一文整理。如有谬误,请斧正,谢谢。)

    iOS下各种音频框架iOS下各种音频框架

    TheAmazingAudioEngine就是基于AudioUnit框架、AudioToolBox框架、AVFoundation框架的封装,使其更方便使用。

    音频的播放

    这部分和官方AVAudioPalyer以及AVAudioEngine都比较类似,拿到文件路径、或者音频buffer,调用相关方法播放即可,这里举例文件的播放。
    具体步骤:

    • 创建AEAudioController对象;
    • 拿到音频的路径(一个NSURL对象);
    • 根据音频路径创建AEAudioFilePlayer对象;
    • 通过AEAudioController的addChannels:方法将AEAudioFilePlayer对象add到AEAudioController对象中即可。
      范例如下:
    #pragma mark - 音频播放
    - (void)playNewSongCH1:(NSURL *)songURL {
        if (_selectedSongCH1Player) {
            [_audioController removeChannels:@[_selectedSongCH1Player]];
            _selectedSongCH1Player = nil;
        }
        
        // 创建AEAudioFilePlayer对象
        _selectedSongCH1Player = [[AEAudioFilePlayer alloc] initWithURL:songURL error:nil];
        
        // 进行播放
        [_audioController addChannels:@[_selectedSongCH1Player]];
    }
    

    关于音频文件路径的获取,如果是直接拖进Xcode的文件,利用文件名及后缀即可创建NSURL对象,如下:

    // 歌曲名和后缀名
    static NSString *audioFileName   = @"leftRightTest";
    static NSString *audioFileFormat = @"mp3";
    
    NSURL *songURL = [[NSBundle mainBundle] URLForResource:audioFileName
                                             withExtension:audioFileFormat];
    
    

    如果是想拿手机中的歌曲,则通过MPMediaPickerController的委托方法mediaPicker:didPickMediaItems:方法获得,如下:

    #pragma mark - MPMediaPickerControllerDelegate
    - (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {
    
        // 我这里要播放两首歌,所以有两个MPMediaPickerController对象,这里作一个判断
        if (mediaPicker == _mediaCH1PickerController) {
        
            // mediaItemCollection.representativeItem.assetURL这一句即可拿到使用者选择歌曲的URL
            // 备注:这里已经将播放歌曲的方法playNewSongCH1:封装到自定义的engine类中
            [[HNMCManager shareManager].engine playNewSongCH1:mediaItemCollection.representativeItem.assetURL];
        } 
        else {
            [[HNMCManager shareManager].engine playNewSongCH2:mediaItemCollection.representativeItem.assetURL];
        }
        
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    

    音频的录制

    普通录制(录完再播)

    步骤:

    • 创建AERecorder对象;
    • 获取录音文件的保存路径;
    • 通过AEAudioController的addInputReceiver:方法(录制麦克风的声音)或addOutputReceiver:方法(录制手机喇叭的声音)将AERecorder对象add到AEAudioController对象中。

    范例:

    // 保存的录音文件名字
    static NSString *ch1RecorderFileName = @"ch1Recording.m4a";
    
    #pragma mark - 开始录音
    - (void)setupCH1RecorderBeginRecording {
        // 实例化AERecorder对象
        _ch1Recorder = [[AERecorder alloc] initWithAudioController:_audioController];
        
        // 获取录制后文件存放的路径
        NSString *filePath = [self getFilePathWithFileName:ch1RecorderFileName];
        
        NSError *error = nil;
        if (![_ch1Recorder beginRecordingToFileAtPath:filePath fileType:kAudioFileM4AType error:&error]) {
            return;
        }
        
        // 同时录制输入及输出通道的声音(即既录人声,也录手机播放的声音)
        [_audioController addInputReceiver:_ch1Recorder];
        [_audioController addOutputReceiver:_ch1Recorder];
    }
    
    #pragma mark Helper Method
    - (NSString *)getFilePathWithFileName:(NSString *)fileName {
        NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
        NSString *filePath = [documentsFolder stringByAppendingPathComponent:fileName];
        return filePath;
    }
    
    #pragma mark - 停止录音
    - (void)stopCH1Recording {
        if (_ch1Recorder) {
            [_ch1Recorder finishRecording];
            [_audioController removeInputReceiver:_ch1Recorder];
            [_audioController removeOutputReceiver:_ch1Recorder];
            _ch1Recorder = nil;
        }
    }
    
    #pragma mark - 播放录音
    - (void)playRecordCH1 {
        // 通过文件名拿到文件路径
        NSString *filePath = [self getFilePathWithFileName:ch1RecorderFileName];
        
        // 如果文件不存在,结束
        if ( ![[NSFileManager defaultManager] fileExistsAtPath:filePath] ) {
            return;
        }
        
        NSError *error = nil;
        
        // 利用AEAudioFilePlayer对象进行播放
        _ch1RecorderPlayer = [[AEAudioFilePlayer alloc] initWithURL:[NSURL fileURLWithPath:filePath] error:&error];
        if (!_ch1RecorderPlayer) {
            [[[UIAlertView alloc] initWithTitle:@"Error"
                                        message:[NSString stringWithFormat:@"Couldn't start playback: %@", [error localizedDescription]]
                                       delegate:nil
                              cancelButtonTitle:nil
                              otherButtonTitles:@"OK", nil] show];
            return;
        }
        
        // 播放结束后发送一个播放结束通告(可选步骤)
        __weak HNAudioEngine *weakSelf = self;
        _ch1RecorderPlayer.completionBlock = ^{
            weakSelf.ch1RecorderPlayer = nil;
            [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayRecordCH1Completed object:nil];
        };
        
        // 进行播放
        [_audioController addChannels:@[_ch1RecorderPlayer]];
    }
    

    边录边播

    利用TheAmazingAudioEngine中的AEPlaythroughChannel对象,可以方便地实现边录边播。应用场景,想象一下:可以将手机连上音箱,手机就变成一个扩音器了(当然,应该还有很多噪音、回响之类要处理的)。

    代码比较简单:

    #pragma mark 同步录播(边录边播)相关
    - (void)setupCH1playthroughChannelBeginRecording {
        // 实例化AEPlaythroughChannel对象
        _ch1playthroughChannel = [[AEPlaythroughChannel alloc] init];
        
        // 利用addInputReceiver:方法add到AEAudioController对象中
        [_audioController addInputReceiver:_ch1playthroughChannel];
        
        // 利用addChannels:方法add到AEAudioController对象中
        // 我理解:上一行是为了录制,这一行是为了播放
        [_audioController addChannels:@[_ch1playthroughChannel]];
    }
    
    #pragma mark 设置音量
    - (void)setupCH1playthroughChannelVolume:(double)volume {
        if (_ch1playthroughChannel) {
            _ch1playthroughChannel.volume = volume;
        }
    }
    
    #pragma mark 停止
    - (void)stopCH1Playthrough {
        if (_ch1playthroughChannel) {
            [_audioController removeInputReceiver:_ch1playthroughChannel];
            [_audioController removeChannels:@[_ch1playthroughChannel]];
            _ch1playthroughChannel = nil;
        }
    }
    

    音效的实现

    所有音效都是基于AEAudioUnitFilter类实现的。

    TheAmazingAudioEngine上的音效比苹果官方的AVAudioEngine丰富且容易实现。

    总的步骤:

    • 创建AEAudioUnitFilter或其子类对象
    • 用AEAudioController的addFilter:方法将Filter对象add到AEAudioController对象中
    • 设置相关属性值,实现音效的控制

    举例:

    实现高通音效

    该框架有现成的高通音效类:

    #pragma mark 高通音效
    - (void)setupFilterHighPass:(double)cutoffFrequency {
        // 创建并添加AEAudioUnitFilter实例
        [self addHighpassFilter];
        
        // 设置相关属性值,达到音效的控制
        _highPassFilter.cutoffFrequency = cutoffFrequency;
    }
    
    - (void)addHighpassFilter {
         // _highPassFilter是AEHighPassFilter类的实例
        // AEHighPassFilter是AEAudioUnitFilter的子类
        if (!_highPassFilter) {
            _highPassFilter = [[AEHighPassFilter alloc] init];
            [_audioController addFilter:_highPassFilter];
        } else {
            if ( ![_audioController.filters containsObject:_highPassFilter] ) {
                [_audioController addFilter:_highPassFilter];
            }
        }
    }
    
    

    实现EQ调整

    因为本来对音频相关领域的概念、知识不太了解,实现EQ调整还颇费了一番周折。需要实现的EQ调整类似下图:

    要实现10段EQ的音效调整要实现10段EQ的音效调整

    可以通过AEParametricEqFilter类实现,该类也是AEAudioUnitFilter的子类,要实现10段EQ值的调整,就要创建10个AEParametricEqFilter对象,给centerFrequency属性赋值20Hz-20000Hz之间的值(取决于你要调整哪个频率的声音)。而具体音效调整,则是调整增益值(通过gain属性),值范围:-20dB to 20dB。

    #pragma mark EQ音效
    // 创建10个AEParametricEqFilter对象
    - (void)creatEqFliters {
        _eq20HzFilter  = [[AEParametricEqFilter alloc] init];
        _eq50HzFilter  = [[AEParametricEqFilter alloc] init];
        _eq100HzFilter = [[AEParametricEqFilter alloc] init];
        _eq200HzFilter = [[AEParametricEqFilter alloc] init];
        _eq500HzFilter = [[AEParametricEqFilter alloc] init];
        _eq1kFilter    = [[AEParametricEqFilter alloc] init];
        _eq2kFilter    = [[AEParametricEqFilter alloc] init];
        _eq5kFilter    = [[AEParametricEqFilter alloc] init];
        _eq10kFilter   = [[AEParametricEqFilter alloc] init];
        _eq20kFilter   = [[AEParametricEqFilter alloc] init];
        _eqFilters     = @[_eq20HzFilter, _eq50HzFilter, _eq100HzFilter, _eq200HzFilter, _eq500HzFilter, _eq1kFilter, _eq2kFilter, _eq5kFilter, _eq10kFilter, _eq20kFilter];
    }
    
    - (void)setupFilterEq:(NSInteger)eqType value:(double)gain {
        switch (eqType) {
            case EQ_20Hz: {
                // 设置需要调整的频率,并将传入的增益值gain赋值给gain属性,达到音效调整效果
                [self setupEqFilter:_eq20HzFilter centerFrequency:20 gain:gain];
                break;
            }
            case EQ_50Hz: {
                [self setupEqFilter:_eq50HzFilter centerFrequency:50 gain:gain];
                break;
            }
            case EQ_100Hz: {
                [self setupEqFilter:_eq100HzFilter centerFrequency:100 gain:gain];
                break;
            }
            case EQ_200Hz: {
                [self setupEqFilter:_eq200HzFilter centerFrequency:200 gain:gain];
                break;
            }
            case EQ_500Hz: {
                [self setupEqFilter:_eq500HzFilter centerFrequency:500 gain:gain];
                break;
            }
            case EQ_1K: {
                [self setupEqFilter:_eq1kFilter centerFrequency:1000 gain:gain];
                break;
            }
            case EQ_2K: {
                [self setupEqFilter:_eq2kFilter centerFrequency:2000 gain:gain];
                break;
            }
            case EQ_5K: {
                [self setupEqFilter:_eq5kFilter centerFrequency:5000 gain:gain];
                break;
            }
            case EQ_10K: {
                [self setupEqFilter:_eq10kFilter centerFrequency:10000 gain:gain];
                break;
            }
            case EQ_20K: {
                [self setupEqFilter:_eq20kFilter centerFrequency:20000 gain:gain];
                break;
            }
        }
    }
    
    - (void)setupEqFilter:(AEParametricEqFilter *)eqFilter centerFrequency:(double)centerFrequency gain:(double)gain {
        if ( ![_audioController.filters containsObject:eqFilter] ) {
            for (AEParametricEqFilter *existEqFilter in _eqFilters) {
                if (eqFilter == existEqFilter) {
                    [_audioController addFilter:eqFilter];
                    break;
                }
            }
        }
        
        eqFilter.centerFrequency = centerFrequency;
        eqFilter.qFactor         = 1.0;
        eqFilter.gain            = gain;
    }
    

    以上就是应用TheAmazingAudioEngine框架进行音频播放、录制、音效实现的一次简单实践分享。

    当然,这个框架能做的事情还有很多,有时间的朋友可以继续发掘。

    尊重劳动成果,转载请注明出处,谢谢。

    相关文章

      网友评论

      • 吾心傲翔:好文章,参考一下
      • 乔兰伊雪:你好,我想问下他这个库是不是没有暂停功能,录音录一半有事儿暂停了,然后再恢复录音这个库要怎么实现呢?
        a3cd56efd2be:你好,请问这个功能你实现了吗,我也需要做这个:joy:
      • 听见_73b6:你好,请问一下调节均衡器之后如何保存调节后的音乐
      • 愤怒的三维:请问一下录制的时候同时播放,能过滤掉从扬声器里发出的声音吗?
      • 缺舟:博主 我想录制amr格式的音频文件 但是总是失败我能问哈 怎么回事啊 改变了录制类型了
      • d920e665d3d1:大神 这个 框架能依次播放多个音频吗
      • 旸君颜:有没有AudioKit的使用心得啊
      • 独爱ii:AERecorder这个对象找不到
      • 095b62ead3cd:假如说我在录音的时候需要播放伴奏与我的声音合成应该怎么做呢
        b909623356fb:@adougei 你好,我现在也要做这个功能,你这边有实现了的思路吗?
        adougei:你好,请问你是否实现了播放伴奏并录音功能,我现在也在做这个,求指导,qq:934129580
      • 谢衣丶:请问这个player能获取音频文件的总时长和当前时间吗
      • 嘿嘿和露红叶:这个框架能不能做两个音频文件的合成?
      • 515203f1d6bc:博主 你好 能不能再多分享点详细的demo 302822245 方便的话 加个好友
      • 独爱ii:博主,没看明白怎么导入这个框架,怎么具体调用,能不能加我QQ教教俺!我QQ:1315221709
        暴走的菜鸟:@独爱ii 问一下你弄清楚怎么导入这个框架了吗?求指教
      • ee8e22e754a9:边录边播,用蓝牙音响好像完全不能播放? 这个是什么原因呢
        Bluex:你的边录边播怎么实现的啊
      • 隔壁小鱼:你好, 请问这个框架能播放APE和flac这些无损格式吗?
      • 3c9d8ac0aaed:你好,请问你用过这个框架的AENewTimePitchFilter.h滤镜吗?为何我改变音调后录音,在模拟器上是可以正常播放的,但在真机上会出现播放声音速度特别快导致失真的问题.
        3c9d8ac0aaed:@luckyncl 直接调用AERecorder这个类里边的方法就可以录了,滤镜的音效就加在里面呢.
        luckyncl:@赵宇星 你好,问一下,添加完滤镜,怎么才可以得到处理过的音频呢
      • Gong_Hao:按照你的方法,录音的时候有一个错误事:AEAudioFileWriter.m:198: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat: -66563

        麻烦问一下是为什么?
      • shuolol:你好 我用这个做2个音频播放的时候 切换第二个音频的播放地址也就是音频文件
        内存没办法释放 一直在增加 退出这个控制器以后 也没办法释放
      • bb017a1a92bf:博主你好,我想请教一下,TAAE是不是只支持本地音频文件的播放,不能播放远程的音频文件(就是网络上的音乐),谢谢。
        AntonyWong:@曹小虎 看了一下网上,有人用AVAudioEngine实现,以buffer的形式播放、添加音效,但是性能不行。貌似只能用底层的框架自己实现了。
        bb017a1a92bf:@AntonyWong 嗯,这个我也有看到,如果我要实现远程音频播放并且添加自定义均衡器效果,是不是只能用AudioUnit实现呢?请问博主有其他建议吗??
        AntonyWong:@曹小虎 看框架作者的回复,似乎是不支持的。希望能帮到你。http://forum.theamazingaudioengine.com/discussion/3/is-framework-support-audio-streaming-from-network
      • 飞龙在天aaa:试了不少音效,还是没弄出适合唱歌的那种效果,请问你有没有试出比较好的唱歌效果的音效么?类似唱吧,VV音乐那种的。
        嘿嘿和露红叶:@扇子飞舞 你找到K歌功能的音效了吗?
        AntonyWong:@扇子飞舞 我暂时没有做过这方面的尝试。可能需要更多音乐方面的理论知识。
        飞龙在天aaa:@扇子飞舞 用这个 AEReverbFilter 效果的混响太大了,声音失真比较严重。
      • 土豆炖排骨:学习了
        AntonyWong:@土豆炖排骨 互相切磋:)
      • 8be9fb8d8f49:讚一個
        AntonyWong:@ilyi1116 感谢赞
      • 667a69f9a9e1:赞
        AntonyWong:@667a69f9a9e1 谢谢
      • 无wu:会用到的
        AntonyWong:@80cf3878d0f1 谢谢。希望能帮到你
      • ff4027bec949:不错
        AntonyWong:@洪文城 谢谢。

      本文标题:iOS第三方音频框架TheAmazingAudioEngine使

      本文链接:https://www.haomeiwen.com/subject/stldkttx.html