从 pcm 播放器,继续学习 AudioToolBox 的 services 与非压缩格式

一般的播放套路,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

从 pcm 播放器,继续学习 AudioToolBox 的 services 与非压缩格式

直观地了解到,

我确定我没有做过任何减压之类的事。

[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/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球