/* audio.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/ /* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/ #include "meow_launcher_internal.h" static int amixer_vol(int numid) { char cmd[96]; snprintf(cmd, sizeof cmd, "amixer -c 0 cget numid=%d 2>/dev/null", numid); FILE *p = popen(cmd, "r"); if (!p) return -1; char buf[128]; int v = -1; while (fgets(buf, sizeof buf, p)) { /* ": values=N" line — the other "values=1" on the type line is the * element's value COUNT, not the actual volume */ char *s = strstr(buf, ": values="); if (s) { v = atoi(s + 9); break; } } pclose(p); return v; } static void amixer_set(int numid, int v) { if (v < 0) return; char cmd[96]; snprintf(cmd, sizeof cmd, "amixer -c 0 cset numid=%d %d >/dev/null 2>&1", numid, v); system(cmd); } struct vol_snap vol_snapshot(void) { struct vol_snap s = { amixer_vol(1), amixer_vol(2) }; return s; } void vol_restore(struct vol_snap s) { logmsg("volume restore -> L=%d R=%d", s.l, s.r); amixer_set(1, s.l); amixer_set(2, s.r); } int settings_volume = MEOW_VOL_DEFAULT; /* 当前音量 0-100(= 写入 DAC 的映射值) */ int settings_volume_max = MEOW_VOL_MAX_DEFAULT; /* 最大音量上限 clamp */ int settings_volume_step = MEOW_VOL_STEP_DEFAULT; /* 跳步 */ /* M2.17/M2.16:把 meow_settings 当前值同步到本模块的缓存全局。读取方 * (DAC/状态栏/息屏/刷新率调度)统一从缓存取,避免散读 backend。 */ void settings_sync_cache_from_backend(void) { int v; if (meow_settings_get_int("audio.volume", &v) == 0) settings_volume = v; if (meow_settings_get_int("audio.volume_max", &v) == 0) settings_volume_max = v; if (meow_settings_get_int("audio.volume_step", &v) == 0) settings_volume_step = v; if (meow_settings_get_enum("display.refresh_rate", &v) == 0) /* names "60,30,15" */ refresh_rate_cfg = (v == 1) ? 30 : (v == 2) ? 15 : 60; if (meow_settings_get_bool("display.refresh_always_max", &v) == 0) refresh_always_max = v; if (meow_settings_get_int("display.screen_off_timeout", &v) == 0) screen_off_timeout = v; if (meow_settings_get_enum("display.screen_off_mode", &v) == 0) screen_off_mode = v; if (meow_settings_get_int("power.longpress_menu_ms", &v) == 0) power_longpress_menu_ms = v; if (meow_settings_get_int("power.forceoff_ms", &v) == 0) power_forceoff_ms = v; } /* meow_settings 变更回调:set_*(如音量键)后同步缓存全局,保证既有引用 * settings_volume 等变量的代码与 backend 单点一致(SYSTEM_SETTINGS §3)。 */ void settings_sync_cache_listener(const char *key) { if (!strcmp(key, "audio.volume")) { int v; if (meow_settings_get_int("audio.volume", &v) == 0) settings_volume = v; } else if (!strcmp(key, "audio.volume_max")) { int v; if (meow_settings_get_int("audio.volume_max", &v) == 0) settings_volume_max = v; } else if (!strcmp(key, "audio.volume_step")) { int v; if (meow_settings_get_int("audio.volume_step", &v) == 0) settings_volume_step = v; } else if (!strcmp(key, "display.refresh_rate")) { int v; if (meow_settings_get_enum("display.refresh_rate", &v) == 0) refresh_rate_cfg = (v == 1) ? 30 : (v == 2) ? 15 : 60; } else if (!strcmp(key, "display.refresh_always_max")) { int v; if (meow_settings_get_bool("display.refresh_always_max", &v) == 0) refresh_always_max = v; } else if (!strcmp(key, "display.screen_off_timeout")) { int v; if (meow_settings_get_int("display.screen_off_timeout", &v) == 0) screen_off_timeout = v; } else if (!strcmp(key, "display.screen_off_mode")) { int v; if (meow_settings_get_enum("display.screen_off_mode", &v) == 0) screen_off_mode = v; } else if (!strcmp(key, "power.longpress_menu_ms")) { int v; if (meow_settings_get_int("power.longpress_menu_ms", &v) == 0) power_longpress_menu_ms = v; } else if (!strcmp(key, "power.forceoff_ms")) { int v; if (meow_settings_get_int("power.forceoff_ms", &v) == 0) power_forceoff_ms = v; } } /* 启动加载(M2.11/M2.24):统一由 meow_settings 后端读 settings.conf; * 此处仅注册监听(一次性)+ 把 backend 当前值同步到缓存全局。重载(应用期 * watchdog 可能改音量)等价于 meow_settings_init() + 手动同步。 */ int settings_cache_registered = 0; /* M2.24:监听只注册一次 */ /* ---- MeowOS 真实 DAC 音量(CS43198,ANALYSIS §18.39)---- * HibyOS 播放器经 ioctl 直写 /dev/snd/controlC0 的 "Left/Right Playback * Volume"(numid 1/2,0-255)→ codec 驱动 cs43198_vol_*_put → I2C 直写 CS43198 * 寄存器(0x9000-0x9002/0x7000-0x7001,双片 L/R)。我们复刻同路径(amixer * cset = SNDRV_CTL_IOCTL_ELEM_WRITE),0-100 → 0-255 映射直写两声道。 * 读回不可用(驱动 get 为 dummy 恒 0;/dev/i2c-3 被驱动占用),故系统音量以 * settings_volume(写入 DAC 的映射值)为权威跟踪值。 */ void meow_dac_vol_write(int pct) { int dac = pct * 255 / 100; if (dac < 0) dac = 0; if (dac > 255) dac = 255; char cmd[160]; snprintf(cmd, sizeof cmd, "amixer -c 0 cset numid=1 %d >/dev/null 2>&1; amixer -c 0 cset numid=2 %d >/dev/null 2>&1", dac, dac); system(cmd); logmsg("DAC vol -> L/R=%d (pct %d)", dac, pct); } static unsigned long long vol_dac_last_ms = 0; void meow_dac_vol_write_throttled(void) { unsigned long long now = now_ms(); if (now - vol_dac_last_ms < VOL_DRAG_DAC_MS) return; /* 节流窗口内跳过,等抬手统一写 */ vol_dac_last_ms = now; meow_dac_vol_write(settings_volume); } /* 抬手/点击提交连 save 时调用:无条件直写一次真实 DAC(绕过节流,保证最终音量落 DAC/落盘)。 */ void meow_dac_vol_flush(void) { vol_dac_last_ms = now_ms(); meow_dac_vol_write(settings_volume); } /* clamp 集中:0..MEOW_VOL_MAX,再 clamp 到上限 settings_volume_max。 */ int vol_clamp(int v) { if (v < 0) v = 0; if (v > MEOW_VOL_MAX) v = MEOW_VOL_MAX; if (v > settings_volume_max) v = settings_volume_max; return v; } int settings_volume_get(void) { return settings_volume; } int vol_ramp_dir = 0; /* +1 上 / -1 下 / 0 停 */ static int vol_ramp_target = -1; /* 目标值;-1=无 */ static unsigned long long vol_ramp_next_at = 0; static unsigned long long vol_ramp_press_at = 0; /* M2.18 Bug 4:本方向键按下时刻(长按判据) */ static void vol_ramp_begin(int delta) { int target = vol_clamp(settings_volume + delta); if (target == settings_volume) return; vol_ramp_dir = (target > settings_volume) ? 1 : -1; vol_ramp_target = target; vol_ramp_press_at = now_ms(); /* M2.18 Bug 4:记录该方向键按下时刻,供 CLICK_MS 长按判定 */ vol_ramp_next_at = 0; /* 立即迈第一步,给即时反馈 */ } /* 每主循环 tick 调;key_held=该方向按键是否仍按住(长按续爬)。返回 1=仍在渐变。 * M2.18 Bug 4 语义:单击(