基于炬芯ATS3089X音箱的音乐播放歌词显示功能实现

MP3 / WAV 播放与屏幕歌词同步说明

<!--zk+ 本文说明音乐歌词命令、文件准备、原理、源码入口和验证方法,使用中文维护。-->

本功能通过串口指令播放开发板用户盘中的 MP3 或 WAV,并在屏幕显示相应 LRC 文件中的同步歌词。页面显示歌曲标题、上一句、当前句、下一句和播放状态;当前句使用高亮颜色。暂停后歌词保持在实际音乐断点,继续播放后按媒体进度切换。

音频和歌词需要分别准备,例如 `LTING.MP3` 与 `LTING.LRC`。本功能读取外部 LRC,不读取 MP3 内嵌歌词,也不从声音自动识别或生成歌曲歌词。首次验证可直接执行 `app music demo`,让板端 CPU 生成原创演示文件。

1. 最快验证:板端生成演示文件

先退出 U 盘模式,再在已有串口 shell 中执行:

```text

app udisk off

app music demo

app music status

```

`app music demo` 在 `/NAND:/` 新建 `DEMO.WAV` 和 `DEMO.LRC`,生成成功后启动播放并请求显示歌词页。普通串口即可发送这些命令;USB U 盘用于复制文件。

演示音频长 12 秒,格式为 16 kHz、16 bit、单声道 PCM WAV,文件大小为 384044 字节。板端 CPU 根据采样点生成四段测试音调,每段 3 秒;歌词也是本工程原创演示文字:

```text

ti:歌词同步演示

ar:开发板

00:00.000音乐开始,歌词跟随播放

00:03.000这是第二句演示文字

00:06.000暂停音乐,歌词也会暂停

00:09.000继续播放,时间保持同步

00:12.000

```

演示声音为合成音调,用来验证文件播放和时间同步;这些文字不会被朗读成歌声。12 秒是音频长度,文件生成耗时由 NAND 写入速度决定。生成阶段本条 shell 命令会等待文件写完,再开始播放;生成过程中会让出 CPU 给显示、蓝牙等任务。

已有同名 WAV 或 LRC 时返回 `-EEXIST`,不会覆盖。再次生成可以换名字:

```text

app music demo DEMO2

```

名字只能包含 1~8 个 ASCII 英文字母、数字或下划线,不写扩展名。例如 `DEMO2` 对应 `/NAND:/DEMO2.WAV` 和 `/NAND:/DEMO2.LRC`。生成、同步或关闭文件失败时,会清理本次新建的不完整文件并释放存储预约。

已有演示文件可以直接重新播放:

```text

app music play /NAND:/DEMO.WAV

app music pause

app music resume

app music stop

```

2. 准备自己的 MP3 / WAV 和 LRC

在电脑准备匹配的音频、歌词。文件名建议使用 ASCII 8.3 格式:主文件名最多 8 个字符,扩展名最多 3 个字符。当前工程关闭了 FAT 长文件名支持,不能依赖中文名、空格或长名字。

推荐放在开发板 U 盘根目录:

```text

LTING.MP3

LTING.LRC

TEST.WAV

TEST.LRC

```

`LTING` 仅为文件名示例;实际音源和与其版本匹配的歌词由用户自行提供。不要把任意歌曲的歌词配给另一段音频,否则时间标签与声音自然会不一致。

2.1 通过 U 盘复制

  1. 连接 USB 数据线,在普通串口 shell 执行 `app udisk on`。

  2. 等待 Windows 出现开发板 U 盘。把匹配的音频、LRC 复制到该盘根目录。

  3. 等待复制完成,在 Windows 安全弹出该盘,确保写入完成。

  4. 在板端 shell 执行 `app udisk off`,等待文件系统重新挂载。

  5. 执行播放命令。

```text

app udisk on

```

在电脑复制文件、完成安全弹出后:

```text

app udisk off

app music play /NAND:/LTING.MP3

```

