feat(ncm): 三期第一批 — 歌单真分页(取消256截断)+专辑封面ncm_cache+歌词后台加载补锁
[agent-239] 仅改 patch_v2/ncm_app/。 1. 歌单分页(REPORT §14.1): track/all 全 404(§13.1) 弃用, 回 detail(n=1)全量 trackIds + song_detail 分批(≤NCM_SONG_BATCH=100) 触底逐页加载。appstate_load_playlist_songs_page offset=0 重建 song_ids[]/track_ids_count 载第0页, offset>0 追加(失败保留已载); playlist.c g_pl_more=(song_count<track_ids_count && <NCM_SONG_MAX), 触底 g_ploffset=song_count 起下一页(保留滚动), 首屏失败整页空态+点按重试, 追加失败 snack+保留已载。NCM_SONG_MAX 256→1000(单页渲染上限, bss +~1MB), NCM_SONG_BATCH 64→100, 删 NCM_PLAYLIST_PAGE(track/all 遗留)。 2. 专辑封面(§14.2): cover.c 封面改走 ncm_cache(LRU, 键=djb2(url)→song_cov<h>.jpg 同池), 缓存命中免下载/未命中下载→open/write/commit 落盘, 仍写 cover_current.jpg 供 launcher; 顺带修真 bug: save_cover_raw 在已持 g_cm 临界区内再 lock(非递归死锁)→封面永不刷新, 改调用方持锁。 歌单列表/详情头块封面保持现状。 3. 歌词后台加载(§14.3): lyric_worker 已异步(§11)未改线程模型; 补 lyric.c g_lyric_mu 数据竞争锁, 写侧 lyric_parse 整段持锁(清空用 lyric_clear_nolock 免二次取锁), 读侧 lyric_count/line_at/ line_text/line_tl 逐次取锁, 消除 UI/IPC 与 worker 间数据竞争(原 §10.9 FM4 良性竞态)。 验证(0 真实 NCM 请求): bench/pagination_sim.c 离线 stub 驱动真实 appstate 分页函数全 PASS (130首读全程/5191首封顶1000/空/越界无操作); mipsel build_ncm.sh 0 warning(text 842820/bss 2052068); 宿主 build_ncm_host.sh+MEOW_UI_DUMP home/playlist/player/lands 渲染正常无崩溃; host SDK_SRC 补 meow_pinyin(拼音IME py_predict 漏编致链接断)。ncm md5 b34bc1ed, ncm_host md5 1a2327e0。 未动 sdk/(主会话改 meow_ui_ime.c)/meow_launcherd/meow_amsd/vendor/core; 未 commit 它们。
This commit is contained in:
1 parent
5fac5e380d
commit
ee104f1b18
10 files changed
+471
-87
No files matched your search
@@ -10,11 +10,24 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 歌词数据由 lyric_worker(player_manager.c,后台线程)写(lyric_parse/lyric_clear)而 UI 线程
|
||||
* 与 IPC 线程读(lyric_count/lyric_line_at/lyric_line_text/lyric_line_tl)。三期第一批(REPORT §14.3)
|
||||
* 补一把锁消除**数据竞争**(早前为良性竞态 §10.9 FM4;本节要求禁止数据竞争)。锁粒度=函数级,
|
||||
* 写侧在 lyric_parse 整段持锁、读侧每次访问持锁,无嵌套(清空用 nolock 版,避免二次取锁自锁死)。 */
|
||||
#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 pthread_mutex_t g_lyric_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void lyric_clear_nolock(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));
|
||||
}
|
||||
|
||||
/* 复制一行歌词正文 n 字节到 dst,把「字面反斜杠 n/r」序列(服务器用 \n 把多行文本塞进一行,
|
||||
* 字面 \n 渲染成豆腐块/凌乱)替换为空格;顺带把 \r\n / \n\r 折叠为单空格。有界 ≤max。 */
|
||||
@@ -104,57 +117,74 @@ static int cmp_lines(const void *a, const void *b)
|
||||
|
||||
int lyric_parse(const char *lrc, const char *tlrc)
|
||||
{
|
||||
lyric_clear();
|
||||
pthread_mutex_lock(&g_lyric_mu);
|
||||
lyric_clear_nolock();
|
||||
int *idx = NULL;
|
||||
int result = 0;
|
||||
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);
|
||||
idx = (int *)malloc(sizeof(int) * (size_t)(s_n ? s_n : 1));
|
||||
if (idx) {
|
||||
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);
|
||||
}
|
||||
free(idx);
|
||||
return s_n;
|
||||
result = s_n;
|
||||
pthread_mutex_unlock(&g_lyric_mu);
|
||||
return result;
|
||||
}
|
||||
|
||||
int lyric_line_at(int64_t ms)
|
||||
{
|
||||
pthread_mutex_lock(&g_lyric_mu);
|
||||
int line = 0;
|
||||
for (int i = 0; i < s_n; i++) {
|
||||
if (s_t[i] <= ms) line = i;
|
||||
else break;
|
||||
}
|
||||
pthread_mutex_unlock(&g_lyric_mu);
|
||||
return line;
|
||||
}
|
||||
|
||||
const char *lyric_line_text(int i)
|
||||
{
|
||||
if (i < 0 || i >= s_n) return "";
|
||||
return s_txt[i];
|
||||
pthread_mutex_lock(&g_lyric_mu);
|
||||
const char *p = (i < 0 || i >= s_n) ? "" : s_txt[i];
|
||||
pthread_mutex_unlock(&g_lyric_mu);
|
||||
return p;
|
||||
}
|
||||
|
||||
const char *lyric_line_tl(int i)
|
||||
{
|
||||
if (i < 0 || i >= s_n) return "";
|
||||
return s_tl[i];
|
||||
pthread_mutex_lock(&g_lyric_mu);
|
||||
const char *p = (i < 0 || i >= s_n) ? "" : s_tl[i];
|
||||
pthread_mutex_unlock(&g_lyric_mu);
|
||||
return p;
|
||||
}
|
||||
|
||||
int lyric_count(void) { return s_n; }
|
||||
int lyric_count(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_lyric_mu);
|
||||
int n = s_n;
|
||||
pthread_mutex_unlock(&g_lyric_mu);
|
||||
return 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));
|
||||
pthread_mutex_lock(&g_lyric_mu);
|
||||
lyric_clear_nolock();
|
||||
pthread_mutex_unlock(&g_lyric_mu);
|
||||
}
|
||||
Reference in new issue
Block a user