1. 播放音效
#pragma mark 基本使用
- (void)baseUse
{
//1. 创建URL地址
NSURL *url = [[NSBundle mainBundle] URLForResource:@"buyao.wav" withExtension:nil];
//2. 系统音效文件 SystemSoundID = UInt32
SystemSoundID soundID;
//3. 创建音效文件
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
//4. 播放音效文件
//4.1 不带震动的播放
//AudioServicesPlaySystemSound(soundID);
//4.2 带振动的播放 --> 真机才有效果
AudioServicesPlayAlertSound(soundID);
//5. 如果不需要播放了, 需要释放音效所占用的内存
AudioServicesDisposeSystemSoundID(soundID);
}
2. 简单封装
MTAudioTool.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface MTAudioTools : NSObject
/** 播放系统音效*/
+ (void)playSystemSoundWithURL:(NSURL *)url;
/** 播放震动音效*/
+ (void)playAlertSoundWithURL:(NSURL *)url;
/** 清空音效文件的内存*/
+ (void)clearMemory;
@end
MTAudioTool.m
#import "MTAudioTools.h"
/** 缓存字典*/
static NSMutableDictionary *_soundIDDict;
@implementation MTAudioTools
// 只要头文件参与了编译调用
//+ (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重复创建 --> soundID每次创建, 就会有对应的URL地址产生
// 可以将创建后的soundID 及 对应的URL 进行缓存处理
//1. 获取URL的字符串
NSString *urlStr = url.absoluteString;
//2. 从缓存字典中根据URL来取soundID 系统音效文件
SystemSoundID soundID = [_soundIDDict[urlStr] intValue];
//需要在刚进入的时候, 判断缓存字典是否有url对应的soundID
//3. 判断soundID是否为0, 如果为0, 说明没有找到, 需要创建
if (soundID == 0) {
//3.1 创建音效文件
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
//3.2 缓存字典的添加键值
_soundIDDict[urlStr] = @(soundID);
}
return soundID;
}
/** 清空音效文件的内存*/
+ (void)clearMemory
{
//1. 遍历字典
[_soundIDDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//2. 清空音效文件的内存
SystemSoundID soundID = [obj intValue];
AudioServicesDisposeSystemSoundID(soundID);
}];
}
@end
ViewController.m
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "MTAudioTools.h"
/**
1. 导入AVFoundation框架
2. 创建音效文件
3. 播放音效文件
音效: 非常短的音乐, 一般来说30秒以内的声音, 都算作音效
*/
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark 点击播放音效
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1. 获取URL地址
NSURL *url = [[NSBundle mainBundle] URLForResource:@"buyao.wav" withExtension:nil];
//2. 调用工具类播放音效
//[MTAudioTools playSystemSoundWithURL:url];
[MTAudioTools playAlertSoundWithURL:url];
}
#pragma mark 当前控制器收到内存警告时会调用的方法
- (void)didReceiveMemoryWarning
{
// 局部音效需要在这里进行释放
[MTAudioTools clearMemory];
NSLog(@"%s",__func__);
}
@end
3. 音乐播放
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
/**
1. 需要使用AVFoundatiaon框架
2. 创建音乐播放器
3. 根据需求, 进行播放/暂停/停止
*/
@interface ViewController ()
@property (nonatomic, strong) AVAudioPlayer *player;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建音乐播放器
//1. 获取URL路径
NSURL *url = [[NSBundle mainBundle] URLForResource:@"我爱你你却爱着她.mp3" withExtension:nil];
//2. 创建一个error对象
NSError *error;
//3. 创建音乐播放器
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (error) {
NSLog(@"有错误产生");
}
}
- (IBAction)playClick:(id)sender {
//1. 准备播放 --> 将音频文件加载到内存中 --> 这句话可以不写 --> play会隐式调用prepareToPlay方法. 但是规范来说, 还是会写上
[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. 创建录音对象
3. 根据需求, 进行录音/暂停/停止
*/
@interface ViewController ()
@property (nonatomic, strong) AVAudioRecorder *recorder;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
@implementation ViewController
/**
//录音
// //settings 设置参数 录音相关参数 声道 速率 采样率
// NSMutableDictionary *setting = [NSMutableDictionary dictionary];
// // 音频格式
// setting[AVFormatIDKey] = @(kAudioFormatAppleIMA4);
// // 音频采样率
// setting[AVSampleRateKey] = @(600.0);
// // 音频通道数
// setting[AVNumberOfChannelsKey] = @(1);
// // 线性音频的位深度
// setting[AVLinearPCMBitDepthKey] = @(8);
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 创建录音对象
//1. 获取URL地址 --> 具体的文件名路径
//--> 注意之前是获取资源地址, 这里是指要将录音存放到哪里
NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"recorde.wav"];
//2. 将path字符串转换成NSURL --> file://
NSURL *url = [NSURL fileURLWithPath:path];
//3. 配置设置字典
// 如果传空, 默认就是高质量
// 录音的参数值
NSMutableDictionary *setting = [NSMutableDictionary dictionary];
//4. 创建Error对象 __autoreleasing 可以不加, 加上之后是最标准写法
__autoreleasing NSError *error;
//5. 创建录音对象
self.recorder = [[AVAudioRecorder alloc] initWithURL:url settings:setting error:&error];
// if (error) {
// NSLog(@"error");
// }
//6. 打开分贝的检测
self.recorder.meteringEnabled = YES;
//7. 如果要在真机运行, 还需要一个session类, 并且制定分类为录音
AVAudioSession *session = [AVAudioSession new];
[session setCategory:AVAudioSessionCategoryRecord error:nil];
}
- (IBAction)recordClick:(id)sender {
//1. 准备录音
[self.recorder prepareToRecord];
//2. 开始录音 --> 如果同一路径再次录音, 则会覆盖之前的文件
[self.recorder record];
//3. 进行分贝的循环检测 --> 添加计时器
[self updateMetering];
}
- (IBAction)pauseClick:(id)sender {
// 暂停录音 --> 如果用户只是暂停了, 应该提示用户进行保存操作
[self.recorder pause];
// 暂停循环
self.displayLink.paused = YES;
}
- (IBAction)stopClick:(id)sender {
// 停止录音 --> 之后停止录音时, 最终的录音文件才会生产
[self.recorder stop];
// 暂停循环
self.displayLink.paused = YES;
NSLog(@"停止录音");
}
#pragma mark 添加计时器
- (void)updateMetering
{
// 如果没有displayLink就创建
if (self.displayLink == nil) {
//1. 创建displayLink
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateMeter)];
//2. 添加到运行循环中
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
// 判断如果暂停了循环, 就打开
if (self.displayLink.isPaused) {
self.displayLink.paused = NO;
}
}
#pragma mark 循环调用的方法
- (void)updateMeter
{
//需求: 自动停止录音 --> 根据分贝的大小来判断
//1. 我们需要获取分贝信息
//2. 设置分贝如果小于某个值, 一定时间后, 自动停止
//1. 更新分贝信息
[self.recorder updateMeters];
//2. 获取分贝信息 --> iOS直接传0
// 0 ~ -160 , 值最大是0, 最小是-160. 系统返回的是负值
CGFloat power = [self.recorder averagePowerForChannel:0];
//3. 实现2S自动停止
static NSInteger number;
//displayLink,一秒默认是60次, 如果120此的调用都小于某个分贝值, 我们就可以认为要自动停止
//3.1 先判断用户是否小于某个分贝值 --> 用户是否没说话
if (power < -30) {
//3.2 如果发现很安静, 我们就可以记录一下, number进行叠加
number++;
//3.3 如果发现120次了, 都小于设定的分贝值
if (number / 60 >= 2) {
//3.4 调用停止方法
[self stopClick:nil];
}
} else {
number = 0;
}
NSLog(@"power: %f",power);
}
@end