iOS Audio(音频)

  1. 云栖社区>
  2. 博客>
  3. 正文

iOS Audio(音频)

凌浩雨 2018-01-25 16:21:00 浏览369
展开阅读全文

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

网友评论

登录后评论
0/500
评论
凌浩雨
+ 关注