`/NAND:/` 是开发板的用户盘路径。电脑显示的 `E:\`、`F:\` 等盘符属于 Windows,不能作为板端播放参数。

进入 U 盘模式时会先停止原播放器、关闭文件,再交给电脑访问。同一个 NAND 卷不能同时由电脑 MSC 和板端播放器使用;U 盘模式下播放、继续播放和演示文件生成会拒绝启动。歌词文件加载或演示生成正在使用文件系统时,切盘入口也会拒绝切换,避免双方同时操作 FAT。

2.2 自备 MP3 的完整示例

电脑中的配对文件:

```text

LTING.MP3

LTING.LRC

```

复制完成并退出 U 盘模式后:

```text

app music play /NAND:/LTING.MP3

app music status

app music pause

app music status

app music resume

app music offset 200

app music stop

```

没有显式 LRC 参数时,播放器把音频末尾扩展名替换成 `.LRC`,自动查找 `/NAND:/LTING.LRC`。扩展名匹配不区分大小写,FAT 上的配对名字仍建议保持一致。

也可以指定不同名的歌词:

```text

app music play /NAND:/LTING.MP3 /NAND:/ALT.LRC

```

自动查找的 LRC 不存在或内容不合法时,音频仍可播放,页面显示"暂无同步歌词",`status` 给出 `lyric_error`。存储被 U 盘、录音或文件转换任务占用时会直接拒绝。显式指定 LRC 时,如果歌词缺失、编码或内容不合法,会拒绝本次播放请求,保留原歌词会话,不启动新的音频。

2.3 自备 WAV 的独立示例

电脑中的配对文件:

```text

TEST.WAV

TEST.LRC

```

```text

app udisk off

app music play /NAND:/TEST.WAV

app music pause

app music resume

app music stop

```

首次 WAV 测试建议使用普通线性 PCM WAV,例如 16 kHz、16 bit、单声道,与板端演示文件一致。其他 WAV 编码、MP3 采样率、声道和码率是否可播放,取决于现有 SDK parser/decoder 支持范围;不要把 `.pcm` 裸数据仅改名为 `.wav`。

3. 指令表

| 指令 | 功能 |

| --- | --- |

| `app music play /NAND:/LTING.MP3` | 播放 MP3,自动匹配同名 `.LRC`,请求显示歌词页 |

| `app music play /NAND:/TEST.WAV` | 播放 WAV,自动匹配同名 `.LRC` |

| `app music play 音频路径 歌词路径` | 使用显式指定的 LRC |

| `app music demo` | 生成 `DEMO.WAV` / `DEMO.LRC`,然后播放 |

| `app music demo DEMO2` | 用指定的 1~8 字符名字生成演示文件 |

| `app music pause` | 保存真实媒体断点并暂停 |

| `app music resume` | 从保存断点继续 |

| `app music stop` | 停止音频、清理歌词会话,关闭当前歌词页 |

| `app music status` | 打印播放状态、媒体进度、歌词加载情况和当前句 |

| `app music volume` | 查询音量及板级允许范围 |

| `app music volume 8` | 设置音量,合法上限以查询结果为准 |

| `app music offset 500` | 歌词整体晚显示 500 ms |

| `app music offset -500` | 歌词整体早显示 500 ms |

| `app music offset 0` | 当前歌词取消偏移 |

| `app music show` | 请求显示当前音乐的歌词页 |

| `app music hide` | 关闭当前歌词页,音频继续播放 |

屏幕"返回"和 `hide` 一样只关闭歌词页,停止播放请使用 `stop`。`show` / `hide` 通过 GUI 消息异步处理;命令成功表示请求已提交,不能据此单独判定页面布局和声音输出已完成。

通话、弹窗或其他应用占据前台时,显示请求会跳过,避免把当前业务界面替换掉。音乐会话仍由播放器管理;前台条件恢复后可以执行 `app music show`。页面创建失败会释放资源并返回原界面,不应留下永久空白页。

3.1 查看状态

`app music status` 的输出字段如下,以下是字段格式示例,数值取决于实际播放文件和查询时刻:

```text

music: version=261014-v1 state=1 file=/NAND:/DEMO.WAV position_ms=3500 total_ms=12000 position_valid=1

