iOS 音频格式转换怎么实现?从 AVAudioFile、AAC、MP3 到 FLAC 与批量转码架构
在前面的文章中,我们已经逐渐把文件浏览器扩展成一个完整的文件处理平台:
01 iOS 文件浏览器架构
02 GB 级大文件处理
03 Wi-Fi 文件传输
04 SMB / NAS
05 WebDAV
06 FTP
07 ZIP / RAR / 7z
08 图片转换与压缩
09 视频转换与压缩
这一篇继续补齐媒体处理的另一块核心能力:
音频格式转换
用户真正遇到的问题往往非常直接:
FLAC 太大,怎么转 MP3?
或者:
WAV 文件几百 MB,能不能压小一点?
又或者:
这个音频格式我的播放器打不开,怎么转换?
从产品角度看,就是:
Input Audio
↓
Convert
↓
Output Audio
但从技术角度看,音频转换背后至少涉及:
- Container / File Format
- Codec
- Sample Rate
- Bit Depth
- Channel Count
- Bitrate
- PCM
- Lossy / Lossless
- Metadata
- Duration
- Encoder / Decoder
- Resampling
- Channel Mixing
所以:
音频格式转换并不等于修改扩展名。
真正的音频转换通常是:
Decode
↓
PCM
↓
Optional Processing
↓
Encode
这一篇就从文件管理 App 的角度,系统拆解一套可扩展的 AudioService 架构。
1. MP3、AAC、WAV、FLAC 到底有什么区别?
普通用户通常会把:
MP3
AAC
WAV
FLAC
都叫:
音频格式。
但技术上它们并不是完全同一个层级的概念。
最简单可以先分成:
Compressed Audio
+
Uncompressed Audio
以及:
Lossy
+
Lossless
例如:
MP3
AAC
通常属于:
有损压缩
而:
FLAC
通常属于:
无损压缩
WAV 则经常作为:
PCM Audio Container
存在。
所以:
WAV
本身并不等于:
一定完全无压缩的某一种固定编码。
2. 最重要的概念:PCM
理解音频转换之前,先理解:
PCM
可以简单认为,PCM 是比较接近“解码后的数字音频采样数据”的形式。
例如:
MP3 File
↓
MP3 Decoder
↓
PCM Samples
然后:
PCM Samples
↓
AAC Encoder
↓
AAC File
所以:
MP3 → AAC
本质上更像:
MP3
↓ Decode
PCM
↓ Encode
AAC
这和上一篇视频里的:
Decode → Raw Frames → Encode
非常类似。
3. 音频格式转换为什么不能直接改扩展名?
例如:
song.flac
直接改成:
song.mp3
内部数据还是:
FLAC
所以播放器真正读取时仍然可能失败。
正确流程:
song.flac
↓
FLAC Decoder
↓
PCM
↓
MP3 Encoder
↓
song.mp3
所以:
Extension 只描述文件名,Codec 才决定真正的数据结构。
4. 什么是 Lossy?
例如:
MP3
AAC
通常属于:
Lossy Compression
也就是:
为了降低文件大小,会丢弃一部分音频信息。
例如:
WAV
50 MB
转换为:
MP3
5 MB
体积可能大幅下降。
代价就是:
Audio Information Loss
所以:
MP3 → WAV
并不能把之前损失的信息恢复回来。
5. MP3 转 WAV 不等于音质变好
这是非常典型的误区。
例如:
MP3
128 kbps
转成:
WAV
最终文件可能从:
4 MB
变成:
40 MB
但音质不会自动变成:
原始无损音质。
因为:
MP3 Encoding
阶段已经丢失的信息无法凭空恢复。
所以:
MP3 → WAV
只是:
将已经解码出来的 PCM 数据保存成另一种文件形式。
文件更大,不代表质量更高。
6. FLAC 为什么叫无损压缩?
FLAC 的特点是:
PCM
↓
Lossless Compression
↓
FLAC
然后:
FLAC
↓
Decode
↓
Original PCM
理论目标是可以恢复原始音频采样数据。
所以:
FLAC → WAV
通常属于:
Lossless Decode
只要整个转换链正确,
不会因为 FLAC 解码本身产生 MP3 那种有损压缩损失。
7. FLAC 转 MP3 会发生什么?
流程:
FLAC
↓
Decode
↓
PCM
↓
MP3 Encode
↓
MP3
这里损失发生在:
MP3 Encode
阶段。
所以:
FLAC → MP3
是:
Lossless Source → Lossy Destination
适合:
Reduce File Size
Improve Compatibility
但不适合:
追求保留完全原始音频数据。
8. AAC 和 MP3 应该怎么理解?
两者都是非常常见的有损音频编码方案。
从用户视角可以简单理解:
MP3
→ 兼容性非常广
AAC
→ Apple / MP4 / Streaming 场景非常常见
在某些相近码率场景下,AAC 可以有很不错的压缩效率。
但产品设计不能只看:
谁更先进
还需要看:
Compatibility
因为用户可能就是需要:
MP3
上传到某个老系统。
9. 一个音频文件到底由哪些参数决定?
音频文件大小和质量通常受到:
Codec
+
Sample Rate
+
Bit Depth
+
Channels
+
Bitrate
+
Duration
影响。
例如:
44.1 kHz
16-bit
Stereo
和:
96 kHz
24-bit
Stereo
数据规模明显不同。
所以一个 AudioService 不能只知道:
文件后缀
而应该先:
Inspect Audio
10. AudioInfo 应该包含什么?
例如:
struct AudioInfo {
let duration: TimeInterval
let sampleRate: Double
let channelCount: Int
let bitRate: Int64?
let bitDepth: Int?
let codec: AudioCodec?
let format: AudioFileFormat
let fileSize: Int64
}
UI 可以展示:
FLAC
44.1 kHz
16-bit
Stereo
3:42
28.4 MB
这样用户才知道:
自己到底在处理什么。
11. Sample Rate 是什么?
例如:
44.1 kHz
大致意味着:
每秒对音频波形进行 44,100 次采样。
常见值:
44.1 kHz
48 kHz
96 kHz
并不是:
Sample Rate 越高,任何音频都会自动更好。
例如源文件本来:
44.1 kHz
强行转:
96 kHz
并不会凭空产生新的真实高频信息。
这和:
720p 图片放大成 4K
类似。
12. Resampling 是什么?
如果:
Source:
96 kHz
目标:
48 kHz
就需要:
Resampling
也就是:
PCM 96k
↓
Sample Rate Converter
↓
PCM 48k
所以 AudioService 内部可能不仅是:
Decode
+
Encode
还可能是:
Decode
↓
Resample
↓
Encode
13. Channel Count 也可能发生转换
例如源文件:
5.1 Channels
目标格式或用户需求:
Stereo
则需要:
Downmix
流程:
5.1 PCM
↓
Channel Mixer
↓
Stereo PCM
同样:
Stereo
↓
Mono
也是一种 Channel Conversion。
所以:
Audio Convert
实际上可能包括:
Codec Conversion
Sample Rate Conversion
Channel Conversion
14. Bit Depth 是什么?
PCM 音频里常见:
16-bit
24-bit
32-bit Float
Bit Depth 会影响:
Dynamic Range
+
PCM Data Size
例如:
24-bit PCM
通常比:
16-bit PCM
占用更多数据。
但是:
MP3
AAC
这类有损 Codec 更常用:
Bitrate
描述压缩后的目标数据量。
所以:
Bit Depth 和 Bitrate 不是一回事。
15. Bitrate 对 MP3 / AAC 文件大小非常重要
例如:
128 kbps
192 kbps
256 kbps
320 kbps
粗略来说:
File Size
≈
Bitrate × Duration
例如:
128 kbps
×
240 sec
大约:
30,720 kb
再除以:
8
大约:
3.84 MB
实际还会有:
Container
Metadata
Encoder
带来的差异。
但用于估算已经非常有价值。
16. 为什么 WAV 文件特别大?
假设:
44.1kHz
16-bit
Stereo
每秒原始 PCM 数据大约:
44,100
×
16
×
2
bits。
约:
1,411,200 bits/sec
也就是:
≈ 1,411 kbps
而 MP3 可能:
128 kbps
所以两者体积自然差异巨大。
这就是为什么:
10MB MP3
转 WAV 以后可能变成:
100MB+
17. AudioService 应该和 FileProvider 分开
继续沿用整个系列的架构:
FileProvider
负责:
文件来自哪里。
例如:
Local
SMB
WebDAV
FTP
AudioService 负责:
这个音频怎么处理。
所以:
FileProvider
↓
FileItem
↓
AudioService
而不是:
SMBAudioConverter
FTPAudioConverter
LocalAudioConverter
18. 可以定义统一 AudioService
例如:
protocol AudioService {
func inspect(
_ item: FileItem
) async throws -> AudioInfo
func convert(
_ item: FileItem,
options: AudioConvertOptions
) async throws -> FileItem
func compress(
_ item: FileItem,
options: AudioCompressOptions
) async throws -> FileItem
}
其中:
AudioConvertOptions
可以包含:
Output Format
Codec
Sample Rate
Channels
Bitrate
Metadata Policy
19. AVAudioFile 可以作为重要的音频文件抽象
在 Apple 的音频体系中:
AVAudioFile
可以作为很多音频文件操作的入口。
整体思路:
File URL
↓
AVAudioFile
↓
Audio Format
↓
PCM Buffer
然后:
PCM Buffer
↓
Output Audio File
对于:
- PCM
- 格式转换
- Audio Engine 流程
都很有价值。
但是:
不应该假设所有你想支持的 Codec 和文件封装都能靠单一 Apple API 自动完整覆盖。
如果产品需要:
MP3
FLAC
OGG
Opus
等广泛格式,
通常仍需要根据目标格式、系统能力以及所选底层库设计不同实现策略。
20. 一个典型音频转换 Pipeline
例如:
FLAC → AAC
可以抽象成:
FLAC File
↓
Decoder
↓
PCM Buffer
↓
Audio Converter
↓
AAC Encoder
↓
Output File
如果需要修改采样率:
FLAC
↓
PCM 96kHz
↓
Resampler
↓
PCM 48kHz
↓
AAC Encoder
如果还需要转换声道:
5.1
↓
Mixer
↓
Stereo
所以完整 Pipeline:
Decoder
↓
Sample Rate Converter
↓
Channel Mixer
↓
Encoder
21. 不要把整个音频一次读进内存
比如:
8 hour WAV
可能非常大。
错误方向:
Audio File
↓
Data
↓
Memory
或者:
Entire PCM
↓
RAM
更合理的是:
Input File
↓
PCM Buffer
↓
Convert
↓
Output
↓
Next Buffer
也就是:
Chunk / Buffer Based Processing
这样:
Audio Duration = 8 hours
并不意味着:
Memory Usage = 整个 8 小时 PCM
22. 为什么音频处理中 PCM 特别容易占内存?
例如:
96 kHz
24-bit
Stereo
虽然源 FLAC 文件可能只有几百 MB,
解码后的 PCM 数据量会明显增加。
所以:
Compressed Audio Size
≠
Decoded PCM Memory
这和前面:
HEIC → Raw Pixels
以及:
ZIP → Expanded Data
是一样的资源管理问题。
23. Audio Buffer 应该循环复用
例如:
Read Buffer
↓
Convert
↓
Write
↓
Reuse Buffer
而不是:
Buffer 1
Buffer 2
Buffer 3
...
全部留在内存
长音频转换尤其需要:
Bounded Memory
也就是:
文件再长,内存峰值依然应该受到控制。
24. 音频进度怎么计算?
相比视频,
音频也很适合根据:
Processed Frames
计算。
例如:
Processed Frames
÷
Total Frames
或者:
Processed Time
÷
Duration
最终转换成:
0.0 ... 1.0
UI:
Converting...
02:10 / 05:00
43%
这样上层根本不需要理解:
Sample Buffer
细节。
25. 音频格式转换和音频压缩要分开
比如:
Convert
FLAC → WAV
重点:
Compatibility / Format
Compress
WAV 500MB
↓
AAC 60MB
重点:
File Size
因此产品上最好提供:
Convert Audio
和:
Compress Audio
两个用户意图明确的入口。
26. “压缩音频”最直接的方法是降低 Bitrate
例如:
AAC
256 kbps
降低到:
128 kbps
文件大小粗略可以下降很多。
如果再:
Stereo → Mono
在语音场景下还可能进一步降低。
但是音乐:
Stereo
通常有明显价值。
因此不能所有音频都默认:
Mono
27. 语音和音乐应该采用不同策略
这是产品设计很重要的一点。
Voice
例如:
Meeting
Lecture
Voice Memo
Podcast Speech
可以更倾向:
Lower Bitrate
Mono
Reduced Sample Rate
Music
则可能更适合:
Stereo
Higher Bitrate
44.1 / 48 kHz
所以可以提供:
Voice
Music
High Quality
这样的预设。
而不是让用户自己理解:
48kHz / 128kbps / Stereo
是什么意思。
28. 可以提供三种简单压缩模式
例如:
Small
优先文件大小
Balanced
大小与音质平衡
High Quality
优先保留音质
高级设置再提供:
Codec
Bitrate
Sample Rate
Channel
这和前面的:
Image
Video
产品设计保持一致。
29. “压到 10MB 以下”也可以做
和图片、视频一样,
用户可能遇到:
Upload Limit:
10 MB
这时可以根据:
Duration
估算目标 Bitrate。
例如:
Duration:
600 sec
Target:
10 MB
粗略总 Bitrate:
10 × 8 × 1024
÷
600
约:
136 kbps
再根据:
Container Overhead
留出余量。
然后选择例如:
128 kbps
输出。
30. 音频目标大小比视频更容易估算
因为对于固定 Bitrate 编码:
File Size
和:
Bitrate × Duration
关系相对直接。
例如:
128 kbps
10 分钟大约就是一个比较容易估算的范围。
所以:
Target File Size
在音频工具里非常适合产品化。
31. VBR 和 CBR 又有什么区别?
CBR
Constant Bitrate
码率相对固定。
优点:
大小容易估计
VBR
Variable Bitrate
复杂片段可以使用更多数据,
简单片段使用更少数据。
通常更有利于:
Quality / Size Balance
但最终文件大小预测不会像固定码率一样简单。
所以:
如果用户强制要求目标大小,CBR / ABR 思路通常更容易预测;如果用户更看重质量效率,则可考虑 VBR。
32. 批量音频转换是高频需求
例如用户有:
80 FLAC Files
需要:
FLAC → MP3
这类场景非常适合:
Batch Convert
流程:
Select Folder
↓
Choose MP3
↓
Choose Quality
↓
Start
↓
80 Tasks
这比:
一首一首点
有价值得多。
33. 但批量音频也要控制并发
假设:
100 Files
全部同时:
Decode
+
Encode
会导致:
CPU Pressure
Memory Pressure
Disk IO Pressure
所以仍然需要:
Operation Queue
限制:
Max Concurrent Audio Tasks
具体数量应该通过:
Device Benchmark
决定。
而不是:
CPU 8 核,所以同时跑 8 个。
34. 音频转换比视频更适合有限并发
和视频相比,
音频编码资源压力通常小很多。
因此:
2–4 audio tasks
在某些设备和场景下可能比较合理。
但这不是固定数字。
如果源文件是:
192kHz / 32-bit / Multichannel
处理成本也会明显增加。
核心还是:
并发度应该由实际资源占用决定。
35. 单首失败不应该让整个批次失败
例如:
100 FLAC
其中:
99 Success
1 Corrupted
应该:
Completed with 1 error
而不是:
Batch Failed
用户可以查看:
Failed Items
然后:
Retry
所以继续复用:
BatchOperation
的:
Partial Success
机制。
36. Metadata 是否保留?
音频文件可能包含:
Title
Artist
Album
Genre
Year
Track Number
Artwork
例如:
FLAC
↓
MP3
用户通常希望:
Artist
Album
Cover
继续存在。
所以 AudioService 不能只处理:
PCM
还要考虑:
Metadata Mapping
37. 不同格式 Metadata 并不完全一样
例如:
MP3
常见:
ID3
FLAC 有自己的 Metadata Block 体系。
MP4 / M4A 也有自己的 Metadata 结构。
所以:
Source Metadata
↓
Unified AudioMetadata
↓
Destination Metadata Writer
会比:
原始 Tag 直接复制
更加可扩展。
38. 可以定义统一 AudioMetadata
例如:
struct AudioMetadata {
let title: String?
let artist: String?
let album: String?
let genre: String?
let year: Int?
let trackNumber: Int?
let artwork: Data?
}
然后:
FLAC Metadata
↓
AudioMetadata
↓
MP3 Metadata Writer
这样格式转换的时候:
Audio Content
和:
Metadata
都可以迁移。
39. Album Artwork 可能很大
有些歌曲:
Audio = 4MB
但封面可能:
5MB PNG
最后文件:
9MB
所以做:
Audio Compression
时,如果用户目标是极小文件,
还应该考虑:
Artwork Resize / Compression
例如:
3000 × 3000 PNG
↓
1000 × 1000 JPEG
否则音频码率已经很低,
文件还是不小。
40. 但不要偷偷删除用户封面
和图片 Metadata 一样。
产品可以提供:
Keep Artwork
默认开启。
高级压缩时提供:
Compress Artwork
Remove Artwork
让用户自己决定。
否则用户可能发现:
转换以后专辑封面没了。
41. 音频转换任务仍然应该使用临时文件
例如:
song.mp3
转换过程中:
.song.mp3.tmp
流程:
Create Temp
↓
Encode
↓
Finish
↓
Validate
↓
Rename
失败:
Delete Temp
这样可以避免:
半个 MP3
出现在正常文件列表里。
42. 如果覆盖原文件怎么办?
例如用户希望:
Replace Original
仍然应该:
Source
↓
Create Temp Output
↓
Complete
↓
Validate
↓
Replace
而不是:
Delete Source
↓
Convert
否则:
Encoder Failed
时用户原文件也可能没了。
43. 同名文件继续使用 ConflictResolver
例如:
song.flac
转:
song.mp3
目录已经存在:
song.mp3
继续统一处理:
Replace
Keep Both
Skip
Cancel
Keep Both:
song (1).mp3
这样:
Copy
Extract
Image Convert
Video Convert
Audio Convert
全部共享:
ConflictResolver
44. 远程音频怎么办?
例如:
NAS
↓
song.flac
用户:
Convert to MP3
最简单方案:
SMB
↓
Download Temp
↓
AudioService
↓
MP3
然后:
Save Local
如果用户需要保存回 NAS:
MP3
↓
Upload
↓
SMB
这和视频处理逻辑一样。
45. 音频比视频更适合流式远程转换吗?
理论上,某些顺序音频处理很适合:
Remote Read Stream
↓
Decoder
↓
PCM
↓
Encoder
↓
Destination Stream
也就是:
NAS FLAC
↓
iPhone Transcode
↓
WebDAV MP3
无需完整落地源文件。
但这需要底层:
Decoder
Provider
Encoder
都能够很好地支持流式数据源和流式输出。
所以它属于更高级的架构。
第一版完全可以:
Remote → Local Temp → Process
先保证可靠性。
46. AudioDataSource 也可以抽象
和:
ArchiveDataSource
ImageDataSource
VideoDataSource
一样。
可以进一步:
AudioDataSource
例如:
protocol AudioDataSource {
func read(
offset: Int64,
length: Int
) async throws -> Data
}
上层:
AudioService
不一定知道数据来自:
Local
SMB
WebDAV
但同样要尊重底层音频框架的真实能力。
不要为了统一 API 而强行做不合适的抽象。
47. 音频预览和音频转换应该分开
用户只是点击:
song.mp3
播放。
应该:
AudioPlayerService
处理。
而:
FLAC → MP3
是:
AudioService
处理。
所以:
Playback
≠
Conversion
不要把所有音频功能塞进一个巨大:
AudioManager
48. Waveform 又应该属于哪里?
如果产品显示:
Audio Waveform
通常可以单独设计:
WaveformService
流程:
Audio
↓
Downsample Samples
↓
Amplitude Data
↓
Waveform Cache
而不是每次打开页面都:
Decode Entire Audio
重新生成。
和:
Image Thumbnail
Video Thumbnail
思路类似。
49. Waveform 也应该缓存
例如缓存 Key:
File ID
+
Modified Date
+
Waveform Resolution
文件没变:
Return Cache
文件更新:
Regenerate
对于 WebDAV:
ETag
也可以帮助判断缓存是否过期。
50. Audio Operation 进入 FileOperationManager
目前任务系统已经拥有:
Copy
Upload
Download
Extract
Compress
Image Convert
Video Convert
继续增加:
Audio Convert
Audio Compress
例如:
Converting Audio
23 / 80
29%
任务状态:
Waiting
Preparing
Running
Cancelled
Completed
Failed
这样所有长任务体验保持一致。
51. 音频错误也要业务化
不要只显示:
Conversion Failed
可以定义:
enum AudioProcessingError: Error {
case unsupportedFormat
case unsupportedCodec
case invalidAudio
case encoderUnavailable
case insufficientStorage
case destinationConflict
case cancelled
case unknown(Error)
}
例如:
Unsupported Codec
UI 可以进一步提示:
当前版本暂不支持将该音频编码格式转换为 MP3。
比:
OSStatus -xxxx
有意义很多。
52. OGG 和 Opus 需要特别看底层能力
如果产品计划支持:
OGG
Opus
不能只在:
enum AudioFormat
里增加两个 case 就结束。
需要确认:
Decoder
Encoder
Container
Metadata
Seeking
Duration
实际是否由:
系统框架
或者:
第三方库
支持。
尤其商业 App 还需要检查:
License
这和上一篇 RAR / 7z 的原则一样:
支持一种格式,是一个完整能力,不是增加一个扩展名。
53. 第三方音频库最好封装在 Adapter 后面
如果:
MP3
AAC
用方案 A。
FLAC
用方案 B。
Opus
用方案 C。
不要让业务层知道。
可以:
AudioCodecAdapter
│
├── SystemAudioAdapter
├── FLACAdapter
└── OpusAdapter
上层:
AudioService
统一调用:
Decode
Encode
这样以后更换底层实现,
不会影响 UI 和 FileOperation。
54. 可以设计 AudioStrategyResolver
类似上一篇视频。
例如:
Audio Task
↓
Inspect
↓
Strategy Resolver
决定:
Direct Copy?
Remux?
Decode + Encode?
Resample?
Downmix?
例如:
AAC in M4A
↓
AAC in MP4-compatible output
某些场景可能不用完整重新编码。
而:
FLAC → MP3
显然需要:
Decode + Encode
所以:
能避免重新编码时,也应优先避免无意义的二次有损转换。
55. 有损音频不要反复转换
例如:
MP3
↓
AAC
↓
MP3
↓
AAC
每次有损重新编码都可能进一步降低质量。
所以如果源已经是:
MP3
用户只是想:
减小文件
应优先直接重新编码一次到目标码率。
不要在内部经过多个有损中间文件。
最合理 Pipeline:
Source
↓
PCM
↓
Final Destination Codec
而不是:
MP3
↓
AAC Temp
↓
MP3 Final
56. PCM 中间状态最好只存在于内存 Buffer
如果只是为了:
FLAC → MP3
通常没有必要:
FLAC
↓
50GB WAV Temp
↓
MP3
更好的方式:
FLAC Decoder
↓
PCM Buffer
↓
MP3 Encoder
也就是:
Decode and Encode in Pipeline
这样:
- 节省磁盘
- 减少 IO
- 更快
- 更容易流式处理
57. 音频文件处理仍然要考虑磁盘空间
虽然 PCM 不一定完整落地,
目标文件还是需要空间。
例如批量:
200 FLAC
总计:
30GB
转换 MP3 输出预计:
8GB
设备只剩:
3GB
最好提前提示。
所以继续复用:
StorageChecker
。
58. 一个完整 AudioService 架构
最终可以整理:
┌─────────────────────────────┐
│ UI │
│ │
│ Convert Audio │
│ Compress Audio │
│ Batch Convert │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ FileOperationManager │
│ │
│ Queue │
│ Progress │
│ Cancel │
│ Retry │
└──────────────┬──────────────┘
│
▼
┌─────────────────────────────┐
│ AudioService │
│ │
│ Inspect │
│ Strategy Resolver │
│ Decode │
│ Resample │
│ Mix │
│ Encode │
└──────────────┬──────────────┘
│
┌───────┼────────┐
▼ ▼ ▼
System FLAC Other Codec
Audio Adapter Adapter
│
▼
PCM Buffer
旁边继续存在:
MetadataService
WaveformService
StorageChecker
TemporaryFileManager
ConflictResolver
Credential / Provider Layer
59. 整个媒体架构现在已经完整很多
现在:
Services
│
├── ArchiveService
├── ImageService
├── VideoService
├── AudioService
├── ThumbnailService
└── WaveformService
下面:
FileProvider
│
├── Local
├── SMB
├── WebDAV
└── FTP
中间统一:
FileOperationManager
于是一个文件可以:
找到
↓
打开
↓
复制
↓
转换
↓
压缩
↓
分享
整个体验不用跳出文件管理器。
60. TS File Explorer 的音频功能真正解决什么?
从开发者视角:
PCM
Sample Rate
Bitrate
Encoder
AVAudioFile
是技术。
但用户真正的问题是:
FLAC 太大,手机里占空间。
或者:
WAV 发不出去。
或者:
我的播放器只支持 MP3。
所以真正产品流程可能是:
FLAC
↓
TS File Explorer
↓
Convert to MP3
↓
Share
或者:
500MB WAV
↓
Compress
↓
70MB AAC
↓
Upload
用户真正需要的不是:
一个 Codec 面板。
而是:
这个音频文件现在能不能帮我处理成我要的样子。
61. 开发音频转换模块前建议先回答这 20 个问题
1. 支持哪些输入格式?
2. 支持哪些输出格式?
3. 哪些 Codec 使用系统能力?
4. 哪些需要第三方实现?
5. PCM Pipeline 如何设计?
6. 是否支持 Sample Rate Conversion?
7. 是否支持 Channel Conversion?
8. Bit Depth 怎么处理?
9. MP3 / AAC Bitrate 如何选择?
10. 是否支持目标文件大小?
11. 是否支持 CBR / VBR?
12. Metadata 是否保留?
13. Artwork 是否保留?
14. 大文件是否 Buffer Streaming?
15. 批量任务并发度多少?
16. 单文件失败是否影响批次?
17. 临时文件如何处理?
18. Remote Audio 如何处理?
19. 第三方 Codec License 是否可商用?
20. Audio Operation 如何接入 FileOperationManager?
提前把这些问题想清楚以后,
音频模块才不会从:
一个 MP3 转换按钮
最后演变成:
几十个格式判断
+
不同 Encoder 特判
+
Metadata Bug
+
批量任务 Bug
写在最后
音频转换表面看起来只是:
FLAC → MP3
但真正的架构实际上是:
Decode
+
PCM
+
Sample Rate
+
Channels
+
Bitrate
+
Metadata
+
Encode
+
File Operation
和图片、视频一样:
不要把音频模块设计成一堆“格式 A 转格式 B”的函数。
否则最后很容易出现:
flacToMp3()
wavToMp3()
aacToMp3()
mp3ToWav()
flacToWav()
组合越来越多。
更合理的设计应该是:
Source Audio
↓
Inspect
↓
Decode
↓
PCM Pipeline
↓
Encode
↓
Destination Format
这样未来增加新格式,
只需要扩展:
Decoder / Encoder Adapter
而不需要重新组合所有转换路径。
到这里,整个系列已经完成:
01 File Browser
↓
02 Large File IO
↓
03 Wi-Fi Transfer
↓
04 SMB
↓
05 WebDAV
↓
06 FTP
↓
07 Archive
↓
08 Image
↓
09 Video
↓
10 Audio
下一篇我建议进入 TS File Explorer 另一个非常实用、同时搜索需求也很明确的模块:
《iOS PDF 工具怎么实现?从 PDFKit、标注、签名到扫描生成 PDF》
可以继续拆解:
PDFKit
PDFView
PDFDocument
Annotation
Highlight
Underline
Pen
Signature
Page Rendering
Camera Scan
Image → PDF
Large PDF
Thumbnail Cache
这样就从媒体文件处理继续扩展到:
Document Processing。