MelismaKit 是一个专为 macOS 打造的原生歌词渲染组件,基于 AppKit、Core Text、Core Animation 与 Core Image 构建。它直接支持与 AMLL 兼容的 TTML 歌词格式,并以秒为单位维护媒体计时;宿主应用仅需提供播放状态,并继续持有音频播放器的完整控制权。
本软件包分为两个产物(Products):
MelismaKit:包含核心渲染器、TTML 解码器、时间线、布局引擎、动效系统、交互逻辑以及数据值模型。MelismaKitSwiftUI:包含一个轻量级的NSViewRepresentable桥接封装。用于挂载宿主持有的LyricsView,不会镜像内部渲染状态,亦不创建第二套控制器层级。
外观样式与渲染行为完全由组件内部自持。LyricsConfiguration 提供了对排版字体、调色板、对齐方式、翻译/Ruby注音/罗马音、逐字高亮与脏话过滤模式、封面模糊通道、图层混合模式、栅格化渲染质量、DisplayLink 帧率上限以及动效参数的完整控制。宿主应用只需传入语义化配置和播放状态,无需直接侵入图层树或处理 DOM 细节。
- macOS 15 或更高版本
- Swift 6.1 或更高版本的兼容工具链
- 渲染器核心依赖 AppKit;SwiftUI 支持为可选依赖(通过
MelismaKitSwiftUI)
添加软件包依赖:
dependencies: [
.package(url: "https://github.com/kmgcc/melismakit.git", from: "0.2.0")
]然后在目标 Target 中链接所需产物:
dependencies: [
.product(name: "MelismaKit", package: "MelismaKit"),
.product(name: "MelismaKitSwiftUI", package: "MelismaKit")
]LyricsView 是该组件的对外边界。宿主在主线程(MainActor)上创建实例、加载 TTML 字节数据、同步播放采样,并处理歌词点击 Seek:
import MelismaKit
@MainActor
final class LyricsController {
let view = LyricsView(frame: .zero)
init() {
view.onSeek = { sourceTime in
// 通知宿主音频播放器跳转(Seek)到 sourceTime
}
}
func load(ttml: Data) throws {
try view.load(ttml: ttml, time: 0, playing: false)
}
func update(time: Double, isPlaying: Bool) {
view.synchronize(time: time, playing: isPlaying)
}
}当宿主需要自定义呈现样式时,直接配置该组件:
var configuration = LyricsConfiguration()
configuration.fontSize = 36
configuration.showTranslation = true
configuration.motion.pointerExitDelay = 0
configuration.palette.mainActive = LyricsColor(0.95, 0.98, 1.0)
lyricsController.view.configuration = configuration对于后台导入场景,可在 UI 线程之外完成解码,再将已解析的 Document 对象安装到主线程的视图中。同步调用方的 load(ttml:) 依然可用且行为保持不变:
@MainActor
func loadInBackground(_ data: Data, into view: LyricsView) async throws {
let document = try await TTMLDecoder().decodeAsync(data)
view.install(document: document)
}SwiftUI 产物有意保持职责归属明确:
import MelismaKit
import MelismaKitSwiftUI
import SwiftUI
struct LyricsPanel: View {
let lyricsView: LyricsView
var body: some View {
MelismaKitViewRepresentable(view: lyricsView)
}
}请在宿主的状态或控制器生命周期中持有 LyricsView。尽管 SwiftUI 可能会频繁重建 Representable 结构体值,底层的 AppKit 视图依然是渲染和交互的唯一数据源(Single Source of Truth)。
![]() |
![]() |
| 当前活跃行的逐字卡拉OK式扫亮效果 | 主文本上方的 Ruby 注音(振假名),并支持罗马音与歌词翻译图层 |
![]() |
![]() |
| 无歌词演唱时的呼吸间奏跳动点 | 由逐字时间驱动的重音缩放(Emphasis)、发光(Glow)与动态模糊 |
上述所有图片均由 MelismaKitProbe 基于内置固件确定性渲染生成,而非手动截屏。重新生成方式见 Demo 与探针工具。
在仓库根目录下运行 AppKit Demo:
swift run --quiet MelismaKitDemo
swift run --quiet MelismaKitDemo --ttml /path/to/lyrics.ttmlDemo 内置了覆盖逐字计时、逐行计时、发光动效、对唱/Ruby 注音以及伴唱人声的 TTML 测试固件。可以通过打开面板选择外部文件。若要检查基于文件夹的曲库目录,可显式传入一个或多个根目录;Demo 绝不会读取任何特定宿主应用的曲库注册表:
swift run --quiet MelismaKitDemo --dump-catalog --library-root /path/to/Library曲库扫描器支持包含 Tracks/<track>/lyrics.ttml 的根目录、Tracks 目录或直接包含 lyrics.ttml 的单曲目录。同目录下的 meta.json 与音频文件为可选项。
进行确定性渲染基准测量:
swift run --quiet MelismaKitProbe Sources/MelismaKitDemo/Resources/complex.ttml /tmp/melismakit-probe 10 760 720 --paused当前的测试与人工验证记录参见 VALIDATION.md,回归测试所覆盖的渲染器契约参见 BEHAVIOR-REGRESSIONS.md。
项目的未来演进方向及各阶段执行计划参见 路线图 (Documentation/ROADMAP.md)。
核心软件包不依赖任何特定的播放器应用、曲目数据模型、设置存储、主题存储、WebView、音频引擎或应用 Bundle 资源。它完全通过值类型、配置对象、onSeek 回调以及传入 synchronize 的播放采样与宿主通信。
LyricsProfile.currentPlayer 是为需要现有原生计时行为的宿主提供的兼容性预设;LyricsProfile.upstream 则是显式的上游风格预设。两者均在组件内部实现,宿主无需维护复杂的兼容协调层。
更多接入细节详见 接入指南 (Documentation/INTEGRATION.md)。
AMLL (Apple Music-like Lyrics) 是一个 Web 端实现:基于 TypeScript、DOM 图层树与浏览器渲染管线。MelismaKit 是专为 macOS 打造的原生实现,基于 AppKit、Core Text 与 Core Animation。它并非 AMLL 的官方 Swift 移植版本,亦未隶属于 AMLL 项目或获得其背书。
两者共享的是格式规范,而非代码库。MelismaKit 解析相同的 TTML 规范子集,因此能在 AMLL 中正常呈现的文件也能在此渲染。其计时、布局、动效和交互行为经过精心设计,以匹配 AMLL 的可观察行为 —— 并且有若干函数是通过直接阅读 AMLL 源码而非文档实现的。这些函数属于派生作品:
NOTICE记录了对 AMLL 的致谢归属、派生涉及的模块以及代码查阅日期。Documentation/PROVENANCE.md逐个符号列出了各库源码文件中哪些函数属于派生、哪些属于独立原创,以及哪些尚无法完全确定。
两个项目均采用 AGPL-3.0-only 许可证,因此协议完全兼容。
本项目仅在 GNU Affero 通用公共许可证第 3 版(AGPL-3.0-only)下分发。详见 LICENSE、NOTICE 以及 Documentation/LICENSING.md。




