/* 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 #include #include #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)); }