播放引擎
播放引擎的契约是忠实于来源:解码用户的原始字节,使用来源自身的速率,并保持发行结构。本页是其背后的规格。
所有解码都由进程内 FFmpeg 完成,覆盖每个平台:FLAC、MP3、APE、ALAC、AAC、PCM(WAV/AIFF)、Ogg Vorbis、Opus、WavPack 和 DSD 是导入接受的内容;codec 记录以 FFmpeg 从字节中报告的内容为准,而不是文件扩展名。样本会以 32-bit float 输出给输出阶段;采样格式转换是执行的唯一转换。
输出使用平台原生音频 API(桌面端和 iOS 上通过 cpal 使用 Core Audio,Android 上使用 AAudio)。输出流配置为来源的采样率和声道数:96 kHz 文件以 96 kHz 播放。没有重采样阶段。每次构建流时都会解析默认输出设备,因此播放会跟随系统的设备切换。在 Android 上,硬件支持的 codec 可以完全绕过 FFmpeg 进行 offloaded decoding。
播放不需要整个文件。音频通过 sparse buffer 读取,该 buffer 会按需从本地文件、缓存或云端获取字节范围;定位会把目标采样映射到字节位置,因此定位到尚未获取的区域时会从那里开始获取。fetch arbiter 会优先处理正在播放曲目的读取,而不是预加载,因此下一曲不会延迟当前曲目。
作为窗口的曲目
Section titled “作为窗口的曲目”曲目不是文件;它是在一个或多个文件上的有序采样窗口列表,在导入时从 CUE sheet 或文件自身范围构建。这个单一表示承载每种碟形态:每轨一个文件、整张碟一个镜像、pregap 横跨一个文件末尾和另一个文件开头,以及有损抓轨切成每轨一个文件。
Pregap 窗口会被标记。播放会在曲目有 pregap 时从 pregap 进入该曲目(index 00,包含隐藏曲目音频),而时钟显示曲目时间:在正式曲目开始前,位置夹在零,显示时长排除 pregap,与 CD 播放器行为一致。
当前曲目播放时,下一曲会被打开并缓冲。当两者共享采样率和声道数时,引擎会在实时音频 callback 内把它们串接起来:stream 从不停止,边界精确到采样。当格式不同时,stream 会重建,这也是格式变化本来就可能让边界可听见的情况。
导入会在解码验证遍历中测量 integrated loudness(EBU R128)和 true peak,按曲目和按专辑记录。Replay gain 在播放时由这些测量导出,永远不会写入文件:目标 −18 LUFS,并限制 true peak 不能削波,可按曲目或按专辑应用,并在无缝边界处平滑切换。该模式目前发布时没有设置 UI,默认关闭;可在资料库 config file 中设置。
曲目列表携带其面和碟结构;当用户启用 pause-between-sides 时,引擎可以在面边界保持住,并显示继续提示。这个保持是 playback-service state,不是解码出的静音,因此恢复会立即发生。