1. 播放音效
#pragma mark 基本使用
- (void)baseUse
{
//1. 創(chuàng)建URL地址
NSURL *url = [[NSBundle mainBundle] URLForResource:@"buyao.wav" withExtension:nil];
//2. 系統(tǒng)音效文件 SystemSoundID = UInt32
SystemSoundID soundID;
//3. 創(chuàng)建音效文件
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
//4. 播放音效文件
//4.1 不帶震動的播放
//AudioServicesPlaySystemSound(soundID);
//4.2 帶振動的播放 --> 真機(jī)才有效果
AudioServicesPlayAlertSound(soundID);
//5. 如果不需要播放了, 需要釋放音效所占用的內(nèi)存
AudioServicesDisposeSystemSoundID(soundID);
}
2. 簡單封裝
MTAudioTool.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface MTAudioTools : NSObject
/** 播放系統(tǒng)音效*/
+ (void)playSystemSoundWithURL:(NSURL *)url;
/** 播放震動音效*/
+ (void)playAlertSoundWithURL:(NSURL *)url;
/** 清空音效文件的內(nèi)存*/
+ (void)clearMemory;
@end
MTAudioTool.m
#import "MTAudioTools.h"
/** 緩存字典*/
static NSMutableDictionary *_soundIDDict;
@implementation MTAudioTools
// 只要頭文件參與了編譯調(diào)用
//+ (void)load
/** 緩存字典初始化*/
+ (void)initialize
{
_soundIDDict = [NSMutableDictionary dictionary];
}
+ (void)playSystemSoundWithURL:(NSURL *)url
{
// 不帶震動的播放
AudioServicesPlaySystemSound([self loadSoundIDWithURL:url]);
}
/** 播放震動音效*/
+ (void)playAlertSoundWithURL:(NSURL *)url
{
// 帶震動的播放
AudioServicesPlayAlertSound([self loadSoundIDWithURL:url]);
}
#pragma mark 播放音效的公用方法
+ (SystemSoundID)loadSoundIDWithURL:(NSURL *)url
{
// 思路思路
// soundID重復(fù)創(chuàng)建 --> soundID每次創(chuàng)建, 就會有對應(yīng)的URL地址產(chǎn)生
// 可以將創(chuàng)建后的soundID 及 對應(yīng)的URL 進(jìn)行緩存處理
//1. 獲取URL的字符串
NSString *urlStr = url.absoluteString;
//2. 從緩存字典中根據(jù)URL來取soundID 系統(tǒng)音效文件
SystemSoundID soundID = [_soundIDDict[urlStr] intValue];
//需要在剛進(jìn)入的時候, 判斷緩存字典是否有url對應(yīng)的soundID
//3. 判斷soundID是否為0, 如果為0, 說明沒有找到, 需要創(chuàng)建
if (soundID == 0) {
//3.1 創(chuàng)建音效文件
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
//3.2 緩存字典的添加鍵值
_soundIDDict[urlStr] = @(soundID);
}
return soundID;
}
/** 清空音效文件的內(nèi)存*/
+ (void)clearMemory
{
//1. 遍歷字典
[_soundIDDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//2. 清空音效文件的內(nèi)存
SystemSoundID soundID = [obj intValue];
AudioServicesDisposeSystemSoundID(soundID);
}];
}
@end
ViewController.m
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "MTAudioTools.h"
/**
1. 導(dǎo)入AVFoundation框架
2. 創(chuàng)建音效文件
3. 播放音效文件
音效: 非常短的音樂, 一般來說30秒以內(nèi)的聲音, 都算作音效
*/
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark 點(diǎn)擊播放音效
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1. 獲取URL地址
NSURL *url = [[NSBundle mainBundle] URLForResource:@"buyao.wav" withExtension:nil];
//2. 調(diào)用工具類播放音效
//[MTAudioTools playSystemSoundWithURL:url];
[MTAudioTools playAlertSoundWithURL:url];
}
#pragma mark 當(dāng)前控制器收到內(nèi)存警告時會調(diào)用的方法
- (void)didReceiveMemoryWarning
{
// 局部音效需要在這里進(jìn)行釋放
[MTAudioTools clearMemory];
NSLog(@"%s",__func__);
}
@end
3. 音樂播放
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
/**
1. 需要使用AVFoundatiaon框架
2. 創(chuàng)建音樂播放器
3. 根據(jù)需求, 進(jìn)行播放/暫停/停止
*/
@interface ViewController ()
@property (nonatomic, strong) AVAudioPlayer *player;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 創(chuàng)建音樂播放器
//1. 獲取URL路徑
NSURL *url = [[NSBundle mainBundle] URLForResource:@"我愛你你卻愛著她.mp3" withExtension:nil];
//2. 創(chuàng)建一個error對象
NSError *error;
//3. 創(chuàng)建音樂播放器
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (error) {
NSLog(@"有錯誤產(chǎn)生");
}
}
- (IBAction)playClick:(id)sender {
//1. 準(zhǔn)備播放 --> 將音頻文件加載到內(nèi)存中 --> 這句話可以不寫 --> play會隱式調(diào)用prepareToPlay方法. 但是規(guī)范來說, 還是會寫上
[self.player prepareToPlay];
//2. 開始播放
[self.player play];
}
- (IBAction)pauseClick:(id)sender {
// 暫停播放
[self.player pause];
}
- (IBAction)stopClick:(id)sender {
// 停止播放
[self.player stop];
// 歸零操作 / 時間重置 currentTime--> 秒為單位
self.player.currentTime = 0;
}
@end
4. 錄音
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
/**
1. 需要使用AVFoundatiaon框架
2. 創(chuàng)建錄音對象
3. 根據(jù)需求, 進(jìn)行錄音/暫停/停止
*/
@interface ViewController ()
@property (nonatomic, strong) AVAudioRecorder *recorder;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
@implementation ViewController
/**
//錄音
// //settings 設(shè)置參數(shù) 錄音相關(guān)參數(shù) 聲道 速率 采樣率
// NSMutableDictionary *setting = [NSMutableDictionary dictionary];
// // 音頻格式
// setting[AVFormatIDKey] = @(kAudioFormatAppleIMA4);
// // 音頻采樣率
// setting[AVSampleRateKey] = @(600.0);
// // 音頻通道數(shù)
// setting[AVNumberOfChannelsKey] = @(1);
// // 線性音頻的位深度
// setting[AVLinearPCMBitDepthKey] = @(8);
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 創(chuàng)建錄音對象
//1. 獲取URL地址 --> 具體的文件名路徑
//--> 注意之前是獲取資源地址, 這里是指要將錄音存放到哪里
NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"recorde.wav"];
//2. 將path字符串轉(zhuǎn)換成NSURL --> file://
NSURL *url = [NSURL fileURLWithPath:path];
//3. 配置設(shè)置字典
// 如果傳空, 默認(rèn)就是高質(zhì)量
// 錄音的參數(shù)值
NSMutableDictionary *setting = [NSMutableDictionary dictionary];
//4. 創(chuàng)建Error對象 __autoreleasing 可以不加, 加上之后是最標(biāo)準(zhǔn)寫法
__autoreleasing NSError *error;
//5. 創(chuàng)建錄音對象
self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:setting error:&error];
// if (error) {
// NSLog(@"error");
// }
//6. 打開分貝的檢測
self.recorder.meteringEnabled = YES;
//7. 如果要在真機(jī)運(yùn)行, 還需要一個session類, 并且制定分類為錄音
AVAudioSession *session = [AVAudioSession new];
[session setCategory:AVAudioSessionCategoryRecord error:nil];
}
- (IBAction)recordClick:(id)sender {
//1. 準(zhǔn)備錄音
[self.recorder prepareToRecord];
//2. 開始錄音 --> 如果同一路徑再次錄音, 則會覆蓋之前的文件
[self.recorder record];
//3. 進(jìn)行分貝的循環(huán)檢測 --> 添加計時器
[self updateMetering];
}
- (IBAction)pauseClick:(id)sender {
// 暫停錄音 --> 如果用戶只是暫停了, 應(yīng)該提示用戶進(jìn)行保存操作
[self.recorder pause];
// 暫停循環(huán)
self.displayLink.paused = YES;
}
- (IBAction)stopClick:(id)sender {
// 停止錄音 --> 之后停止錄音時, 最終的錄音文件才會生產(chǎn)
[self.recorder stop];
// 暫停循環(huán)
self.displayLink.paused = YES;
NSLog(@"停止錄音");
}
#pragma mark 添加計時器
- (void)updateMetering
{
// 如果沒有displayLink就創(chuàng)建
if (self.displayLink == nil) {
//1. 創(chuàng)建displayLink
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateMeter)];
//2. 添加到運(yùn)行循環(huán)中
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
// 判斷如果暫停了循環(huán), 就打開
if (self.displayLink.isPaused) {
self.displayLink.paused = NO;
}
}
#pragma mark 循環(huán)調(diào)用的方法
- (void)updateMeter
{
//需求: 自動停止錄音 --> 根據(jù)分貝的大小來判斷
//1. 我們需要獲取分貝信息
//2. 設(shè)置分貝如果小于某個值, 一定時間后, 自動停止
//1. 更新分貝信息
[self.recorder updateMeters];
//2. 獲取分貝信息 --> iOS直接傳0
// 0 ~ -160 , 值最大是0, 最小是-160. 系統(tǒng)返回的是負(fù)值
CGFloat power = [self.recorder averagePowerForChannel:0];
//3. 實(shí)現(xiàn)2S自動停止
static NSInteger number;
//displayLink,一秒默認(rèn)是60次, 如果120此的調(diào)用都小于某個分貝值, 我們就可以認(rèn)為要自動停止
//3.1 先判斷用戶是否小于某個分貝值 --> 用戶是否沒說話
if (power < -30) {
//3.2 如果發(fā)現(xiàn)很安靜, 我們就可以記錄一下, number進(jìn)行疊加
number++;
//3.3 如果發(fā)現(xiàn)120次了, 都小于設(shè)定的分貝值
if (number / 60 >= 2) {
//3.4 調(diào)用停止方法
[self stopClick:nil];
}
} else {
number = 0;
}
NSLog(@"power: %f",power);
}
@end