因项目需要,经常接触蓝牙音频相关的操作,如录音、播放、音频播放控制等,但截止目前,项目的工程都是以 OC 来实现,体验了 Swift 便利,自已用 Swift 写了个实现录音、播放(含蓝牙)的 Demo。
一、前言
1.1 音频主要参数
- 采样率(samplerate): 模拟信号数字化的过程,每秒钟采集的数据量,采样频率越高,数据量越大,音质越好
- 声道数(channels): 即单声道或双声道 (iPhone 无法直接采集双声道,但可以模拟,即复制一份采集到的单声道数据.安卓部分机型可以)
- 位宽: 每个采样点的大小,位数越多,表示越精细,音质越好,一般是 8 bit 或 16 bit
- 数据格式: iOS 端设备采集的原始数据为线性 PCM 类型音频数据
- 其他: 还可以设置采样值的精度,每个数据包有几帧数据,每帧数据占多少字节等等
1.2 Audio Session
APP 调用音频相关,自然会用到 iOS 的硬件功能,iOS 设备中,每一个应用 APP,都有一个音频会话 Audio Session ,就是来管理音频操作的,基于 AVFoundation。
iOS 使用音频,管理粒度很细,Audio Session 处理音频,通过它的分类 Audio Session Category 设置默认的分类,
- 允许播放,不允许录音
- 静音按钮开启后,应用就哑巴了,播放音频没声音
- 锁屏后,应用也哑巴了,播放音频没声音
- 如果后台有别的 APP 播放音频,你 APP 要开始播放音频的时候,别的 APP 就哑巴了
- 设置混播或单播模式
二、手机录音(MIC)及播放
2.1 申请权限
右击 Info.plist , 选择以源码方式打开,如下图
在列表中根据自己需要选择添加对应的 Key-Value,常用的有:
<key>NSCalendarsUsageDescription</key>
<string>为了更好的体验,请允许访问日历</string>
<key>NSCameraUsageDescription</key>
<string>为了更好的体验,请允许访问您的相机</string>
<key>NSContactsUsageDescription</key>
<string>为了更好的体验,请允许访问您的联系人</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>为了更好的体验,请允许app后台获取位置</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>为了更好的体验,请允许使用时获取位置</string>
<key>NSMicrophoneUsageDescription</key>
<string>为了更好的体验,请允许访问您的麦克风</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>为了更好的体验,请允许访问您的相册</string>
<key>NSRemindersUsageDescription</key>
<string>为了更好的体验,请允许访问备忘录</string>
本例中因用到 MIC 权限,所以只需添加 MIC 权限即可以。
2.2 初始化 Audio Session
设置 Audio Session 的分类,AVAudioSession.CategoryOptions.defaultToSpeaker , 允许我们的 APP 调用内置的麦克风来录音,又可以播放音频。
这里要做录音及播放功能,就把分类的选项也改了。
let session = AVAudioSession.sharedInstance()
//设置session类型
do {
try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch let err{
print("设置类型失败:\(err.localizedDescription)")
}
//设置session动作
do {
try session.setActive(true)
} catch let err {
print("初始化动作失败:\(err.localizedDescription)")
}
2.3 录音初始化
录音初始化包括:设置录音采样的描述信息,实例化 audioRecorder
var soundRecorder : AVAudioRecorder!
let file_path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/record.wav")
//录音设置,注意,后面需要转换成NSNumber,如果不转换,你会发现,无法录制音频文件,猜测是因为底层还是用OC写的原因
let recordSetting: [String: Any] = [AVSampleRateKey: NSNumber(value: 16000),//采样率
AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM),//音频格式
AVLinearPCMBitDepthKey: NSNumber(value: 16),//采样位数
AVNumberOfChannelsKey: NSNumber(value: 1),//通道数
AVEncoderAudioQualityKey: NSNumber(value: AVAudioQuality.min.rawValue)//录音质量
];
do {
let audioFilename = URL(fileURLWithPath: file_path!)
soundRecorder = try AVAudioRecorder(url: audioFilename, settings: recordSetting )
soundRecorder.delegate = self
soundRecorder.prepareToRecord()
} catch {
print(error)
}
2.4 开始录音及停止录音
开始录音及停止录音涉及两个接口
//开始录音
soundRecorder.record()
//停止录音
soundRecorder.stop()
绑定按键操作
if recordBTN.titleLabel?.text == "Record" {
soundRecorder.record()
recordBTN.setTitle("Stop", for: .normal)
playBTN.isEnabled = false
} else {
soundRecorder.stop()
recordBTN.setTitle("Record", for: .normal)
playBTN.isEnabled = false
}
2.5 播放
录音好了,开始播放,首先进行初始化
var soundPlayer : AVAudioPlayer!
func setupPlayer() {
let audioFilename = URL(fileURLWithPath: file_path!)
do {
soundPlayer = try AVAudioPlayer(contentsOf: audioFilename)
soundPlayer.delegate = self
soundPlayer.prepareToPlay()
soundPlayer.volume = 1.0
} catch {
print(error)
}
}
开始播放
if playBTN.titleLabel?.text == "Play" {
playBTN.setTitle("Stop", for: .normal)
recordBTN.isEnabled = false
setupPlayer()
soundPlayer.play()
} else {
soundPlayer.stop()
playBTN.setTitle("Play", for: .normal)
recordBTN.isEnabled = false
}
三、蓝牙录音及播放
蓝牙录音及播放实际上与手机本机操作差不多,只需要设置下 Audio Session。
手机本机设置下 Audio Session :
do {
try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch let err{
print("设置类型失败:\(err.localizedDescription)")
}
蓝牙录音的 Audio Session 设置:
do {
try session.setCategory(AVAudioSessionCategoryPlayAndRecord,with: [.defaultToSpeaker, .allowBluetooth, .allowBluetoothA2DP])
} catch let err{
print("设置类型失败:\(err.localizedDescription)")
}
其他的不需要特殊处理。
附加操作
采样音量大小计量
AVAudioPlayer 有音频的计量功能,播放音频的时候,音频计量可以检测到,波形的平均能级等信息
AVAudioPlayer 的方法 averagePower(forChannel:),会返回当前的分贝值,取值范围是 -160 ~ 0 db, 0 是很吵, -160 是很安静
波形,长这样
控制播放音量大小
音量的取值范围是 0 ~ 1, 0 是静音,1 是最大
func toSetVolumn(value: Float){
guard let player = audioPlayer else {
return
}
// 苹果都封装好了,设置 audioPlayer 的 volume
player.volume = value
}
设置左右声道
取值范围是 -1 到 1,-1 是全左,1 是全右,0 是均衡声道
func toSetPan(value: Float) {
guard let player = audioPlayer else {
return
}
// 苹果都封装好了,设置 audioPlayer 的 pan
player.pan = value
}
设置播放循环
循环的取值范围是 -1 到 Int.max, numberOfLoops 取值 0 到 Int.max,则会多播放那个取值的次数
func toSetLoopPlayback(loop: Bool) {
guard let player = audioPlayer else {
return
}
// 苹果都封装好了,设置 audioPlayer 的 numberOfLoops
if loop == true{
// numberOfLoops 为 -1,无限循环,直到 audioPlayer 停止
player.numberOfLoops = -1
}
else{
// numberOfLoops 为 0,仅播放一次,不循环
player.numberOfLoops = 0
}
}
设置播放速率
audioPlayer 的播放速率范围是,0.5 ~ 2.0,0.5 是半速播放,1.0 是正常播放,2.0 是倍速播放
// 播放前,要点亮 audioPlayer 的播放速率控制,为可用
audioPlayer.enableRate = true
func toSetRate(value: Float) {
guard let player = audioPlayer else {
return
}
// 苹果都封装好了,设置 audioPlayer 的 rate
player.rate = value
}