TheAmazingAudioEngine是Michael Tyson开源的iOS第三方音频框架。不少音频类APP应用这个框架做开发。html
应用这个框架,能够比较方便地实现iOS音频开发中的各类音效的实现。ios
开始以前,制做了这张图,或许能够更清楚地了解iOS开发中各类音频框架以及其结构关系。(基于官方文档 Using Audio 及objc中国 音频API一览 一文整理。若有谬误,请斧正,谢谢。)git
iOS下各类音频框架github
TheAmazingAudioEngine就是基于AudioUnit框架、AudioToolBox框架、AVFoundation框架的封装,使其更方便使用。app
这部分和官方AVAudioPalyer以及AVAudioEngine都比较相似,拿到文件路径、或者音频buffer,调用相关方法播放便可,这里举例文件的播放。
具体步骤:框架
#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对象,以下:ide
// 歌曲名和后缀名 static NSString *audioFileName = @"leftRightTest"; static NSString *audioFileFormat = @"mp3"; NSURL *songURL = [[NSBundle mainBundle] URLForResource:audioFileName withExtension:audioFileFormat];
若是是想拿手机中的歌曲,则经过MPMediaPickerController的委托方法mediaPicker:didPickMediaItems:方法得到,以下:post
#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]; }
步骤:ui
范例:spa
// 保存的录音文件名字 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丰富且容易实现。
总的步骤:
举例:
该框架有现成的高通音效类:
#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调整相似下图:
要实现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框架进行音频播放、录制、音效实现的一次简单实践分享。
固然,这个框架能作的事情还有不少,有时间的朋友能够继续发掘。