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

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

iOS开发中的音频框架

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

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

音频的播放

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

  • 创建AEAudioController对象;

  • 拿到音频的路径(一个NSURL对象);

  • 根据音频路径创建AEAudioFilePlayer对象;

  • 通过AEAudioController的addChannels:方法将AEAudioFilePlayer对象add到AEAudioController对象中即可。 范例如下:

    备注:代码在Xcode是对得比较整齐的,在这里有点乱掉,忍受一下,抱歉。

#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调整类似下图:

可以通过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: gain:gain];
break;
}
case EQ_50Hz: {
[self setupEqFilter:_eq50HzFilter centerFrequency: gain:gain];
break;
}
case EQ_100Hz: {
[self setupEqFilter:_eq100HzFilter centerFrequency: gain:gain];
break;
}
case EQ_200Hz: {
[self setupEqFilter:_eq200HzFilter centerFrequency: gain:gain];
break;
}
case EQ_500Hz: {
[self setupEqFilter:_eq500HzFilter centerFrequency: gain:gain];
break;
}
case EQ_1K: {
[self setupEqFilter:_eq1kFilter centerFrequency: gain:gain];
break;
}
case EQ_2K: {
[self setupEqFilter:_eq2kFilter centerFrequency: gain:gain];
break;
}
case EQ_5K: {
[self setupEqFilter:_eq5kFilter centerFrequency: gain:gain];
break;
}
case EQ_10K: {
[self setupEqFilter:_eq10kFilter centerFrequency: gain:gain];
break;
}
case EQ_20K: {
[self setupEqFilter:_eq20kFilter centerFrequency: 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框架进行音频播放、录制、音效实现的一次简单实践分享。

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

音频框架TheAmazingAudioEngine实现音效的更多相关文章

  1. Android音频系统之音频框架

    1.1 音频框架 转载请注明,From LXS, http://blog.csdn.net/uiop78uiop78/article/details/8796492 Android的音频系统在很长一段 ...

  2. 11.4、Libgdx的音频之录制PCM音效

    (官网:www.libgdx.cn) 可以通过AudioRecorder接口访问PCM数据.通过如下方式创建一个接口实例: AudioRecorder recorder = Gdx.audio.new ...

  3. ios平台cocos2d-x播放音频、视频、音效的Demo(支持网络视频)

    最近由ios应用转做游戏,游戏开始时需要播放一个视频,由于本身cocos2d-x播放视频的相关库,在网上搜到的资料都不是很全,我自己试过在cocos2dx直接调用ios的MediaPlayer来播放, ...

  4. iOS开发系列--音频播放(音效和音乐)播放本地的

    音频 在iOS中音频播放从形式上可以分为音效播放和音乐播放.前者主要指的是一些短音频播放,通常作为 点缀音频,对于这类音频不需要进行进度.循环等控制.后者指的是一些较长的音频,通常是主音频,对于这些音 ...

  5. iOS开发——音频篇——音效的播放

    一.简单介绍 简单来说,音频可以分为2种 (1)音效 又称“短音频”,通常在程序中的播放时长为1~2秒 在应用程序中起到点缀效果,提升整体用户体验 (2)音乐 比如游戏中的“背景音乐”,一般播放时间较 ...

  6. iOS开发系列--音频播放、录音、视频播放、拍照、视频录制

    --iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制, ...

  7. 《转》iOS音频视频初级开发

    代码改变世界 Posts - 73, Articles - 0, Comments - 1539 Cnblogs Dashboard Logout HOME CONTACT GALLERY RSS   ...

  8. iOS开发----音频播放、录音、视频播放、拍照、视频录制

    随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操 ...

  9. iOS音频

    随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操 ...

随机推荐

  1. uva11400 Lighting System Design

    题目大意: 有一个照明系统需要用到n种灯,每种灯的电压为V,电源费用K,每个灯泡费用为C,需要该灯的数量为L.注意到,电压相同的灯泡只需要共享一个对应的电源即可,还有电压低的灯泡可以被电压高的灯泡替代 ...

  2. Picture poj1177

    Picture Time Limit: 2000MS   Memory Limit: 10000K Total Submissions: 12265   Accepted: 6484 Descript ...

  3. [Xcode 实际操作]六、媒体与动画-(5)使用CoreImage框架给图片添加马赛克效果

    目录:[Swift]Xcode实际操作 本文将演示如何使用CoreImage图像处理框架,给图片添加像素化的滤镜效果. 在项目导航区,打开视图控制器的代码文件[ViewController.swift ...

  4. Luogu P3941 入阵曲【前缀和】By cellur925

    题目传送门 题目大意:给你一个\(n\)*\(m\)的矩阵,每个位置都有一个数,求有多少不同的子矩阵使得矩阵内所有数的和是\(k\)的倍数. 数据范围给的非常友好233,期望得到的暴力分:75分.前1 ...

  5. 洛谷P4719 【模板】动态dp

    https://www.luogu.org/problemnew/show/P4719 大概就是一条链一条链的处理(“链”在这里指重链),对于每一条链,对于其上每一个点,先算出它自身和所有轻儿子的贡献 ...

  6. hdu5726-GCD-ST表+二分

    先用st表处理出所有l-r的GCD值,然后二分求得这些值一共出现了多少次. #include<bits/stdc++.h> #define inf 0x3f3f3f3f ; using n ...

  7. AI入门丨开源学习资源推荐

    现在AI大热,网上的资源也非常多,让人眼花缭乱.非科班的我,经过半年的摸索,也算马马虎虎入了坑.下面整理了我认为不错的学习资源,大部分我都看过,以分享给更多的人.我会不断保持更新,也欢迎大家补充. P ...

  8. 把本地项目上传到github 不使用eclipse

    https://blog.csdn.net/m0_37725003/article/details/80904824

  9. nginx的配置文件server_name的意义 location意义

    配置不同的域名      不同域名都可以有首地址 location   同一域名下   分发到不同的路径   或者项目

  10. cordova开发的坑

    相机 根据android版本,有各种问题. 1.拍照后不会自动清内存造成内存溢出,导致照片不会自动旋转,拍多张之后会自动刷新网页. 2.小米手机安卓7.0时,拍出的照片不会自动旋转,需要安卓端代码调整 ...