oynix

于无声处听惊雷,于无色处见繁花

Unity中AudioClip的导入设置

为了减少最后导出包体的大小,通常会对导入的音频文件做一些通用设置。将其写成一个工具栏的按钮,便于一键操作。

一、 可配置项

在Editor中选中一个音频文件,在Inspector标签页中看到的便是全部可配置的选项,需要做的便是针对不同的音频文件,作出对应的设置。对于每一个可配置项,官方都有着详尽的说明。

Unity Documents: Audio Clip

1. Force To Mono

启用此选项后,多声道音频将在打包前混合味单声道。

2. Normalize

启用此选项后,音频将在强制喂单声道混合过程中被标准化。至于标准化,维基百科上说的是存在两种主要类型的音频标准化:峰值归一化和响度归一化。标准化之后,给人的直观感受就是音量较为统一。

3. Load In Background

如果启用此选项,音频剪辑将在后台加载,不会导致主线程停顿。默认情况下,此选项为关闭状态以确保 Unity 为标准形式,即:在场景开始播放时所有音频剪辑已完成加载。请注意,针对仍在后台加载的音频剪辑的播放请求将被延迟,直到剪辑完成加载。可通过 AudioClip.loadState 属性来查询加载状态。

4. Preload Audio Data

如果启用此选项,则在加载场景时提前加载音频剪辑文件。默认情况下,此选项为开启状态以反映 Unity 标准模式,即:在场景开始播放时所有音频剪辑文件已完成加载。如果未设置此标志,音频数据将加载到第一个 AudioSource.Play()/AudioSource.PlayOneShot(),或者可通过 AudioSource.LoadAudioData() 加载并通过 AudioSource.UnloadAudioData() 卸载。

5. Quality

确定要应用于__压缩__剪辑的压缩量。不适用于 PCM/ADPCM/HEVAG 格式。可在检视面板中查看有关文件大小的统计信息。要调整此值,建议将滑动条拖动到某个位置让播放质量“足够好”,同时又保持文件足够小以满足发布条件。请注意,原始大小与原始文件有关,因此如果这是一个 MP3 文件并且压缩格式设置为 PCM(即未压缩),则生成的比率将大于 100%,因为该文件是未压缩存储,并占用超过了 MP3 格式文件的原始空间。

6. Load Type

Unity在运行时加载音频资源的方法,有3个选项。

a. Decompress On Load

音频文件加载后将立即解压缩。请对小的压缩过的声音使用此选项,以避免动态解压缩产生性能开销。请注意,在加载时解压缩 Vorbis 编码的声音所使用的内存量是保持压缩状态时内存使用量的 10 倍(ADPCM 编码约为 3.5 倍),因此请勿对大文件使用此选项。

b. Compressed In Memory

声音在内存中保持压缩状态,播放时解压缩。此选项具有轻微的性能开销(特别是对于 Ogg/Vorbis 压缩文件),因此仅应当将其用于较大的文件(此情况下,加载时解压缩将使用大量的内存)。解压缩发生在混音器线程中,可在性能分析器窗口的音频面板中的“DSP CPU”部分对其进行监控。

c. Streaming

即时解码声音。此方法使用最少量的内存来缓冲从磁盘中逐渐读取并即时解码的压缩数据。请注意,解压缩发生在单独的串流线程上;可在性能分析器窗口的音频面板中的“Streaming CPU”部分监控其 CPU 使用率。注意:即使没有加载任何音频数据,串流剪辑也有大约 200KB 的过载量。

7. Compression Format

在运行时将用于声音的特定格式

PCM

此选项提供高质量,代价是文件内存变大,适合内存小的声音效果。

ADPCM

此格式适用于大量噪音和需要大量播放的声音(例如脚步声、撞击声、武器声音)。较之于 PCM,压缩能力提高 3.5 倍,但 CPU 使用率远低于 MP3/Vorbis 格式,因此成为上述声音类别的最佳压缩方案。

Vorbis/MP3

压缩使文件减小,但与 PCM 音频相比,质量降低。可通过 Quality 滑动条来配置压缩量。此格式最适合中等长度的音效和音乐。

8. Sample Rate Setting

PCM和ADPCM压缩格式允许自动优化或手动降低采样率

a. Preserve Sample Rate

此设置可保持采样率不变(默认值)。

b. Optimize Sample Rate

此设置根据分析的最高频率内容自动优化采样率。

c. Override Sample Rate

此设置允许手动覆盖采样率,因此可有效地将其用于丢弃频率内容。

9. Ambisonic

立体混响声 (Ambisonic) 音频源作为一种音频格式,表示可根据听者方向而旋转的声场。它非常适合 360 度视频和 XR 应用程序。如果音频文件包含立体混响声编码的音频,请启用此选项。

二、 策略