lyrics: loaded=1 lines=5 index=1 offset_ms=0 lrc=/NAND:/DEMO.LRC error=0

title=歌词同步演示 artist=开发板 current=这是第二句演示文字

```

| 字段 | 含义 |

| --- | --- |

| `state` | `0` 停止、`1` 播放、`2` 暂停、`3` 错误 |

| `position_ms` / `total_ms` | SDK 返回的播放位置和总时长,单位毫秒 |

| `position_valid` | 是否已经取得有效媒体断点;`0` 时不按系统时间猜测歌词位置 |

| `loaded` | 当前匹配会话是否加载了有效时间项 |

| `lines` | 排序并合并重复时间后的时间项数,包含用来清屏的空歌词项 |

| `index` | 当前项从 `0` 起算;第一条之前或尚无有效进度时为 `-1` |

| `offset_ms` | 当前使用的整体歌词偏移 |

| `error` | 自动歌词加载错误;`0` 表示无该错误 |

暂停期间多次查询,进度和当前句应保持稳定。若 `position_valid=0`,页面显示"等待播放进度";首句时间尚未到达时显示"歌词即将开始"。总时长以 SDK 能否正确读取媒体信息为准。

4. LRC 格式、编码和偏移

4.1 支持的时间格式

```text

00:03整秒

00:06.5一位小数,表示 6.500 秒

00:09.25两位小数,表示 9.250 秒

00:12.125三位小数,表示 12.125 秒

00:15.00000:21.000一句在两个时间出现

00:18.000

```

分钟可以超过两位;秒必须写两位且在 `00`~`59` 之间;小数允许 1~3 位。最后一条空歌词用于清屏。时间标签可以乱序,加载后会稳定排序;多个相同时间只保留输入文件中最后一行的文字。

支持 UTF-8 文本、可选 UTF-8 BOM、Windows CRLF 或 LF 换行。支持 `ti:标题`、`ar:歌手`、`offset:整数毫秒`;`al`、`by` 等其他元信息不参与同步显示。没有时间标签的纯文本行不会自动变成同步歌词。

4.2 本工程偏移定义

本工程明确采用:**正偏移让歌词整体晚显示,负偏移让歌词整体早显示。**

例如:

```text

offset:500

00:10.000这一句在播放到 10.500 秒时显示

```

同步查询等价于:

```text

查找时间 = 媒体 position_ms - 当前 offset_ms

当前句 = 最后一个 时间标签 <= 查找时间 的项目

```

`app music offset N` 设置范围为 `-10000`~`10000` 毫秒,**替换** LRC 文件的 offset,不与文件值相加。该设置只作用于当前歌词会话,不改写 U 盘中的文件;重新 `play` 会重新加载文件里的 offset。不同歌词工具可能采用不同偏移口径,导入时应以本工程上述定义为准。

4.3 容量和显示边界

| 项目 | 本次限制 |

| --- | --- |

| LRC 文件大小 | 最大 65536 字节,即 64 KiB |

| 时间标签条目 | 最多 512 个,合并重复时间前计数;一行多时间标签分别计数 |

| 单句歌词 | 最多 255 个 UTF-8 字节,不是 255 个汉字 |

| `ti` / `ar` | 各最多 95 个 UTF-8 字节 |

| 音频 / LRC 路径 | 小于 128 字节,使用 `/NAND:/` 绝对路径 |

| 文件名 | 当前 FAT 配置使用 ASCII 8.3 名称 |

| 歌词形式 | 行级时间同步,不提供逐字卡拉 OK 高亮 |

255 字节可以容纳约 85 个常见三字节汉字;屏幕可见空间更小,超长句虽可解析但可能被页面边界裁切,建议按自然短句标注时间。emoji 等字符是否显示取决于字体字形覆盖。

非法 UTF-8、UTF-16、GBK 原始字节、NUL、无有效时间项、非法秒数、超过容量等会返回明确错误。本次不支持 `<00:00.000>` 这类增强逐字时间标注,也不解析 MP3 ID3 内嵌歌词、SRT 或 ASS。

5. Windows:歌词编码转换工具

工具源码:`application/bt_watch/tools/music/prepare_lyrics.py`。它只使用 Python 标准库,不需要 GUI 或额外 Python 包。

在工程 `` 目录打开 PowerShell。例如把用户提供的原始 LRC 转成板端使用的文件:

```powershell

