From: snickersun Date: Mon, 25 May 2026 08:20:32 +0000 (+0800) Subject: 修正最新修改到k8/default_config.py X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=23bc587e29b038f634209e09396ec7df38118f62;p=mirrors%2FASR1603_G5.git 修正最新修改到k8/default_config.py --- diff --git a/.cursor/rules/002-design.mdc b/.cursor/rules/002-design.mdc index 0d59d0d1..9e9e79f6 100644 --- a/.cursor/rules/002-design.mdc +++ b/.cursor/rules/002-design.mdc @@ -6,7 +6,26 @@ globs: 2.sdk/project/7PIN_1_5/**/vc_*.py,2.sdk/project/7PIN_1_5/**/protocol.py # VC 录音上传与播放 — 设计动作(7PIN_1_5) -**权威设计稿**:`1.doc/K8_diaozhui/design/录音上传与播放-演示文稿.md`。实现:`vc_ux` / `vc_key_router` / `vc_media_arb`。 +**权威设计稿**:`1.doc/K8_diaozhui/design/录音上传与播放-演示文稿.md` +**量产配置**:`products/configs/k8/default_config.py`(`product_k8.ini` → `DEV_TYPE=k8`) +**实现骨架**:`vc_ux` / `vc_key_router` / `vc_media_arb` + +## k8 配置摘要(与代码绑定) + +| 配置项 | k8 值 | 用途 | +|--------|-------|------| +| `RECORD_START_TONE_FILE` | `U:/static/start.mp3` | **开始录音** local 提醒(非按键滴声) | +| `RECORD_START_TONE_GAP_MS` | 180 | 长对话:start 播完再开上行 | +| `VAD_SILENCE_DEFAULT` | 1500 | 长对话 VAD 断句 | +| `PTT_HINT` | 长按说话话术 | PTT **idle 短按** → notify(`_ptt_play_idle_hint`) | +| `PTT_MIN_RECORD_MS` | 400 | PTT 长按过短判定 | +| `PTT_RECORD_TOO_SHORT_TTS` | 见配置 | PTT **长按过短** → notify(`_ptt_abort_short_record`) | +| `POWEROFF_CMD_CLOSE_ONLY` | False | 语音「关机」可走真断电 | +| `AI_ALARM_ENABLED` | False | 无闹钟 | + +**不配置**:`KEY_PRESS_TONE_FILE`(`key_prompt.mp3` 非 K8 需求;按键本身无确认音)。 + +**PTT 两条 notify 勿混**:`PTT_HINT` = 没长按、教操作;`PTT_RECORD_TOO_SHORT_TTS` = 已长按但松太快、取消录音。 ## 术语 ↔ 代码 @@ -28,12 +47,12 @@ globs: 2.sdk/project/7PIN_1_5/**/vc_*.py,2.sdk/project/7PIN_1_5/**/protocol.py | UX | 长对话单击 | PTT 单击 | PTT 长按 | |----|------------|----------|----------| -| idle | 开录;**仅 Power 短按后再开用新 sid**,否则旧 sid | 提醒长按;idle | press 开录 / release 结束 | +| idle | 开录 + `start.mp3`;**仅 Power 短按后再开用新 sid**,否则旧 sid | notify `PTT_HINT`;idle | press 开录 + `start.mp3` / release 结束 | | recording | 停录立即重开,旧 sid | (无) | 同单击 | | thinking | 打断后重开,旧 sid | 停对话→idle | 停对话后开录 | | tts | 停播后重开,旧 sid | 停播→idle | 停播后开录 | -**PTT 异常**:录音时长不足 → **notify TTS** + idle(`PTT_RECORD_TOO_SHORT_TTS`)。 +**PTT 异常**:录音 < `PTT_MIN_RECORD_MS` → **notify** `PTT_RECORD_TOO_SHORT_TTS` + cancel + idle。 ## 按键 — Power 键 @@ -48,5 +67,6 @@ globs: 2.sdk/project/7PIN_1_5/**/vc_*.py,2.sdk/project/7PIN_1_5/**/protocol.py ## 代码锚点 - `KeyRouter.dispatch_press` / `handle_ptt_release` / `handle_power_short` -- `_vc_restart_record_same_dialog`、`_ptt_stop_dialog_to_idle`、`_ptt_abort_short_record` +- `_vc_play_record_start_tone`、`_vc_restart_record_same_dialog` +- `_ptt_play_idle_hint`、`_ptt_abort_short_record`、`_ptt_stop_dialog_to_idle` - `Close()` 清 `_cur_dialog_id`(Power 后下次 AI 为新对话) diff --git a/.cursor/rules/project.mdc b/.cursor/rules/project.mdc index 2499777d..914588d8 100644 --- a/.cursor/rules/project.mdc +++ b/.cursor/rules/project.mdc @@ -6,6 +6,7 @@ alwaysApply: true - 你是高级嵌入式工程师,代码必须遵循最佳实践和最佳设计原则 - 本工程是micropython工程, - 2.sdk/Helios_SDK是编译路径,2.sdk/project/7PIN_1_5是项目路径 ,代码优先在项目路径里找,没有的通用代码才在编译路径里。 +- 2.sdk\project\7PIN_1_5\sdk\services\microPython\ports\quectel\core\products\configs\k8\default_config.py 是默认k8 的配置参数。 - 编译指令是 new_clean.bat k8 1 - 分析日志解决问题时,要考虑根因,不要只是靠简单判断条件进行兜底修复。 - 每次修改都要考虑副作用,不能拆东墙补西墙 \ No newline at end of file diff --git "a/1.doc/K8_diaozhui/design/VC\345\256\236\347\216\260\345\256\241\346\237\245\344\270\216\347\256\200\345\214\226\346\236\266\346\236\204.md" "b/1.doc/K8_diaozhui/design/VC\345\256\236\347\216\260\345\256\241\346\237\245\344\270\216\347\256\200\345\214\226\346\236\266\346\236\204.md" index 512f43b1..7f9c713f 100644 --- "a/1.doc/K8_diaozhui/design/VC\345\256\236\347\216\260\345\256\241\346\237\245\344\270\216\347\256\200\345\214\226\346\236\266\346\236\204.md" +++ "b/1.doc/K8_diaozhui/design/VC\345\256\236\347\216\260\345\256\241\346\237\245\344\270\216\347\256\200\345\214\226\346\236\266\346\236\204.md" @@ -1,54 +1,95 @@ -# VC 实现审查与简化架构(7PIN_1_5) - -> 权威:`录音上传与播放-演示文稿.md`(含 **Power 键**、**sid 规则**、**PTT 录音过短 notify**) - -## 1. 设计稿要点(2025 更新) - -### 媒体三车道 - -| 车道 | 抢占 | -|------|------| -| LLM PCM | notify/local defer | -| Notify TTS | 关 llm + local | -| Local MP3 | 关 llm + notify;录音中可并行 | - -### sid 规则 - -- **AI idle 开录**:默认 **旧 sid**(同 WS 会话续 `dialog_id`)。 -- **仅 Power 短按 Close 后**:清 `_cur_dialog_id`,下次 `AI_VC_start` 为 **新 sid**。 - -### Power 短按 - -各 UX 态均:**协议 cancel(按需)→ Close WS → idle/休眠 → 清 sid**。 - -### PTT 异常 - -长按松手且录音 < `PTT_MIN_RECORD_MS`:cancel、**play_tts notify**、idle。 - -## 2. 代码结构(三件套) - -| 模块 | 文件 | -|------|------| -| UX 四态 | `vc_ux.py` → `derive_ux_state` | -| 按键表 | `vc_key_router.py` → AI / Power / PTT release | -| 媒体仲裁 | `vc_media_arb.py` → `before_local` / `before_notify` / `before_llm_pcm` | - -`vc_mode_strategy` 仅转发 `KeyRouter`;`vc_present` 用 UX 映 LED。 - -## 3. 符合度(当前) - -| 项 | 状态 | -|----|------| -| UX + KeyRouter + MediaArbiter 骨架 | ✅ | -| 长对话 recording 重开 | ✅ | -| PTT idle/thinking/tts 短按 | ✅ | -| Power 短按按 UX cancel+Close | ✅ | -| PTT 录音过短 notify | ✅ | -| notify `play_tts` 仲裁入口 | ✅ | -| `vc_session_io` 仅 FSM actions | 🔲 后续 | - -## 4. 迁移剩余 - -- 收敛 `play_tts_cb` 断 WS:区分 takeover notify 与会话内 synthesis -- 录音灯挂 `VC_Speech_Start_now` 首帧 -- 删与 `InterruptContext` 重复的 `_ptt_*` latch +# VC 实现审查与简化架构(7PIN_1_5 / k8 量产) + +> 权威:`录音上传与播放-演示文稿.md`(含 **Power 键**、**sid 规则**、**PTT 录音过短 notify**) +> **配置唯一入口**:`products/configs/k8/default_config.py`(`product_k8.ini`) + +## 1. 设计稿要点 + +### 媒体三车道 + +| 车道 | 抢占 | +|------|------| +| LLM PCM | notify/local defer | +| Notify TTS | 关 llm + local | +| Local MP3 | 关 llm + notify;录音中可并行 | + +### sid 规则 + +- **AI idle 开录**:默认 **旧 sid**(同 WS 会话续 `dialog_id`)。 +- **仅 Power 短按 Close 后**:清 `_cur_dialog_id`,下次 `AI_VC_start` 为 **新 sid**。 + +### Power 短按 + +各 UX 态均:**协议 cancel(按需)→ Close WS → idle/休眠 → 清 sid**。 + +### PTT 两类 notify(均需保留) + +| 场景 | 用户行为 | 配置 | 代码 | +|------|----------|------|------| +| idle **短按** | 未长按,需教操作 | `PTT_HINT` | `_ptt_play_idle_hint` → `play_tts` | +| **长按过短** | 已开录但 < `PTT_MIN_RECORD_MS` 松手 | `PTT_RECORD_TOO_SHORT_TTS` | `KeyRouter.ptt_release_after_long_press` → `_ptt_abort_short_record` | + +### PTT 异常(过短) + +长按松手且录音 < `PTT_MIN_RECORD_MS`:cancel、**play_tts notify**、idle。 + +## 2. 提示音产品定义(K8,2025 澄清) + +| 类型 | 是否需要 | 实现 | +|------|----------|------| +| 开始录音提醒 | ✅ | `RECORD_START_TONE_FILE` → `U:/static/start.mp3` | +| 按键确认音 / `key_prompt.mp3` | ❌ | k8 已删除 `KEY_PRESS_TONE_*`;非「滴」 | +| PTT idle 教学 | ✅ notify | `PTT_HINT` | +| PTT 录音过短 | ✅ notify | `PTT_RECORD_TOO_SHORT_TTS` | +| think 提醒 | ✅ local | `vc_session_io` 硬编码 `think.mp3`(待配置化) | + +## 3. 代码结构(三件套) + +| 模块 | 文件 | +|------|------| +| UX 四态 | `vc_ux.py` → `derive_ux_state` | +| 按键表 | `vc_key_router.py` → AI / Power / PTT release | +| 媒体仲裁 | `vc_media_arb.py` → `before_local` / `before_notify` / `before_llm_pcm` | + +`vc_mode_strategy` 仅转发 `KeyRouter`;`vc_present` 用 UX 映 LED。 + +## 4. k8 配置 ↔ 设计 + +| 设计项 | k8 配置 | 代码 | +|--------|---------|------| +| Local 开录 start | `RECORD_START_TONE_FILE=start.mp3` | `_vc_play_record_start_tone` | +| Local 长对话短按「滴」 | **无**(非需求) | `_vc_play_key_press_tone` 配置缺省即 return | +| Notify PTT idle 提醒 | `PTT_HINT` | `_ptt_play_idle_hint` | +| Notify 录音过短 | `PTT_RECORD_TOO_SHORT_TTS` | `ptt_release_after_long_press` | +| Notify 开机/模式切换 | `POWER_ON_*` / `CMD_TOGGLE_*` | `play_tts` | +| VAD 断句 | `VAD_SILENCE_DEFAULT=1500` | `Start` payload | +| Power 短按清 sid | (行为) | `KeyRouter.handle_power_short` → `Close` | +| 语音关机 | `POWEROFF_CMD_CLOSE_ONLY=False` | 允许真断电分支 | + +## 5. 符合度(当前) + +| 项 | 状态 | +|----|------| +| UX + KeyRouter + MediaArbiter 骨架 | ✅ | +| 长对话 recording 重开 + start 开录音 | ✅ | +| PTT idle/thinking/tts 短按 | ✅ | +| Power 短按按 UX cancel+Close | ✅ | +| PTT 录音过短 notify | ✅ | +| notify `play_tts` 仲裁入口 | ✅ | +| 无 KEY_PRESS / key_prompt 短按音 | ✅ | +| `vc_session_io` 仅 FSM actions | 🔲 后续 | + +## 6. 迁移剩余 + +- 收敛 `play_tts_cb` 断 WS:区分 takeover notify 与会话内 synthesis +- 录音灯挂 `VC_Speech_Start_now` 首帧 +- 删与 `InterruptContext` 重复的 `_ptt_*` latch + +## 7. 实机检查 + +1. 编译:`new_clean.bat k8 1` 后确认 `core/default_config.py` 与 k8 一致 +2. 日志:`vad_silence=1500`(非 Flash 300) +3. 长对话短按续聊:无滴声,有 **start** 开录音 +4. PTT idle **短按**:播报 `PTT_HINT`(教长按) +5. PTT **长按 <400ms 松手**:播报 `PTT_RECORD_TOO_SHORT_TTS`(过短) +6. `usr/static/start.mp3` 存在 diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_config.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_config.py index aefa493d..d7723d39 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_config.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_config.py @@ -174,13 +174,10 @@ KWS_PTT_HINT = "请您按住圆形的按钮开始讲话,讲完松开即可 " PTT_HINT = "请您按住圆形的按钮开始讲话,讲完松开即可 " # ============================================================================= -# 10. 按键音 / 开录音 / PTT 时序(k8:开录仅以灯提示,不播专用开录音) +# 10. 开录音 / PTT 时序(k8 量产:仅 start.mp3 开录提醒,无按键确认音) # ============================================================================= -KEY_PRESS_TONE_FILE = None # AI 短按操作音;None=不播 -KEY_PRESS_TONE_DELAY_MS = 300 - -RECORD_START_TONE_FILE = None # 单对话长按开录提示音;None=仅蓝快闪 +RECORD_START_TONE_FILE = "U:/static/start.mp3" RECORD_START_TONE_GAP_MS = 180 PTT_RELEASE_END_SPEECH_DELAY_MS = 200 # 松手后发 audio.completed 前延迟 diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/7PIN_1_5/default_config.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/7PIN_1_5/default_config.py index e7c2e84e..d5d13c4f 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/7PIN_1_5/default_config.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/7PIN_1_5/default_config.py @@ -33,8 +33,7 @@ AI_LONG_PRESS_POWER_OFF_ENABLED = False # ---- 开机提示/语音参数默认值 ---- POWER_ON_TONE_ENABLED = True POWER_ON_TONE_DEFAULT = "你好,我来了" -# 服务端 VAD 断句静音时长(ms):句中短暂停顿勿小于 1200;可 storage vad_silence 覆盖 -VAD_SILENCE_DEFAULT = 1500 +VAD_SILENCE_DEFAULT = 1000 VC_TTS_VOICE_TYPE_DEFAULT = None VC_TTS_VOICE_KEY_DEFAULT = None @@ -55,10 +54,8 @@ DIALOG_BOT_OPTION = None DIALOG_PROVIDER_DEFAULT = None KEYWORDS_FROM_VOICE_TYPE = False -# ---- 按键音/提示音策略 ---- -KEY_PRESS_TONE_FILE = "U:/static/key_prompt.mp3" # REQ-AI-01:短按操作确认「滴」 -KEY_PRESS_TONE_DELAY_MS = 300 -RECORD_START_TONE_FILE = "U:/static/start.mp3" # 开录/打断续聊提示音 +# ---- 提示音(K8 量产见 products/configs/k8/default_config.py)---- +RECORD_START_TONE_FILE = "U:/static/start.mp3" RECORD_START_TONE_GAP_MS = 180 # VC 空闲/超时(ai_VC._1S_timer_cb_);长对话无下行超时 VC_IDLE_AUTO_CLOSE_SEC = 20 @@ -67,6 +64,4 @@ VC_IDLE_AUTO_CLOSE_SEC_PTT = 0 VC_IDLE_DEEP_SLEEP_SEC = 60 VC_IDLE_DEEP_SLEEP_ENABLE = 1 PTT_HINT = "请您按住圆形的按钮开始讲话,讲完松开即可 " -PTT_MIN_RECORD_MS = 400 -PTT_RECORD_TOO_SHORT_TTS = "录音时间太短,请按住圆形按钮说话" diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8/default_config.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8/default_config.py index aefa493d..71ac4da2 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8/default_config.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8/default_config.py @@ -114,6 +114,7 @@ GNSS_ENABLED = False # 无 GNSS 硬件时 False,避免 import quecgn UART_SUPPORTED = False BT_OTA_ENABLED = False UPLOAD_START_TONE_ENABLED = False # 开始上传语音时是否播成功提示音 +AI_ALARM_ENABLED = False # K8 吊坠无闹钟/日程硬件 # ============================================================================= # 6. 按键逻辑(AI_dev / btn_device) @@ -139,7 +140,8 @@ VC_IDLE_AUTO_CLOSE_SEC_PTT = 0 # 单对话预留,当前与 idle 统一处理 VC_IDLE_DEEP_SLEEP_SEC = 60 VC_IDLE_DEEP_SLEEP_ENABLE = 1 -VAD_SILENCE_DEFAULT = 300 # VAD 静音阈值(ms) +# 服务端断句静音(ms);句中短暂停顿建议 ≥1200;可被 storage vad_silence 覆盖 +VAD_SILENCE_DEFAULT = 1500 # ============================================================================= # 8. 语音对话 / TTS / 智能体 @@ -174,15 +176,17 @@ KWS_PTT_HINT = "请您按住圆形的按钮开始讲话,讲完松开即可 " PTT_HINT = "请您按住圆形的按钮开始讲话,讲完松开即可 " # ============================================================================= -# 10. 按键音 / 开录音 / PTT 时序(k8:开录仅以灯提示,不播专用开录音) +# 10. 开录音 / PTT 时序(对齐「录音上传与播放」设计稿) # ============================================================================= +# 无按键确认音:不按 key_prompt;仅「开始录音」播 start.mp3(非滴声) +# PTT idle 短按:notify 话术 PTT_HINT;长按开录:start.mp3 -KEY_PRESS_TONE_FILE = None # AI 短按操作音;None=不播 -KEY_PRESS_TONE_DELAY_MS = 300 - -RECORD_START_TONE_FILE = None # 单对话长按开录提示音;None=仅蓝快闪 +RECORD_START_TONE_FILE = "U:/static/start.mp3" # 开录/打断续聊提醒(唯一本地按键相关短音) RECORD_START_TONE_GAP_MS = 180 +PTT_MIN_RECORD_MS = 400 +PTT_RECORD_TOO_SHORT_TTS = "录音时间太短,请按住圆形按钮说话" # notify + PTT_RELEASE_END_SPEECH_DELAY_MS = 200 # 松手后发 audio.completed 前延迟 PTT_CANCEL_WAIT_MS = 2500 # 打断后等 canceled 最长时间 PTT_POST_MUTE_MS = 30 # 本地硬静音后 codec 收尾 diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8s/default_config.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8s/default_config.py index 2567a6e2..73ca356a 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8s/default_config.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/configs/k8s/default_config.py @@ -177,12 +177,9 @@ KWS_PTT_HINT = "请您按住电源键开始讲话,讲完松开即可 " PTT_HINT = "请您按住电源键开始讲话,讲完松开即可 " # ============================================================================= -# 10. 按键音 / 开录音 / PTT 时序(k8s:开录仅以灯提示,不播专用开录音) +# 10. 开录音 / PTT 时序(k8s:无按键确认音;开录见 RECORD_START_*) # ============================================================================= -KEY_PRESS_TONE_FILE = None # AI 短按操作音;None=不播 -KEY_PRESS_TONE_DELAY_MS = 300 - RECORD_START_TONE_FILE = None # 单对话长按开录提示音;None=仅蓝快闪 RECORD_START_TONE_GAP_MS = 180 diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_key_router.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_key_router.py index d9436cb1..98e08638 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_key_router.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_key_router.py @@ -1,169 +1,170 @@ -# -*- coding: utf-8 -*- -"""按键表驱动:mode × UX × 事件 → 既有 vc_session 方法。""" -try: - import EventMesh - import constants - import utime -except Exception: - EventMesh = None - constants = None - utime = None - -from vc_ux import derive_ux_state, UX_IDLE, UX_RECORDING, UX_THINKING, UX_TTS, ux_name - -try: - import vc_config -except Exception: - vc_config = None - - -class KeyRouter(object): - @staticmethod - def dispatch_press(vc): - try: - if int(getattr(vc, "VC_mode", 0)) == 1: - return KeyRouter._ptt_press(vc) - return KeyRouter._long_ai_press(vc) - except Exception as e: - try: - import usys - usys.print_exception(e) - except Exception: - pass - return False - - @staticmethod - def handle_ptt_release(vc): - if int(getattr(vc, "VC_mode", 0)) != 1: - return False - if getattr(vc, "AI_key_long_press_flag", 0) == 1: - return False - ux = derive_ux_state(vc) - try: - print("[KEY][router] ptt_release ux={}".format(ux_name(ux))) - except Exception: - pass - if ux == UX_TTS: - try: - if vc._ptt_is_active_tts_playback(): - vc._ptt_stop_tts_on_short_press() - return True - except Exception: - pass - if ux == UX_THINKING: - try: - vc._ptt_stop_dialog_to_idle() - return True - except Exception: - pass - if ux == UX_IDLE: - try: - vc.key_prompt() - return True - except Exception: - pass - return False - - @staticmethod - def handle_power_short(vc): - """Power 短按:cancel → Close → 清 sid(设计稿幻灯片 6)。""" - ux = derive_ux_state(vc) - try: - print("[KEY][router] power_short ux={}".format(ux_name(ux))) - except Exception: - pass - try: - vc._vc_send_protocol_cancel("power_short/ux={}".format(ux_name(ux))) - except Exception: - pass - try: - vc._vc_close_reason = "power_short" - except Exception: - pass - try: - if EventMesh: - EventMesh.publish("publish_AI_btnInfo", {"func": vc.Close}) - except Exception: - pass - try: - if EventMesh and constants: - EventMesh.publish(constants.EVENT.SYSTEM_UP) - except Exception: - pass - return True - - @staticmethod - def _long_ai_press(vc): - ux = derive_ux_state(vc) - try: - print("[KEY][router] ai_press ux={} {}".format( - ux_name(ux), getattr(vc, "_vc_fsm_log_snip", lambda: "")())) - except Exception: - pass - if ux == UX_RECORDING: - try: - return vc._vc_restart_record_same_dialog() - except Exception: - pass - if ux in (UX_THINKING, UX_TTS): - try: - vc._long_dialogue_interrupt_on_short_press() - return True - except Exception: - pass - if vc._ai_key_press_long_dialogue_session(): - return True - if vc._ai_key_press_long_dialogue_wake(): - if EventMesh and constants: - EventMesh.publish(constants.EVENT.SYSTEM_UP) - return True - return KeyRouter._long_fallback_start(vc) - - @staticmethod - def _long_fallback_start(vc): - try: - vc.Close_flag = 0 - vc._skip_started_uplink_once = 0 - vc._vc_long_dialogue_key_arm_session() - vc._long_dialogue_publish_listening("key.press/fallback") - vc.get_config_timer_count = 0 - vc.key_state = 1 - vc.AI_TTS_str = "" - vc.AI_ASR_str = "" - vc.AI_ASR_str_tmp = "" - EventMesh.publish("publish_AI_btnInfo", {"func": vc.AI_VC_start}) - vc.AI_key_long_press_flag = 0 - return True - except Exception: - return False - - @staticmethod - def _ptt_press(vc): - return True - - @staticmethod - def ptt_release_after_long_press(vc): - """PTT 长按松手:录音过短 → notify TTS + idle。""" - if getattr(vc, "VC_record_state_flag", 0) == 0: - return False - min_ms = 400 - tts = "录音时间太短,请按住圆形按钮说话" - try: - if vc_config: - min_ms = int(vc_config.get_int("PTT_MIN_RECORD_MS", min_ms)) - tts = vc_config.get_attr("PTT_RECORD_TOO_SHORT_TTS", tts) or tts - except Exception: - pass - t_open = int(getattr(vc, "_ptt_record_open_ms", 0) or 0) - if not t_open or utime is None: - return False - try: - if utime.ticks_diff(utime.ticks_ms(), t_open) >= min_ms: - return False - except Exception: - return False - try: - vc._ptt_abort_short_record(tts) - except Exception: - pass - return True +# -*- coding: utf-8 -*- +"""按键表驱动:mode × UX × 事件 → 既有 vc_session 方法。""" +try: + import EventMesh + import constants + import utime +except Exception: + EventMesh = None + constants = None + utime = None + +from vc_ux import derive_ux_state, UX_IDLE, UX_RECORDING, UX_THINKING, UX_TTS, ux_name + +try: + import vc_config +except Exception: + vc_config = None + + +class KeyRouter(object): + @staticmethod + def dispatch_press(vc): + try: + if int(getattr(vc, "VC_mode", 0)) == 1: + return KeyRouter._ptt_press(vc) + return KeyRouter._long_ai_press(vc) + except Exception as e: + try: + import usys + usys.print_exception(e) + except Exception: + pass + return False + + @staticmethod + def handle_ptt_release(vc): + if int(getattr(vc, "VC_mode", 0)) != 1: + return False + if getattr(vc, "AI_key_long_press_flag", 0) == 1: + return False + ux = derive_ux_state(vc) + try: + print("[KEY][router] ptt_release ux={}".format(ux_name(ux))) + except Exception: + pass + if ux == UX_TTS: + try: + if vc._ptt_is_active_tts_playback(): + vc._ptt_stop_tts_on_short_press() + return True + except Exception: + pass + if ux == UX_THINKING: + try: + vc._ptt_stop_dialog_to_idle() + return True + except Exception: + pass + if ux == UX_IDLE: + try: + if hasattr(vc, "_ptt_play_idle_hint"): + vc._ptt_play_idle_hint() + return True + except Exception: + pass + return False + + @staticmethod + def handle_power_short(vc): + """Power 短按:cancel → Close → 清 sid(设计稿幻灯片 6)。""" + ux = derive_ux_state(vc) + try: + print("[KEY][router] power_short ux={}".format(ux_name(ux))) + except Exception: + pass + try: + vc._vc_send_protocol_cancel("power_short/ux={}".format(ux_name(ux))) + except Exception: + pass + try: + vc._vc_close_reason = "power_short" + except Exception: + pass + try: + if EventMesh: + EventMesh.publish("publish_AI_btnInfo", {"func": vc.Close}) + except Exception: + pass + try: + if EventMesh and constants: + EventMesh.publish(constants.EVENT.SYSTEM_UP) + except Exception: + pass + return True + + @staticmethod + def _long_ai_press(vc): + ux = derive_ux_state(vc) + try: + print("[KEY][router] ai_press ux={} {}".format( + ux_name(ux), getattr(vc, "_vc_fsm_log_snip", lambda: "")())) + except Exception: + pass + if ux == UX_RECORDING: + try: + return vc._vc_restart_record_same_dialog() + except Exception: + pass + if ux in (UX_THINKING, UX_TTS): + try: + vc._long_dialogue_interrupt_on_short_press() + return True + except Exception: + pass + if vc._ai_key_press_long_dialogue_session(): + return True + if vc._ai_key_press_long_dialogue_wake(): + if EventMesh and constants: + EventMesh.publish(constants.EVENT.SYSTEM_UP) + return True + return KeyRouter._long_fallback_start(vc) + + @staticmethod + def _long_fallback_start(vc): + try: + vc.Close_flag = 0 + vc._skip_started_uplink_once = 0 + vc._vc_long_dialogue_key_arm_session() + vc._long_dialogue_publish_listening("key.press/fallback") + vc.get_config_timer_count = 0 + vc.key_state = 1 + vc.AI_TTS_str = "" + vc.AI_ASR_str = "" + vc.AI_ASR_str_tmp = "" + EventMesh.publish("publish_AI_btnInfo", {"func": vc.AI_VC_start}) + vc.AI_key_long_press_flag = 0 + return True + except Exception: + return False + + @staticmethod + def _ptt_press(vc): + return True + + @staticmethod + def ptt_release_after_long_press(vc): + """PTT 长按松手:录音过短 → notify TTS + idle。""" + if getattr(vc, "VC_record_state_flag", 0) == 0: + return False + min_ms = 400 + tts = "录音时间太短,请按住圆形按钮说话" + try: + if vc_config: + min_ms = int(vc_config.get_int("PTT_MIN_RECORD_MS", min_ms)) + tts = vc_config.get_attr("PTT_RECORD_TOO_SHORT_TTS", tts) or tts + except Exception: + pass + t_open = int(getattr(vc, "_ptt_record_open_ms", 0) or 0) + if not t_open or utime is None: + return False + try: + if utime.ticks_diff(utime.ticks_ms(), t_open) >= min_ms: + return False + except Exception: + return False + try: + vc._ptt_abort_short_record(tts) + except Exception: + pass + return True diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session.py index f93fd6de..e41fd3a1 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session.py @@ -2854,7 +2854,25 @@ class AI_VC(VCSessionIoMixin, VCSessionMediaMixin, Abstract): return self._handle_pwk_short_release(event, msg, merged_ai_tail=False) def key_prompt(self): - EventMesh.publish("audio_montage_play",["key_prompt.mp3"]) + """遗留 montage(audio.bin 内 key_prompt);K8 量产不用,请用 RECORD_START_TONE。""" + EventMesh.publish("audio_montage_play", ["key_prompt.mp3"]) + + def _ptt_play_idle_hint(self): + """PTT idle 短按:notify 话术提醒(对齐 k8 PTT_HINT,非 KEY_PRESS 本地音)。""" + try: + tts_str = "请您按住圆形的按钮开始讲话,讲完松开即可 " + if "vc_config" in globals() and vc_config: + tts_str = ( + vc_config.get_attr("PTT_HINT", None) + or vc_config.get_attr("KWS_PTT_HINT", None) + or tts_str + ) + tts_str = str(tts_str or "").strip() + if not tts_str: + return + EventMesh.publish("play_tts", [tts_str, 0, "prompt"]) + except Exception as e: + usys.print_exception(e) def VC_error(self, event, msg):#错误播报 self.websocket_error() def set_voicerupt_flag(self,event,msg):#按键控制当前留言播放 diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session_media.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session_media.py index b60d3b8e..9c126e79 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session_media.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_session_media.py @@ -144,7 +144,7 @@ class VCSessionMediaMixin: return False def _vc_play_key_press_tone(self, via=""): - """长对话短按操作确认音(REQ-AI-01);KEY_PRESS_TONE_FILE=None 时不播。""" + """已废弃:K8 无按键确认音;仅 RECORD_START_TONE(start.mp3)。配置缺省则不播。""" try: path = None hold_ms = 300