diff --git a/patch_v2/meow_audiod/meow_audio b/patch_v2/meow_audiod/meow_audio index 790948f..6dd13a7 100755 Binary files a/patch_v2/meow_audiod/meow_audio and b/patch_v2/meow_audiod/meow_audio differ diff --git a/patch_v2/meow_audiod/meow_audio.c b/patch_v2/meow_audiod/meow_audio.c index 53f509d..ef0932c 100644 --- a/patch_v2/meow_audiod/meow_audio.c +++ b/patch_v2/meow_audiod/meow_audio.c @@ -7,6 +7,7 @@ * meow_audio tone [freq] [ms] [rate] 正弦测试音(默认 440Hz 500ms 44100;排查无声不须开 App) * meow_audio route <0|2|3> 手动切路由(0=auto 2=3.5 3=4.4;调试/面板) * meow_audio vol [0-100] 设置音量(无参=查询;v1 透传写 DAC numid=1/2 + settings.conf) + * meow_audio monitor [秒] 终端实时电平表(rms/peak dBFS + 文本电平条,200ms 刷新) * * 传输:AF_UNIX SOCK_STREAM 到 audiod socket(env MEOW_AUDIO_SOCK / --sock 覆盖,宿主/qemu)。 * 帧格式复用 meow_ipc.h 16B 头 + m_audio_* 负载。无 audiod → 连接失败/超时 → 打印友好错误并退出。 @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -163,6 +165,93 @@ static int cmd_status(void) return 0; } +/* ---- monitor:终端实时电平表(200ms 刷新,文本电平条;数据源=MSG_AUDIO_STATE 的 rms/peak 键) ---- */ +static volatile sig_atomic_t g_mon_stop = 0; +static void on_mon_sig(int sig) { (void)sig; g_mon_stop = 1; } + +/* 从 STATE 文本取整型字段(key 含 '=',如 "rms=";缺省 def) */ +static int txt_get_int(const char *t, const char *key, int def) +{ + const char *p = strstr(t, key); + return p ? atoi(p + strlen(key)) : def; +} +static void txt_get_str(const char *t, const char *key, char *out, size_t n) +{ + const char *p = strstr(t, key); + if (!p) { snprintf(out, n, "-"); return; } + p += strlen(key); + size_t k = 0; + while (p[k] && p[k] != ' ' && p[k] != '\n' && p[k] != '\t' && k < n - 1) { out[k] = p[k]; k++; } + out[k] = 0; +} +/* deci-dB → "-13.1"(地板显 "-inf") */ +static void fmt_db10(char *out, size_t n, int v) +{ + if (v <= -990) { snprintf(out, n, " -inf"); return; } + int neg = v < 0; + int a = neg ? -v : v; + snprintf(out, n, "%s%3d.%d", neg ? "-" : " ", a / 10, a % 10); +} +/* 文本电平条:-60..0 dBFS → 0..30 格 */ +static void print_bar(int db10) +{ + int cells = 0; + if (db10 > -600) cells = (db10 + 600) * 30 / 600; + if (cells > 30) cells = 30; + putchar('['); + for (int i = 0; i < 30; i++) putchar(i < cells ? '#' : '-'); + putchar(']'); +} + +static int cmd_monitor(int secs) +{ + if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s) — 音频服务不可用\n", g_sock); return 1; } + signal(SIGINT, on_mon_sig); + signal(SIGTERM, on_mon_sig); + printf("monitor: 5Hz 刷新,%s(Ctrl-C 退出)\n", secs > 0 ? "定时自动停" : "持续监视"); + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t t0 = (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; + uint8_t rbuf[MEOW_IPC_MAX]; + uint16_t rt; + while (!g_mon_stop) { + int n = req_resp(MSG_AUDIO_QUERY, NULL, 0, &rt, rbuf, sizeof rbuf); + if (n < 0 || rt != MSG_AUDIO_STATE) { + fprintf(stderr, "\nmeow_audio: query failed (audiod died?)\n"); + return 1; + } + rbuf[n < (int)sizeof rbuf - 1 ? n : (int)sizeof rbuf - 1] = 0; + const char *txt = (const char *)rbuf; + int active = txt_get_int(txt, "active=", 0); + int rms = txt_get_int(txt, "rms=", -990); + int pk = txt_get_int(txt, "peak=", -990); + int vol = txt_get_int(txt, "vol=", -1); + int port = txt_get_int(txt, "port=", -1); + int rate = txt_get_int(txt, "rate=", 0); + char app[40]; + txt_get_str(txt, "app=", app, sizeof app); + char rmsb[16], pkb[16]; + fmt_db10(rmsb, sizeof rmsb, rms); + fmt_db10(pkb, sizeof pkb, pk); + if (active) { + printf("\rLIVE rms=%sdBFS peak=%sdBFS ", rmsb, pkb); + print_bar(pk); + printf(" %s %dHz port=%d vol=%d%% ", app, rate, port, vol); + } else { + printf("\rIDLE 无活跃流(等待播放…) "); + } + fflush(stdout); + if (secs > 0) { + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t now = (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; + if (now - t0 >= (uint64_t)secs * 1000) break; + } + usleep(200000); /* 200ms 刷新 */ + } + putchar('\n'); + return 0; +} + static int cmd_route(int port) { m_audio_route_t r; r.port = (uint32_t)port; @@ -284,7 +373,8 @@ static int cmd_tone(int freq, int ms, int rate) static void usage(const char *argv0) { - fprintf(stderr, "usage: %s status | tone [freq] [ms] [rate] | route <0|2|3> | vol [0-100]\n" + fprintf(stderr, "usage: %s status | tone [freq] [ms] [rate] | route <0|2|3> | vol [0-100] | monitor [秒]\n" + " monitor: 终端实时电平表(200ms 刷新+文本电平条;缺省 10s,0=持续到 Ctrl-C)\n" " env: MEOW_AUDIO_SOCK (socket 缺省 %s)\n", argv0, AUD_SOCK_DEFAULT); } @@ -325,6 +415,10 @@ int main(int argc, char **argv) if (freq <= 0 || ms <= 0 || rate <= 0) { fprintf(stderr, "tone: bad params\n"); return 2; } if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s) — 音频服务不可用\n", g_sock); return 1; } return cmd_tone(freq, ms, rate); + } else if (!strcmp(cmd, "monitor")) { + int secs = (argc >= 3) ? atoi(argv[2]) : 10; + if (secs < 0) secs = 10; + return cmd_monitor(secs); } usage(argv[0]); return 2; diff --git a/patch_v2/meow_audiod/meow_audio_host b/patch_v2/meow_audiod/meow_audio_host index 5cafc0f..61107f1 100755 Binary files a/patch_v2/meow_audiod/meow_audio_host and b/patch_v2/meow_audiod/meow_audio_host differ diff --git a/patch_v2/meow_audiod/meow_audiod b/patch_v2/meow_audiod/meow_audiod index b52af81..3f0eea5 100755 Binary files a/patch_v2/meow_audiod/meow_audiod and b/patch_v2/meow_audiod/meow_audiod differ diff --git a/patch_v2/meow_audiod/meow_audiod.c b/patch_v2/meow_audiod/meow_audiod.c index 43bc1ca..d62bd76 100644 --- a/patch_v2/meow_audiod/meow_audiod.c +++ b/patch_v2/meow_audiod/meow_audiod.c @@ -171,6 +171,25 @@ static int g_dev_open = 0; static uint32_t g_dev_rate = 0, g_dev_ch = 0, g_dev_fmt = 0; static int g_reconfig_pending = 0; +/* ---- 数字域电平表(DAC 实时监控;CS43198 无模拟电平回读,播放线程看 PCM 样本) ---- + * 播放线程对从 ring 取出的每块 PCM 做纯整数累加(归一到 16bit 域:S24/S32 >>16), + * ~100ms 窗口(rate/10 帧)到点才结算一次 RMS/peak 并发布快照——每帧仅几次整数加乘, + * 不拖慢播放线程;无流时无数据读出即零开销。不用浮点(MIPS 软浮点昂贵)。 */ +#define METER_FLOOR_DB10 (-990) /* 静音地板 -99.0 dBFS */ +static uint64_t g_m_sumsq = 0; /* 窗口内样本平方和(16bit 域) */ +static uint32_t g_m_peak = 0; /* 窗口峰值(0..32768) */ +static uint32_t g_m_frames = 0; /* 窗口已计帧 */ +static uint32_t g_m_win = 0; /* 窗口目标帧数(rate/10;0=未配置/无流) */ +/* 发布快照(g_mu 保护;deci-dB,METER_FLOOR_DB10=静音) */ +static int g_lvl_rms_db10 = METER_FLOOR_DB10; +static int g_lvl_peak_db10 = METER_FLOOR_DB10; +static uint32_t g_lvl_seq = 0; /* 快照代际(每次结算/开流复位递增) */ + +/* ---- 全面监控日志(主线程 1s 节拍写;流活跃才写,流停止即停写,无流零开销) ---- */ +static int g_monfd = -1; +static uint64_t g_mon_prev_played = 0; +static int64_t g_mon_prev_ms = 0; + /* ---- 日志 ---- */ static void logmsg(const char *fmt, ...) { @@ -299,6 +318,73 @@ static const char *fmt_name(int fmt) } static int frame_size(void) { return g_st.ch * fmt_bps(g_st.fmt); } +/* 整数开平方(uint64→uint32,逐位法;替代软浮点 sqrt) */ +static uint32_t isqrt64(uint64_t x) +{ + uint64_t r = 0, bit = 1ULL << 62; + while (bit > x) bit >>= 2; + while (bit) { + if (x >= r + bit) { x -= r + bit; r = (r >> 1) + bit; } + else r >>= 1; + bit >>= 2; + } + return (uint32_t)r; +} + +/* 线性幅度(16bit 域 0..32768)→ deci-dBFS(×10 整数,相对满幅 32768)。 + * log2 = 整数位 + octave 内线性插值;最大误差 ~0.5dB,电平表/日志足够。 */ +static int db10_of_lin(uint32_t v) +{ + if (v == 0) return METER_FLOOR_DB10; + if (v > 32768) v = 32768; + int b = 0; + uint32_t t = v; + while (t >>= 1) b++; /* b = floor(log2(v)) ∈ 0..15 */ + uint32_t frac = v - (1u << b); + /* 20*log10(2)*10 = 60.206 */ + int64_t db10 = (int64_t)60206 * (b - 15) / 1000; + db10 += (int64_t)60206 * (int64_t)frac / ((int64_t)1000 << b); + if (db10 < METER_FLOOR_DB10) db10 = METER_FLOOR_DB10; + return (int)db10; +} + +/* 窗口结算 + 发布快照(调用方已持 g_mu) */ +static void meter_window_end(void) +{ + uint64_t samples = (uint64_t)g_m_frames * (g_st.ch ? g_st.ch : 2); + uint32_t rms = samples ? isqrt64(g_m_sumsq / samples) : 0; + g_lvl_rms_db10 = db10_of_lin(rms); + g_lvl_peak_db10 = db10_of_lin(g_m_peak); + g_lvl_seq++; + g_m_sumsq = 0; g_m_peak = 0; g_m_frames = 0; +} + +/* 喂一块 PCM 进电平表(调用方已持 g_mu,播放线程 ring_read 后紧跟调用)。 + * 逐样本整数累加;**逐帧判窗口边界**(帧循环里一次加+比较,开销可忽略)—— + * 若按块末尾才结算,块越过窗口边界的部分会混进旧窗口(宿主流失真正弦+静音 + * 混合窗口实测 rms 偏差 7dB+)。fmt 为 grant 格式;S24/S32 归一 >>16。 */ +static void meter_feed(const uint8_t *buf, size_t n) +{ + if (!g_m_win) return; + int bps = fmt_bps(g_st.fmt); + int ch = g_st.ch ? (int)g_st.ch : 2; + size_t fs = (size_t)ch * (size_t)bps; + if (fs == 0) return; + size_t frames = n / fs; + for (size_t i = 0; i < frames; i++) { + const uint8_t *p = buf + i * fs; + for (int c = 0; c < ch; c++, p += bps) { + int32_t v; + if (bps == 2) { int16_t s; memcpy(&s, p, 2); v = s; } + else { int32_t s; memcpy(&s, p, 4); v = s >> 16; } + uint32_t a = (v < 0) ? (uint32_t)(-(int64_t)v) : (uint32_t)v; + g_m_sumsq += (uint64_t)a * a; + if (a > g_m_peak) g_m_peak = a; + } + if (++g_m_frames >= g_m_win) meter_window_end(); + } +} + static size_t ring_bytes(void) /* 已缓冲字节 */ { return (g_wpos >= g_rpos) ? (g_wpos - g_rpos) : (g_ring_cap - g_rpos + g_wpos); @@ -936,6 +1022,7 @@ static void *playback_thread(void *arg) size_t fs = (size_t)frame_size(); if (fs > 0) want = (want / fs) * fs; size_t n = ring_read_locked(buf, want); + if (n) meter_feed(buf, n); /* 电平表:整数累加,窗口到点才结算 */ pthread_mutex_unlock(&g_mu); if (n == 0) continue; dev_write(buf, n); @@ -1044,6 +1131,12 @@ static void dispatch(struct client *c, uint16_t type, uint32_t req_id, const uin g_drop_frames = 0; /* 每流计数(txdrop 为全局客户端级,不随流清) */ g_stall = 0; g_dev_bytes = 0; /* 测试钩子计数随新流归零 */ + /* 电平表复位 + 窗口目标(~100ms) */ + g_m_win = g_st.rate / 10; + if (!g_m_win) g_m_win = 1; + g_m_sumsq = 0; g_m_peak = 0; g_m_frames = 0; + g_lvl_rms_db10 = g_lvl_peak_db10 = METER_FLOOR_DB10; + g_lvl_seq++; /* 重建 ring + 标记重配 */ ring_rebuild(); _audio_mark_reconfig_needed(); @@ -1105,7 +1198,7 @@ static void dispatch(struct client *c, uint16_t type, uint32_t req_id, const uin snprintf(txt, sizeof txt, "active=%d app=%s sid=%u rate=%u ch=%u fmt=%s " "written=%llu played=%llu underrun=%u dropped=%llu stall=%u txdrop=%u paused=%d " - "route_mode=%s port=%d vol=%d\n", + "route_mode=%s port=%d vol=%d rms=%d peak=%d lvlseq=%u\n", g_st.active ? 1 : 0, g_st.active ? g_st.app_id : "-", (unsigned)g_st.sid, (unsigned)g_st.rate, (unsigned)g_st.ch, @@ -1117,7 +1210,8 @@ static void dispatch(struct client *c, uint16_t type, uint32_t req_id, const uin (unsigned)g_stall, (unsigned)g_txdrop, g_paused, g_route_manual == -1 ? "auto" : "manual", (g_route_manual != -1 ? g_route_manual : rb), - vol); + vol, + g_lvl_rms_db10, g_lvl_peak_db10, (unsigned)g_lvl_seq); send_frame(c->fd, MSG_AUDIO_STATE, req_id, txt, (uint32_t)strlen(txt) + 1); break; } @@ -1310,6 +1404,95 @@ static void *route_thread(void *arg) return NULL; } +/* ---- 全面监控日志(§8 扩展:流活跃时每 1s 一行;主线程节拍驱动,不碰播放线程) ---- */ +/* deci-dB → "-13.1" 文本(静音地板显 "-inf") */ +static void fmt_db10(char *out, size_t n, int v) +{ + if (v <= METER_FLOOR_DB10) { snprintf(out, n, "-inf"); return; } + int neg = v < 0; + int a = neg ? -v : v; + snprintf(out, n, "%s%d.%d", neg ? "-" : "", a / 10, a % 10); +} + +static void mon_close(void) +{ + if (g_monfd >= 0) { close(g_monfd); g_monfd = -1; } +} + +/* 主线程 1s 节拍调用。流活跃 → 懒打开 TF 卡 MeowOS/logs/audio_monitor_.log + * (无卡降级 /tmp)并写一行(时间戳+app+参数+rms/peak dBFS+帧计数+underrun/stall/ + * dropped/txdrop+路由口+FPGA 状态+音量%+hw 消费速率帧/s);流停止即停写关文件。 + * 快照先取后写:文件 IO 不持 g_mu。 */ +static void mon_tick(void) +{ + pthread_mutex_lock(&g_mu); + int active = g_st.active; + char app[MEOW_APP_ID_MAX]; snprintf(app, sizeof app, "%s", g_st.app_id); + uint32_t sid = g_st.sid, rate = g_st.rate; + unsigned ch = g_st.ch, fmt = g_st.fmt; + uint64_t wr = g_written_frames, pl = g_played_frames, dr = g_drop_frames; + unsigned ur = g_underrun, st = g_stall, tx = g_txdrop; + int paused = g_paused, vol = g_vol_pct, rmanual = g_route_manual, rcur = g_route_cur; + int rms = g_lvl_rms_db10, pk = g_lvl_peak_db10; + pthread_mutex_unlock(&g_mu); + + if (!active) { /* 流停止即停写 */ + if (g_monfd >= 0) { + mon_close(); + logmsg("monitor log closed (stream ended)"); + } + g_mon_prev_ms = 0; + return; + } + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + int64_t now_ms = (int64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000; + if (g_monfd < 0) { /* 懒打开(流开始才建文件) */ + char ts[64]; + get_bootts(ts, sizeof ts); + char path[368]; + if (access(LOG_DIR_CARD, W_OK) == 0) + snprintf(path, sizeof path, "%s/audio_monitor_%s.log", LOG_DIR_CARD, ts); + else + snprintf(path, sizeof path, "/tmp/audio_monitor_%s.log", ts); + g_monfd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (g_monfd < 0) return; /* 打不开就静默降级(下拍再试) */ + logmsg("monitor log -> %s", path); + const char *hdr = "# meow_audiod monitor: time | app sid rate/ch/fmt | rms/peak dBFS | written played underrun stall dropped txdrop | route fpga vol hwrate\n"; + if (write(g_monfd, hdr, strlen(hdr)) < 0) { } + g_mon_prev_played = pl; + g_mon_prev_ms = now_ms; + } + int64_t dt = now_ms - g_mon_prev_ms; + uint64_t hwrate = (dt > 0 && pl >= g_mon_prev_played) + ? (pl - g_mon_prev_played) * 1000 / (uint64_t)dt : 0; + g_mon_prev_played = pl; + g_mon_prev_ms = now_ms; + struct tm tm; + time_t now = time(NULL); + localtime_r(&now, &tm); + char tbuf[32]; + strftime(tbuf, sizeof tbuf, "%Y-%m-%d %H:%M:%S", &tm); + char rmsb[16], pkb[16]; + fmt_db10(rmsb, sizeof rmsb, rms); + fmt_db10(pkb, sizeof pkb, pk); + int port = (rmanual != -1) ? rmanual : rcur; + char line[512]; + int n = snprintf(line, sizeof line, + "%s app=%s sid=%u %uHz/%uch/%s rms=%sdBFS peak=%sdBFS " + "written=%llu played=%llu underrun=%u stall=%u dropped=%llu txdrop=%u " + "route=%d(%s) fpga=%s vol=%d%% hwrate=%lluf/s%s\n", + tbuf, app, (unsigned)sid, (unsigned)rate, ch, fmt_name((int)fmt), + rmsb, pkb, + (unsigned long long)wr, (unsigned long long)pl, + ur, st, (unsigned long long)dr, tx, + port, port_name(port), + rcur >= 0 ? "armed" : "unknown", + vol, (unsigned long long)hwrate, + paused ? " paused" : ""); + if (n > 0 && write(g_monfd, line, (size_t)n) < 0) { } +} + static void on_sig(int sig) { (void)sig; @@ -1406,8 +1589,10 @@ int main(int argc, char **argv) logmsg("WATCHDOG: playback thread silent %ds (active=%d written=%llu played=%llu, device wedged?)", play_silent, act, (unsigned long long)wr, (unsigned long long)plf); } + mon_tick(); /* 流活跃才写监控日志(无流零开销) */ } logmsg("meow_audiod shutdown"); + mon_close(); pthread_join(ti, NULL); pthread_join(tp, NULL); pthread_join(tr, NULL); diff --git a/patch_v2/meow_audiod/meow_audiod_host b/patch_v2/meow_audiod/meow_audiod_host index 47164c8..1e037d8 100755 Binary files a/patch_v2/meow_audiod/meow_audiod_host and b/patch_v2/meow_audiod/meow_audiod_host differ diff --git a/patch_v2/meow_audiod/test_audiod_host b/patch_v2/meow_audiod/test_audiod_host index 3975099..500b513 100755 Binary files a/patch_v2/meow_audiod/test_audiod_host and b/patch_v2/meow_audiod/test_audiod_host differ diff --git a/patch_v2/meow_audiod/test_audiod_host.c b/patch_v2/meow_audiod/test_audiod_host.c index 1263e7f..171da57 100644 --- a/patch_v2/meow_audiod/test_audiod_host.c +++ b/patch_v2/meow_audiod/test_audiod_host.c @@ -479,6 +479,99 @@ int main(void) close(z7.fd); stop_audiod(); + /* ---- T8:数字域电平表 + 全面监控日志(M2.97 DAC 实时监控)。 + * 已知幅度正弦(amp=0.3, 440Hz):理论 RMS=0.3/√2 → -13.5dBFS,peak=0.3 → -10.5dBFS。 + * audiod 电平表为整数近似(octave 线性插值,误差 ~0.5dB)→ 容差 ±2.5dB。 + * 监控日志:流活跃期主线程 1s 节拍写 /tmp/audio_monitor_.log(宿主无卡降级)。 ---- */ + printf("[T8 level meter + monitor log]\n"); + unlink(TSOCK); unlink(TWAV); + g_audiod_pid = start_audiod(); /* file sink */ + usleep(400000); + struct cli m8; + check(cli_connect(&m8) == 0, "connect M8"); + m_audio_open_ack_t ackM8; + check(do_open(&m8, 44100, 2, AUDIO_FMT_S16, &ackM8) == 0 && ackM8.err == MEOW_OK, "open M8"); + /* 喂 ~270ms 的 440Hz/0.3 幅度正弦(>2 个 100ms 窗口) */ + for (int i = 0; i < 12; i++) + do_write(&m8, pcm, (uint32_t)gen_sine(pcm, sizeof pcm, AUDIO_FMT_S16, 1000, 440, 44100)); + char q8[1024]; + check(do_query(&m8, q8, sizeof q8) == 0 && strstr(q8, "rms=") && strstr(q8, "peak="), + "query exposes rms/peak keys"); + int rms8 = -990, pk8 = -990; + for (int i = 0; i < 20; i++) { /* 最多 2s 等首个窗口结算(播放排空竞态护栏) */ + do_query(&m8, q8, sizeof q8); + rms8 = atoi(strstr(q8, "rms=") + 4); + pk8 = atoi(strstr(q8, "peak=") + 5); + if (rms8 > -990) break; + usleep(100000); + } + printf(" sine 0.3: rms=%d peak=%d (expect rms≈-135 peak≈-105 deci-dB)\n", rms8, pk8); + check(rms8 >= -160 && rms8 <= -110, "sine RMS ≈ -13.5dBFS (±2.5dB)"); + check(pk8 >= -130 && pk8 <= -80, "sine peak ≈ -10.5dBFS (±2.5dB)"); + /* 静音 → 地板(轮询等窗口结算:窗口按块粒度对齐,消除播放排空时序竞态) */ + { + uint8_t zeros[4096]; + memset(zeros, 0, sizeof zeros); + for (int i = 0; i < 8; i++) do_write(&m8, zeros, sizeof zeros); /* ~186ms */ + int floor_ok = 0; + for (int i = 0; i < 20; i++) { /* 最多 2s 等纯零窗口结算 */ + usleep(100000); + do_query(&m8, q8, sizeof q8); + rms8 = atoi(strstr(q8, "rms=") + 4); + pk8 = atoi(strstr(q8, "peak=") + 5); + if (rms8 <= -990 && pk8 <= -990) { floor_ok = 1; break; } + } + printf(" silence: rms=%d peak=%d\n", rms8, pk8); + check(floor_ok, "silence -> -inf floor"); + } + /* monitor CLI 冒烟(流仍活跃:输出含电平字段+电平条) */ + { + unlink("/tmp/meow_mon_cli.out"); + pid_t mp = fork(); + if (mp == 0) { + setenv("MEOW_AUDIO_SOCK", TSOCK, 1); + int fd = open("/tmp/meow_mon_cli.out", O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) { dup2(fd, 1); close(fd); } + execl("./meow_audio_host", "meow_audio_host", "monitor", "2", (char *)0); + _exit(127); + } + int mst = 0; + waitpid(mp, &mst, 0); + char mb[4096]; + int ml = 0; + int fd = open("/tmp/meow_mon_cli.out", O_RDONLY); + if (fd >= 0) { ml = (int)read(fd, mb, sizeof mb - 1); close(fd); if (ml > 0) mb[ml] = 0; } + check(ml > 0 && strstr(mb, "rms=") && strstr(mb, "LIVE") && strchr(mb, '['), + "monitor CLI prints live level bar"); + } + /* 监控日志文件:活跃期应已写入(前面耗时已超 2 个 1s 节拍) */ + char bootts[64] = ""; + { + int fd = open("/tmp/meow_bootts", O_RDONLY); + if (fd >= 0) { ssize_t r = read(fd, bootts, 63); close(fd); if (r > 0) bootts[r] = 0; } + } + char monpath[160]; + snprintf(monpath, sizeof monpath, "/tmp/audio_monitor_%s.log", bootts); + char monbuf[4096]; + int monlen = 0; + { + int fd = open(monpath, O_RDONLY); + if (fd >= 0) { monlen = (int)read(fd, monbuf, sizeof monbuf - 1); close(fd); if (monlen > 0) monbuf[monlen] = 0; } + } + check(monlen > 0 && strstr(monbuf, "app=test") && strstr(monbuf, "rms=") && + strstr(monbuf, "hwrate=") && strstr(monbuf, "vol="), + "monitor log file written while stream active"); + /* 流停止即停写 */ + do_ctrl(&m8, MSG_AUDIO_STOP, ackM8.stream_id); + usleep(1400000); + off_t msz1 = -1, msz2 = -1; + { int fd = open(monpath, O_RDONLY); if (fd >= 0) { msz1 = lseek(fd, 0, SEEK_END); close(fd); } } + usleep(1400000); + { int fd = open(monpath, O_RDONLY); if (fd >= 0) { msz2 = lseek(fd, 0, SEEK_END); close(fd); } } + check(msz1 >= 0 && msz1 == msz2, "monitor log stops when stream stops"); + close(m8.fd); + stop_audiod(); + printf("=== SUMMARY: pass=%d fail=%d ===\n", g_pass, g_fail); return g_fail ? 1 : 0; } diff --git a/scripts/build/fw_content.conf b/scripts/build/fw_content.conf index 4d482c3..e2c8503 100644 --- a/scripts/build/fw_content.conf +++ b/scripts/build/fw_content.conf @@ -38,8 +38,8 @@ patch_v2/meow_svc_power/meow_svc_power|/usr/bin/meow_svc_power|39ae3aa255695a97f patch_v2/meow_verify/meow_verify|/usr/bin/meow_verify|6c2e8ed24aebe5caf41e7aed391312d5|II期验签 2B(多公钥+v2签名块 agent-221) patch_v2/meow_crash/meow_crash|/usr/bin/meow_crash|62781084445e82351bcadbdadbe4c7fc|系统级崩溃捕获 (agent-187) patch_v2/meow_svc_perf/meow_svc_perf|/usr/bin/meow_svc_perf|7ea196de9d59ab8cbd1e26edb7762772|性能专项服务 M2.75(net_json 补 network 闭合括号,修 /api/system JSON 解析失败) -patch_v2/meow_audiod/meow_audiod|/usr/bin/meow_audiod|772cc3d836c06b84efb67edde894cc3e|独占音频守护 M2.96(+真静音写255 修炸耳安全事故) -patch_v2/meow_audiod/meow_audio|/usr/bin/meow_audio|c23b96490842f54324b855fd9c2467a5|audiod 用户侧 CLI(status/tone/route/vol;connect/write 全有界 2s 超时) +patch_v2/meow_audiod/meow_audiod|/usr/bin/meow_audiod|ab193a2bd094f2c9f975f0f28ba09cba|独占音频守护 M2.97(+DAC 数字域电平表 rms/peak + 1s 监控日志 audio_monitor_.log) +patch_v2/meow_audiod/meow_audio|/usr/bin/meow_audio|891ab26475ef9e9931bf9147d265dce4|audiod 用户侧 CLI M2.97(+monitor 实时电平表/文本电平条;status 补 rms/peak) # ---- 开机 init 脚本(etc/init.d,数字决定启动顺序,按文件名自然序)---- patch_v2/meow_sysui/S70meow_sysui|/etc/init.d/S70meow_sysui|e435affcb56499e5229728c6ae2524b9|sysui 自启 M2.85(launcher.conf enabled=1+迁移)