python .\application\bt_watch\tools\music\prepare_lyrics.py --input "D:\music\原歌词.lrc" --output "D:\music\LTING.LRC"

```

自动模式按以下顺序处理:UTF-16 BOM、UTF-8(可有 BOM)、UTF-8 解码失败时尝试 GB18030。GB18030 可用于常见 GBK 来源的歌词。若已知源编码,建议显式指定:

```powershell

python .\application\bt_watch\tools\music\prepare_lyrics.py --input "D:\music\GBK原歌词.lrc" --output "D:\music\LTING.LRC" --encoding gb18030

python .\application\bt_watch\tools\music\prepare_lyrics.py --input "D:\music\UTF16原歌词.lrc" --output "D:\music\TEST.LRC" --encoding utf-16

python .\application\bt_watch\tools\music\prepare_lyrics.py --input "D:\music\无BOM原歌词.lrc" --output "D:\music\TEST2.LRC" --encoding utf-16-le

```

输出为不带 BOM 的 UTF-8、CRLF 换行。输出名字必须是 1~8 个英文字母、数字或下划线,加 `.LRC`;目标已经存在时工具拒绝覆盖,换一个新目标名字即可。

没有 BOM 的 UTF-16 不能靠自动模式可靠判断;已知编码时使用对应参数。若转换后文字不正确,也应明确指定源编码,工具不会通过替换非法字符来掩盖解码失败。

转换工具检查基本时间标签、NUL 和 64 KiB 文件上限,完整语法、条目数和每句字节限制仍由板端解析器校验。它不会下载歌词、识别歌声、补齐未知歌词或为无时间标签的纯文本自动生成时间。

转换完成后,把 `LTING.LRC` 与用户自己的 `LTING.MP3` 一起复制到开发板 U 盘根目录,再执行:

```text

app udisk off

app music play /NAND:/LTING.MP3

```

6. 播放、同步与线程流程

```text

app music play 音频 歌词

-> 检查路径,使用指定 LRC 或构造同名 .LRC

-> 取得临时存储预约,阻止 USB 切盘及冲突的录音/转换任务

-> 在原 opus_fs_lock 中读取有界 LRC,关闭文件,释放预约

-> 校验 UTF-8、解析时间与元信息、排序及合并重复时间

-> 调用原 mp3_player_play,MP3 / WAV 选择对应 SDK parser/decoder

-> 播放成功后提交新的歌词模型,再异步请求歌词页面

播放器工作线程每约 100 ms

-> media_player_get_breakpoint 获取真实 time_offset

-> 更新缓存 position_ms / position_valid

GUI 歌词页每约 100 ms

-> 复制非阻塞播放器及歌词快照

-> 按真实媒体进度减 offset,二分查找当前时间项

-> 仅在 GUI 线程更新标题、上句、当前句、下句和状态

```

媒体进度来自 `media_player_get_breakpoint()` 返回的毫秒 `time_offset`,不会用开机时间或系统时钟累计。SDK 查询失败时保留最后一次真实进度;尚未取得有效断点时等待。因此暂停、TTS 回复临时占用音频通道或恢复断点时,歌词不会因为系统时间继续走而提前跳到后面。

`pause` 保存真实文件位置和时间断点并关闭音频资源;`resume` 按这些断点重开媒体。断点精度由 SDK 的 parser/decoder 决定,不能承诺逐个 PCM 采样点无缝恢复。

模型采用静态双缓冲,放在本板普通 BSS 对应的 PSRAM,避免把 64 KiB 文本放在线程栈上。新歌词先读入 staging 模型;显式歌词或音频启动失败时不提交,成功后再用短锁切换 active。GUI 复制文本,不持有内部模型指针;当前媒体路径与模型不匹配时不显示旧歌歌词。

FAT 文件操作沿用 `opus_fs_lock()`,不另外建立竞争的文件系统锁。歌词加载和演示生成共用 `music_lyrics_storage_begin()` / `end()`;启用 LC3 验证时沿用其文件录音预约,覆盖录音已 create 尚未 start、运行、暂停和 LC3 文件任务。所有文件关闭后释放预约,再调用同步播放器,不持歌词模型锁等待媒体线程。当前 FAT 只有 4 个文件句柄,因此歌词加载后关闭文件,播放期间从 RAM 查询歌词。

歌词页的 `show` / `hide` 投递 GUI 消息,GUI 自己创建或删除 LVGL 对象。缓存短锁忙时返回 `-EAGAIN` 并跳过本次刷新,不在绘制回调中等待播放器或访问 FAT。页面显示请求具有超时和资源清理,通话或模态页面优先。

7. 中文字体与屏幕

歌词页使用 `LVGL_FONT_OPEN_DEFAULT()` 加载中文字体。当前 ATS3089P 配置实际对应 `/NAND:C/vfont.ttf`,源文件位于:

```text

