feat(audiod): DAC 实时监控 — 数字域电平表(100ms窗口整数RMS/peak,deci-dBFS)+1s监控日志(TF audio_monitor_<bootts>.log,流停即停)+CLI monitor 电平条;STATE 补 rms/peak/lvlseq 键(ABI兼容)。宿主47/47,qemu+真机0.7.91 tone验证(1kHz 0.3幅:rms=-13.9 peak=-10.8dBFS,hwrate~44.1k健康)。audiod ab193a2b meow_audio 891ab264
This commit is contained in:
1 parent
70aec677ce
commit
9a4ea10c64
9 files changed
+377
-5
No files matched your search
Binary file not shown.
@@ -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 <math.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
@@ -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;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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_<bootts>.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);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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_<bootts>.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;
|
||||
}
|
||||
@@ -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_<bootts>.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+迁移)
|
||||
|
||||
Reference in new issue
Block a user