根据上面的配置项介绍,可以看出,但从音频文件的大小这一维度去考虑基本就可以应对绝大多数情况,剩下的具体情况具体分析即可。

  • 如无特殊情况,统一使用单声道,这个配置在缩小包体上收益最高。
  • 标准化需启用,配合单声道使用。
  • Load In Background无需启用,同步加载就好,可以减少很多异步处理。
  • Preload Audio Data需启用,原因同上。
  • Ambisonic无需启用,如有立体声音频的需要,可单独处理。
  • Load Type根据文件大小选择,200KB以下使用Decompressed On Load,这基本可以涵盖大部分一次性音效文件,剩下的就是背景音,采用Streaming方式。
  • Compression Format根据Load Type设定,Decompressed On Load使用PCM,Streaming使用Vorbis,Quality使用100,也可以按照实际效果降低一些。如有符合ADPCM的音效,如爆炸声、脚步声,也可以特殊处理,采用ADPCM。
  • Sample Rate Setting采用Preserve,保持原有采样率,这是基于文本本身采样率较为统一,如果音效文件的采样率各个都不同,也可以选择Optimize。

三、 代码

通过代码操作AudioClip的配置,需要通过AudioImporter。我的资源文件都放在了同一个目录下,所有没有遍历Assets下的所有文件,如有需要,也可全部遍历一次。

另外需要注意的是,获取AudioImporter时,传入的路径是Assets开头的相对路径,而不是绝地路径,传入绝对路径返回值是空。

移除了其他平台的覆盖配置,只保留了缺省配置。以下是按照上述逻辑实现简单的示例代码,若有其他需要,在其后补充逻辑即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
[MenuItem("Tools/Checkout Audio Clip")]
public static void FormatAudioClick()
{
Handle();
}

private static void Handle()
{
var path = Path.Combine(Application.dataPath, "AddressablesAssets");
HandleDir(path);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}

private static void HandleDir(string path)
{
var dirInfo = new DirectoryInfo(path);
foreach (var dir in dirInfo.GetDirectories())
{
HandleDir(dir.FullName);
}

foreach (var file in dirInfo.GetFiles())
{
HandleFile(file.FullName);
}
}

private static void HandleFile(string path)
{
var info = new FileInfo(path);
if (!AudioExtension.Contains(info.Extension)) return;

var audioImporter = AssetImporter.GetAtPath(TransformToRelevant(path)) as AudioImporter;
if (audioImporter == null)
{
DebugUtil.Log($"audio importer not found for path: {path}");
return;
}


var changed = false;

if (audioImporter.GetOverrideSampleSettings("android").GetHashCode() !=
audioImporter.defaultSampleSettings.GetHashCode())
{
changed = true;
audioImporter.ClearSampleSettingOverride("android");
}

if (audioImporter.GetOverrideSampleSettings("iOS").GetHashCode() !=
audioImporter.defaultSampleSettings.GetHashCode())
{
changed = true;
audioImporter.ClearSampleSettingOverride("iOS");
}

if (!audioImporter.forceToMono)
{
changed = true;
audioImporter.forceToMono = true;
}

if (audioImporter.loadInBackground)
{
changed = true;
audioImporter.loadInBackground = false;
}

if (audioImporter.ambisonic)
{
changed = true;
audioImporter.ambisonic = false;
}

if (!audioImporter.preloadAudioData)
{
changed = true;
audioImporter.preloadAudioData = true;
}

var defaultSettings = audioImporter.defaultSampleSettings;
var settings = new AudioImporterSampleSettings();
if (info.Length < 204800)
{
settings.loadType = AudioClipLoadType.DecompressOnLoad;
if (defaultSettings.loadType != AudioClipLoadType.DecompressOnLoad) changed = true;

settings.compressionFormat = AudioCompressionFormat.Vorbis;
if (defaultSettings.compressionFormat != AudioCompressionFormat.Vorbis) changed = true;

settings.quality = 1;
if (defaultSettings.quality < 1 || defaultSettings.quality > 1) changed = true;

settings.sampleRateSetting = AudioSampleRateSetting.PreserveSampleRate;
if (defaultSettings.sampleRateSetting != AudioSampleRateSetting.PreserveSampleRate) changed = true;
}
else
{
settings.loadType = AudioClipLoadType.Streaming;
if (defaultSettings.loadType != AudioClipLoadType.Streaming) changed = true;

settings.compressionFormat = AudioCompressionFormat.Vorbis;
if (defaultSettings.compressionFormat != AudioCompressionFormat.Vorbis) changed = true;

settings.quality = 0.8f;
if (defaultSettings.quality < 0.8f || defaultSettings.quality > 0.8f) changed = true;

// 大文件的采样率
settings.sampleRateSetting = AudioSampleRateSetting.PreserveSampleRate;
if (defaultSettings.sampleRateSetting != AudioSampleRateSetting.PreserveSampleRate) changed = true;
}

audioImporter.defaultSampleSettings = settings;
if (changed)
{
audioImporter.SaveAndReimport();
}
}

对于大文件,修改采样率是收益最高的操作,但是采样率会直接影响音频的品质,所以这二者之间的关系需要根据实际需求来调和

四、注意

经过实践验证发现,该方式在Editor中操作有效,在batch mode中操作无效,猜测问题在于SaveAndReimport方法。

------------- (完) -------------
  • 本文作者: oynix
  • 本文链接: https://oynix.com/2024/02/1e78fb0fc52b/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道