application/bt_watch/boards/ats3089p_dev_watch/fonts/vfont.ttf

```

该字体随固件字体资源分区打包;用户盘 `/NAND:/` 上只需放音频和 LRC。不要把系统默认的 UNSCII ASCII 字体当作中文字库。页面加载会检查字体打开结果和代表性中文字形;失败时报告错误并清理页面资源。

屏幕展示标题、上一句、当前句、下一句,底部显示播放/暂停及 `分:秒 / 总时长`。当前句高亮,上下句较暗。长句采用换行显示,标题过长时省略;页面尺寸按板级屏幕分辨率缩放。

播放成功与页面显示是两个阶段。串口已经显示音频启动成功、但页面未出现时,检查字体资源、GUI 创建日志以及是否正在通话/显示弹窗;可以退出其他前台业务后执行 `app music show`。声音仍需要通过实际板端播放确认。

8. 源码位置与编程入口

| 文件 | 作用和关键入口 |

| --- | --- |

| `application/bt_watch/src/main/system_app_shell.c` | `shell_music()`:`app music` 参数解析、USB 切换锁、播放/演示/状态/偏移/页面指令;旧 `shell_mp3()` 仍为音量和播放器命令入口 |

| `application/bt_watch/src/main/music_lyrics.c` | LRC 读取与 UTF-8 校验、时间解析、稳定排序、双模型提交、二分选句、文件访问预约 |

| `application/bt_watch/src/main/music_lyrics.h` | 公共模型 API、容量限制及快照结构 |

| `application/bt_watch/src/main/music_demo.c` / `.h` | `music_demo_create()`:CPU 生成 12 秒 WAV 和原创 LRC,检查同名文件、同步关闭、失败清理 |

| `application/bt_watch/src/main/mp3_player.c` / `.h` | 复用原媒体播放器,按扩展名选择 `MP3_TYPE` / `WAV_TYPE`,断点暂停继续、100 ms 真实进度缓存、音量和资源清理 |

| `application/bt_watch/src/launcher/lyrics_view.c` / `.h` | 独立 LVGL 歌词页、异步 show/hide、非阻塞刷新、中文字体、前台检查和亮屏锁释放 |

| `application/bt_watch/src/include/app_ui.h` | 歌词 view ID 及现有字体路径/字体操作宏 |

| `application/bt_watch/src/launcher/launcher_app.c` / `.h` | launcher 恢复状态查询,供异步页面请求等待 PM 完成 |

| `application/bt_watch/src/card_reader/card_reader_main.c` | 进入 USB MSC 前检查歌词文件访问预约,沿用先停止播放器再交接存储的流程 |

| `application/bt_watch/src/main/opus_cpu_file.c` / `opus_rec.h` | 已有 `opus_fs_lock()` / `unlock()` 文件系统串行访问入口 |

| `application/bt_watch/src/main/lc3_validation.c` / `.h` | 已有录音/文件任务预约,本功能在相应配置启用时临时复用 |

| `framework/media/parser_config.c` / `codec_config.c` | SDK MP3/WAV parser 和 decoder 注册;通过原媒体通道使用 DSP 解码 |

| `application/bt_watch/boards/ats3089p_dev_watch/fonts/vfont.ttf` | 随字体资源分区打包的中文字体 |

| `application/bt_watch/tools/music/prepare_lyrics.py` | Windows LRC 编码转换工具,支持源文件中文路径,输出 ASCII 8.3 UTF-8 LRC |

| `application/bt_watch/Kconfig` / `prj.conf` | 歌词功能开关与依赖 |

| `application/bt_watch/src/main/CMakeLists.txt` | 加入歌词模型与演示文件源码 |

| `application/bt_watch/src/launcher/CMakeLists.txt` | 加入歌词页面源码 |

| `application/bt_watch/tests/lyrics/` | 真实解析/模型及 GUI 生命周期 ARM 回归 |

| `application/bt_watch/tests/music_demo/` | 真实板端 WAV/LRC 生成及错误清理 ARM 回归 |

8.1 供后续 GUI/控制任务使用的接口

| API | 用途 |

| --- | --- |

| `music_lyrics_play(audio, lrc_or_NULL)` | 由控制任务加载歌词并启动音频;`NULL` 表示同名自动匹配 |

| `music_lyrics_get_snapshot(&snapshot)` | 非阻塞复制当前播放器状态和上下句;`-EAGAIN` 时稍后重试 |

| `music_lyrics_set_offset(ms)` | 替换当前歌词整体偏移 |

| `music_lyrics_clear()` | 清理当前歌词会话,不直接停止播放器 |

| `music_lyrics_storage_begin()` / `end()` | 临时文件访问预约,必须完成所有 close 后成对释放 |

| `music_lyrics_fs_busy()` | USB 切盘和其他边界检查文件访问是否忙 |

| `mp3_player_get_cached_status(&status)` | 非阻塞复制媒体状态,不让 GUI 等待同步媒体命令 |

| `lyrics_view_show()` / `hide()` | 请求 GUI 线程显示/关闭当前歌词页 |

后续界面增加"选择歌曲"时,应把完整音频/LRC 路径发送给独立控制任务,由该任务调用 `music_lyrics_play()`,成功后请求 `lyrics_view_show()`。不要在 LVGL 点击/绘制回调、launcher 主线程或 IRQ 中等待同步文件播放命令。界面刷新直接读取快照即可。

`app mp3` 原接口和已有语音音乐控制复用同一播放器实例;本次 `app music` 在其上增加外部歌词模型与独立页面。音频解析/解码沿用 SDK,歌词解析和演示 WAV 合成由 CPU 执行。新增及关键修改位置按工程要求标注 `//zk+` / `//zk+-` 和中文说明;配置、CMake、Python 使用 `#zk+` / `#zk+-`。

