feat(ncm): 网易云客户端 v1 UI+播放链路 (agent-205) — os.meow.ncm: 登录页(真实unikey二维码+轮询状态机)/我的歌单(listview)/歌单详情/播放页(封面+seek+滚动歌词+竖横屏切换)/设置(音质默认Hi-res+缓存上限默认10G); 播放线程(缓存LRU+minimp3→ALSA UAPI直开hw:0,0); 媒体会话MSG_META上报+PLAYCTL接收(首真实媒体源)
ncm 1e331964 (text 764KB); 0 warning; 宿主离屏6页截图目检+真实QR可扫+qemu-mipsel干净启动+解码446xRT 真机待联调: ALSA时序/803扫码闭环/媒体会话联动/gbk大字库渲染
This commit is contained in:
1 parent
32543ac616
commit
21519ce2e4
23 files changed
+3255
No files matched your search
@@ -0,0 +1,139 @@
|
||||
/* logic/lyric.c — LRC 时间轴解析 + 当前行定位。
|
||||
*
|
||||
* 输入:ncm_api_lyric 返回的 lrc(原文)与 tlrc(翻译)。两者均为标准 [mm:ss.xx] 行。
|
||||
* 解析:把所有带时间戳的行按时间升序排序,存 timestamp(ms)/text;翻译按相同时间戳合并到
|
||||
* 对应原文行(存 tl)。无匹配的翻译行忽略。流式、有界:NCM_LYRIC_LINES 行,超限截断。
|
||||
* 纯 C 无 UI,仅依赖标准库。
|
||||
*/
|
||||
#include "../ncm_app_internal.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LRC_MAX_TEXT 192
|
||||
static long s_t[NCM_LYRIC_LINES];
|
||||
static char s_txt[NCM_LYRIC_LINES][LRC_MAX_TEXT];
|
||||
static char s_tl[NCM_LYRIC_LINES][LRC_MAX_TEXT];
|
||||
static int s_n;
|
||||
|
||||
static void lrc_parse(const char *lrc, int is_trans)
|
||||
{
|
||||
if (!lrc) return;
|
||||
const char *p = lrc;
|
||||
while (*p) {
|
||||
/* 找行首 '[' */
|
||||
while (*p && *p != '[') p++;
|
||||
if (!*p) break;
|
||||
/* 解析一个或多个时间标签 */
|
||||
int have = 0;
|
||||
long first_ms = 0;
|
||||
while (*p == '[') {
|
||||
int m = 0, s = 0, cs = 0;
|
||||
if (sscanf(p, "[%d:%d.%d]", &m, &s, &cs) >= 2) {
|
||||
long ms = (long)m * 60000L + (long)s * 1000L + (long)cs * 10L;
|
||||
if (!have) { first_ms = ms; have = 1; }
|
||||
/* 跳到本标签结尾 */
|
||||
const char *q = strchr(p, ']');
|
||||
if (!q) break;
|
||||
p = q + 1;
|
||||
} else {
|
||||
/* 非时间标签([ti:..] 等)——跳过到行尾 */
|
||||
const char *q = strchr(p, ']');
|
||||
if (!q) return;
|
||||
p = q + 1;
|
||||
/* 若行内还有时间标签继续解析(极少),否则整行的正文以第一个文本为准 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (have) {
|
||||
/* 正文 = 到行尾(去 \r\n) */
|
||||
const char *e = p;
|
||||
while (*e && *e != '\n' && *e != '\r') e++;
|
||||
size_t len = (size_t)(e - p);
|
||||
if (len > LRC_MAX_TEXT - 1) len = LRC_MAX_TEXT - 1;
|
||||
if (is_trans) {
|
||||
/* 翻译行:按 first_ms 匹配到已有原文行 */
|
||||
for (int i = 0; i < s_n; i++) {
|
||||
if (s_t[i] == first_ms) {
|
||||
memcpy(s_tl[i], p, len); s_tl[i][len] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (s_n < NCM_LYRIC_LINES) {
|
||||
s_t[s_n] = first_ms;
|
||||
memcpy(s_txt[s_n], p, len); s_txt[s_n][len] = 0;
|
||||
s_tl[s_n][0] = 0;
|
||||
s_n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (*p && *p != '\n') p++; /* 跳到下一行 */
|
||||
if (*p == '\n') p++;
|
||||
}
|
||||
}
|
||||
|
||||
static int cmp_lines(const void *a, const void *b)
|
||||
{
|
||||
const int *ia = (const int *)a, *ib = (const int *)b;
|
||||
if (s_t[*ia] < s_t[*ib]) return -1;
|
||||
if (s_t[*ia] > s_t[*ib]) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lyric_parse(const char *lrc, const char *tlrc)
|
||||
{
|
||||
lyric_clear();
|
||||
lrc_parse(lrc, 0);
|
||||
lrc_parse(tlrc, 1);
|
||||
/* 按时间排序(用索引数组) */
|
||||
int *idx = (int *)malloc(sizeof(int) * (size_t)(s_n ? s_n : 1));
|
||||
if (!idx) return s_n;
|
||||
for (int i = 0; i < s_n; i++) idx[i] = i;
|
||||
qsort(idx, (size_t)s_n, sizeof(int), cmp_lines);
|
||||
long ttmp[NCM_LYRIC_LINES];
|
||||
char txt[NCM_LYRIC_LINES][LRC_MAX_TEXT];
|
||||
char tl[NCM_LYRIC_LINES][LRC_MAX_TEXT];
|
||||
memcpy(ttmp, s_t, sizeof(s_t));
|
||||
memcpy(txt, s_txt, sizeof(s_txt));
|
||||
memcpy(tl, s_tl, sizeof(s_tl));
|
||||
for (int i = 0; i < s_n; i++) {
|
||||
s_t[i] = ttmp[idx[i]];
|
||||
memcpy(s_txt[i], txt[idx[i]], LRC_MAX_TEXT);
|
||||
memcpy(s_tl[i], tl[idx[i]], LRC_MAX_TEXT);
|
||||
}
|
||||
free(idx);
|
||||
return s_n;
|
||||
}
|
||||
|
||||
int lyric_line_at(int64_t ms)
|
||||
{
|
||||
int line = 0;
|
||||
for (int i = 0; i < s_n; i++) {
|
||||
if (s_t[i] <= ms) line = i;
|
||||
else break;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
const char *lyric_line_text(int i)
|
||||
{
|
||||
if (i < 0 || i >= s_n) return "";
|
||||
return s_txt[i];
|
||||
}
|
||||
|
||||
const char *lyric_line_tl(int i)
|
||||
{
|
||||
if (i < 0 || i >= s_n) return "";
|
||||
return s_tl[i];
|
||||
}
|
||||
|
||||
int lyric_count(void) { return s_n; }
|
||||
|
||||
void lyric_clear(void)
|
||||
{
|
||||
s_n = 0;
|
||||
memset(s_t, 0, sizeof(s_t));
|
||||
memset(s_txt, 0, sizeof(s_txt));
|
||||
memset(s_tl, 0, sizeof(s_tl));
|
||||
}
|
||||
Reference in new issue
Block a user