一般的播放套路,3 步走
先读数据,文件还原采样数据
对于音频资源文件,使用 Audio File Services, 和 Audio File Stream Services
采样数据,集中为音频缓冲
通过 Audio Converter Services,
AudioConverterFillComplexBuffer
, 这个方法比较全面,
非压缩数据可以转 pcm buffer,
压缩数据也可以转 pcm buffer,
把 pcm buffer 交给 AVAudioPlayerNode ,就可以播放了
把 AVAudioEngine 的节点关联下,发动下 AVAudioEngine, 让 AVAudioPlayerNode play 就好了
如果你不熟悉,你可以参考系列博客:
[En]
If you are unfamiliar, you can refer to the series of blogs:
该目录还包括入门级博客。
[En]
The directory also includes entry-level blogs.
这一篇,主要介绍 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
直观地了解到,
我确定我没有做过任何减压之类的事。
[En]
I’m sure I didn’t do anything like decompression.
基本上没有音频数据编解码器操作。
[En]
There is basically no audio data codec operation.
可以理解为, 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
对于单声道,其他配置相同
[En]
For mono, other configurations are the same
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
采样数据,集中为音频缓冲 ( 略 ,见系列博客 )
把 pcm buffer 交给 AVAudioPlayerNode ,就可以播放了 ( 略 ,见系列博客 )
Original: https://blog.csdn.net/dengjiangszhan/article/details/110392650
Author: dengjiangszhan
Title: 从 pcm 播放器,继续学习 AudioToolBox 的 services 与非压缩格式
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/526070/
转载文章受原作者版权保护。转载请注明原作者出处!