iOS 音频格式转换怎么实现?从 AVAudioFile、AAC、MP3 到 FLAC 与批量转码架构

0 阅读20分钟

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。