8.2 编译开关

```text

CONFIG_APP_MUSIC_LYRICS=y

CONFIG_PARSER_MP3=y

CONFIG_DECODER_MP3=y

CONFIG_PARSER_WAV=y

CONFIG_DECODER_WAV=y

```

`APP_MUSIC_LYRICS` 依赖当前 ATS3089P 板、音频、shell、UI_MANAGER/LVGL、文件系统、FS_MANAGER、SYS_WAKELOCK、USB 串口和 USB MSC,以及 MP3/WAV parser/decoder。中文字体使用当前 `CONFIG_LVGL_USE_FREETYPE_FONT` 和 `CONFIG_APP_FONT_DISK="/NAND:C"`;文本编码配置为 UTF-8。

该开关不依赖 LC3 算法功能;启用 LC3 时复用其录音/文件预约,关闭时使用录音状态检查。板端本次配置含 UI 支持;若关闭 GUI 导致功能依赖不满足,`app music` 不应被编入,原播放器仍按自己的配置构建。

9. 编译、打包与固件位置

在配置好原 SDK 构建环境后,从工程 `` 目录执行:

```powershell

.\zephyr\build.cmd -n -b ats3089p_dev_watch -a bt_watch

```

`-n` 清除旧的构建选择,确保本次明确选择 `ats3089p_dev_watch` 与 `bt_watch`;构建脚本会按工程流程编译并生成固件。不要直接使用仍写着 ATS3089C 板名的旧快捷命令来构建本次 ATS3089P 固件。

