一般的播放套路,3 步走
先读数据,文件还原采样数据
对于音频资源文件,使用 Audio File Services, 和 Audio File Stream Services
采样数据,集中为音频缓冲
通过 Audio Converter Services,
AudioConverterFillComplexBuffer, 这个方法比较全面,
非压缩数据可以转 pcm buffer,
压缩数据也可以转 pcm buffer,
把 pcm buffer 交给 AVAudioPlayerNode ,就可以播放了
把 AVAudioEngine 的节点关联下,发动下 AVAudioEngine, 让 AVAudioPlayerNode play 就好了
如果陌生,可以参考系列博客:
从 wav 播放器,学习 AudioToolBox 的 services
Swift 音频 DIY ,Audio Queue Services 搞缓冲,AVAudioEngine 加声效
该目录中,还包括入门博客 ...
从 wav 播放器,学习 AudioToolBox 的 services
这一篇,主要介绍 Audio File Services 和 Audio File Stream Services 读取音频文件播放,
本篇主要介绍直接播放 pcm 采样数据
本篇播放套路,3 步走
先读数据,文件还原采样数据
本篇例子是 pcm 数据文件,
wav, 非压缩格式音频文件
wav 文件 = pcm 数据文件 + asbd
使用 ffmpeg 方便把 in.pcm, 转换为 file.wav
ffmpeg -f s16le -ar 44.1k -ac 2 -i /Users/Music/_wav/X/src/in.pcm file.wav
可以直观看出,两文件占用的硬盘空间大小,都是 5.3 M
直观地了解到,
肯定没有做解压缩等事情,
基本没有音频数据编解码操作
可以理解为, wav 文件 = pcm 数据文件 + asbd
那么播放 pcm 文件,就简单了
pcm 与 wav 类似,wav 自动配置 asbd, pcm 手动下就好
Audio File Services, 和 Audio File Stream Services ,可以读取 wav 非压缩格式音频文件,不能直接读音频数据 pcm
自己读 pcm 音频数据,自己配置 asbd, 完结
音频数据,还原采样
例子中的音频数据是,
bit depth 为 .pcmFormatInt16, 采样率 44100,双声道,音频数据交错
物理世界的音频信息,是模拟信号,
计算机能够处理的,是数字信号
bit depth 位深越大,表示采集的信息越精准。位深越小,采集的信号越失真
音频采样的准确度,通过位深和采样率保证。采样率越高,说明单位时间内,采集的越频繁
-
interleaved: true, 音频数据交错, 多声道音频数据,用于播放
-
interleaved: false, 音频数据非交错, 多声道音频数据,用于数据分析。
音频数据分析的时候,一般希望各 channel 的音频数据,相互独立
下面代码可看出,一个音频采样帧 frame 里面放 4 个 UInt8
位深为 16 位,无符号整型。需要 2 个 UInt8 来表达
音频数据为立体声,有两个 channel, 一个音频采样帧 frame 就需要位深( 2 个 UInt8 ) * 2
后续 github repo 可看出,对于非压缩音频数据,其 asbd 中一个 packet 只有一个数据帧 Frame
public internal(set) var dataFormatD = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 44100, channels: 2, interleaved: true)!
do {
let data = try Data(contentsOf: src)
let array = data.withUnsafeBytes { (pt: UnsafeRawBufferPointer) -> [UInt8] in
let head = pt.bindMemory(to: UInt8.self)
if let addr = head.baseAddress{
let buffer = UnsafeBufferPointer(start: addr, count: data.count)
return Array(buffer)
}
else{
return []
}
}
let count = array.count
guard count > 0 else {
return
}
for i in stride(from: 0, to: count, by: 4){
let arr: [UInt8] = [array[i], array[i + 1], array[i + 2], array[i + 3]]
packetsX.append(Data(arr))
}
} catch {
print(error)
}
本文例子中的 pcm 音频数据,时长 30 s
对于单声道下,其他配置一样,
public internal(set) var dataFormatD = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 44100, channels: 1, interleaved: true)!
do {
// 与上面一样
// ....
for i in stride(from: 0, to: count, by: 2){
let arr: [UInt8] = [array[i], array[i + 1]]
packetsX.append(Data(arr))
}
} catch {
print(error)
}
单声道数据,一帧 Frame 一个采样数据, 位深 bit depth 16 位,两个 UInt 8 来表达
把上例中的 pcm 当作单声道处理,时长就成了 60 s