已有正确构建目录时,可增量编译,再重新打包:

```powershell

ninja -C .\application\bt_watch\outdir\ats3089p_dev_watch

.\zephyr\build.cmd -n -p -b ats3089p_dev_watch -a bt_watch

```

仅执行 `ninja` 生成 ELF/应用二进制,重新烧录完整 `.fw` 前仍需完成打包,使应用与字体/DSP 等资源保持一致。这里用 `-n -p -b ... -a ...` 明确本次板名与应用,避免沿用其他工程的构建选择。

本次已完成 ATS3089P 编译及普通/调试固件打包,交付文件如下:

```text

application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261014_music_lyrics.fw

application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261014_music_lyrics_debug.fw

```

普通包为 24342016 字节,调试包为 9908736 字节。最终 ELF 于 2026-10-14 16:53:35 编译;普通包与调试包分别于 16:54:37、16:54:46 打包,包含最终歌词页面修复及原中文字库。构建日志为 `application/bt_watch/outdir/music_lyrics_final_build.log`,打包日志为 `application/bt_watch/outdir/music_lyrics_pack.log`。

| 包 | SHA-256 |

| --- | --- |

| 普通 | `2DBFD4008749A8CBC1BE1B0341DB27DD8547C5613E0F3B98F2075E50280D3BF7` |

| 调试 | `13EB1B463FDFF4B9542E765D7ECDF76B72C8064C99BCF0F4584D74F10FFAD5B2` |

烧录后可执行 `app music status` 检查本次命令版本 `261014-v1`。这只确认命令和软件版本;实际 DSP 出声、显示排版和歌曲同步需通过下面的实板步骤确认。

10. 软件回归与实板验证

10.1 可在 Windows 执行的软件回归

在 `` 目录执行:

```powershell

python .\application\bt_watch\tests\lyrics\run_tests.py

python .\application\bt_watch\tests\lyrics\run_view_tests.py

python .\application\bt_watch\tests\music_demo\run_tests.py

python .\application\bt_watch\tests\mp3\run_tests.py

python .\application\bt_watch\tests\lyrics\run_shell_tests.py

python .\application\bt_watch\tests\spp\run_usb_tests.py

```

ARM 测试使用 SDK 的 `arm-none-eabi-gcc` 和 Python 测试依赖 Unicorn、pyelftools,编译并执行生产 C 源码或生产固件中的 ARM 指令。歌词模型测试同时检查 LC3 开/关的存储预约路径;GUI 测试检查页面消息、前台限制、定时器、字体失败和资源释放;演示测试检查 WAV/LRC 内容、短写、打开/同步/关闭失败以及成对清理。

本次已通过:歌词模型 LC3 开/关合计 290 项、歌词页面 112 项、演示 WAV/LRC 生成 48 项;最终固件 ELF 中的 MP3/WAV、ASR/TTS 和 USB 切换回归全部通过。新增串口命令入口 6 组回归直接运行最终固件的 `shell_music()` 与注册表,确认 `app music` 的真实注册、参数传递、错误返回及 USB 锁释放。Windows 歌词转换也已检查 UTF-8、UTF-8 BOM、GB18030、UTF-16、换行、已有目标拒绝覆盖及非法文件输入。

这些测试把 RTOS、FAT、GUI 或媒体设备边界替换为可控桩,覆盖软件逻辑,不连接开发板,不表示已经完成硬件播放、屏幕观感或 DSP 解码验证。

10.2 建议的实板验证顺序

  1. 烧录本次固件,确认开机原页面正常,`app music status` 能识别。

  2. `app music demo DEMO2` 生成新名字文件,确认测试音和中文歌词页同时出现。

  3. 在第二句中间执行 `pause`,等待几秒,多次 `status` 确认进度和歌词不走;执行 `resume`,确认从原位置继续。

  4. 分别设置 `offset 500`、`offset -500`、`offset 0`,按本工程正值后移的定义观察切句变化。

  5. `hide` 确认音频继续,`show` 恢复歌词页;屏幕返回也检查同样行为;`stop` 确认音频停止。

  6. 按 U 盘流程复制自己的 `LTING.MP3/LTING.LRC`,退出 U 盘后播放;再独立验证自己的 WAV 文件。

  7. 测试自动缺 LRC、显式坏 LRC、乱码源文件转换、文件自然播放结束、重复播放和切换歌曲。

  8. 检查原语音"暂停音乐/播放音乐"及 TTS 回复;检查通话、弹窗、页面切换后能继续正常显示和退出。

  9. 播放期间进入 `app udisk on`,确认音频文件关闭后电脑能访问盘;安全弹出并 `off` 后可重新播放。

出现问题时保留实际音频、LRC、`app music status` 输出和完整日志,便于区分文件内容、媒体断点、字体和 GUI 问题。

11. 常见现象

| 现象 | 检查方法 |

| --- | --- |

| `app unknown parameter: music` | 确认已烧录本次固件,且 `CONFIG_APP_MUSIC_LYRICS=y` |

| 自动模式能听到音乐,但显示"暂无同步歌词" | 检查同名 `.LRC`、UTF-8 编码和 `status` 的 `error` |

| 显式 LRC 返回错误,新歌未播放 | 此模式先严格校验歌词;修正路径/编码/时间格式后重试 |

| `-EBUSY` | 退出 U 盘/USB 音频,等待演示生成或文件转换结束,停止冲突的录音/媒体任务后重试 |

| `-EEXIST`,无法重新生成演示 | WAV 或 LRC 已存在,使用新 basename,或直接播放已有演示 WAV |

| `-EILSEQ` | 歌词不是合法 UTF-8,使用转换工具并明确源编码;也检查字体缺字日志 |

| `-E2BIG` | 检查 64 KiB、512 原始时间标签、255 字节单句、95 字节标题/歌手限制 |

| `position_valid=0` / "等待播放进度" | SDK 尚未给有效断点;不要用系统时间补算,保留媒体日志及文件分析 |

| 歌词长期提前或滞后 | 检查歌词是否匹配同一音源版本;再用 offset 调整固定差值 |

| 越播误差越大 | 通常不是固定 offset 能修复,检查 LRC 时间标注、音频版本和媒体实际进度 |

| MP3/WAV 有声音但页面未显示 | 检查是否在通话/弹窗/其他应用,检查字体和 GUI 日志,再执行 `show` |

| 中文缺失或页面布局失败 | 确认打包包含 `/NAND:C/vfont.ttf`,保留 font open / missing glyph 日志 |

| 电脑有文件,板端找不到 | 检查 8.3 ASCII 短名、是否放在根目录、是否已安全弹出并执行 `app udisk off` |

<!--zk+- MP3/WAV 歌词使用与开发说明结束;硬件结果按实板验证更新。-->

相关推荐
、达西先生1 小时前
【unifolm_wla】从动作语言到连续机器人控制
人工智能·深度学习·机器人·宇树·wam·wla
CV@CV1 小时前
世界模型环境预测与内在表征基础原理
人工智能·学习·边缘计算·量子计算·具身智能
知识分享小能手1 小时前
C学习教程,从入门到精通,C语言数组 —— 知识点详解(8)
c语言·学习·算法
老余说AI1 小时前
2026 多语言语音克隆 AI 工具推荐:把克隆嵌进视频本地化全流程
人工智能·声音克隆
hetao17338371 小时前
联盟场-提高组-R2 & ZYZSC-S-Round 5
c++·算法
comyccccc1 小时前
TB6612编码电机驱动板设计全解析学习笔记
单片机·嵌入式硬件
HAHAXX81 小时前
可以直接部署使用的AI RPA有哪些?全离线内网部署方案实测
人工智能·rpa
honsor1 小时前
以太网温湿度传感器原理与 Modbus TCP 组网实战,机房动环监控落地
网络·嵌入式硬件·物联网·网络协议·tcp/ip
前端小菜鸡yy1 小时前
前端 + AI 全链路开发教程:从设计图、需求文档、接口文档到项目管理
人工智能