chore: initial commit

This commit is contained in:
CSBOOK\44744
2026-06-25 00:34:34 +08:00
commit 3cce1be162
295 changed files with 30971 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
@echo off
REM Listen to audio debug dump from ESP32-S3 over UDP.
REM
REM Usage:
REM audio_debug_listen.bat - play post-AEC PCM on port 7200
REM audio_debug_listen.bat 7201 - play on custom port
REM
REM Requirements: ffplay (from ffmpeg), add ffmpeg\bin to PATH
setlocal
set PORT=%1
if "%PORT%"=="" set PORT=7200
where ffplay >nul 2>&1
if errorlevel 1 (
echo Error: ffplay not found. Install ffmpeg and add its bin directory to PATH.
exit /b 1
)
echo Listening for 16kHz 16-bit mono PCM on UDP port %PORT% ...
echo Press Ctrl+C to stop.
ffplay -nodisp -f s16le -ar 16000 -ch_layout mono udp://0.0.0.0:%PORT%
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Listen to audio debug dump from ESP32-S3 over UDP.
#
# Usage:
# ./audio_debug_listen.sh # play post-AEC PCM on port 7200
# ./audio_debug_listen.sh 7201 opus # play raw Opus on port 7201
#
# Requirements: ffplay (from ffmpeg)
set -euo pipefail
PORT="${1:-7200}"
MODE="${2:-pcm}"
if ! command -v ffplay &>/dev/null; then
echo "Error: ffplay not found. Install ffmpeg first." >&2
exit 1
fi
if [ "$MODE" = "opus" ]; then
echo "Listening for Opus frames on UDP port $PORT ..."
echo "Note: raw Opus over UDP may not play correctly without an RTP wrapper."
echo "Press Ctrl+C to stop."
ffplay -nodisp -f ogg -i "udp://0.0.0.0:$PORT"
else
echo "Listening for 16kHz 16-bit mono PCM on UDP port $PORT ..."
echo "Press Ctrl+C to stop."
ffplay -nodisp -f s16le -ar 16000 -ch_layout mono "udp://0.0.0.0:$PORT"
fi
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""
Save audio debug dump from ESP32-S3 to a WAV file.
Usage:
python audio_debug_save.py # save to debug_aec.wav on port 7200
python audio_debug_save.py -o out.wav -p 7200 # custom output and port
The script listens for raw 16-bit mono PCM at 16 kHz streamed over UDP,
and writes it into a standard WAV file. Press Ctrl+C to stop and finalize.
"""
import argparse
import socket
import struct
import sys
SAMPLE_RATE = 16000
CHANNELS = 1
SAMPLE_WIDTH = 2 # 16-bit
def main():
parser = argparse.ArgumentParser(description="Save UDP audio debug dump to WAV")
parser.add_argument("-p", "--port", type=int, default=7200, help="UDP port (default: 7200)")
parser.add_argument("-o", "--output", default="debug_aec.wav", help="Output WAV file")
args = parser.parse_args()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", args.port))
print(f"Listening on UDP port {args.port}, writing to {args.output}")
print("Press Ctrl+C to stop and finalize WAV.")
# Write WAV header with placeholder data size, patch on exit
f = open(args.output, "wb")
data_bytes = 0
byte_rate = SAMPLE_RATE * CHANNELS * SAMPLE_WIDTH
block_align = CHANNELS * SAMPLE_WIDTH
def write_wav_header(total_data_bytes):
f.seek(0)
f.write(b"RIFF")
f.write(struct.pack("<I", 36 + total_data_bytes))
f.write(b"WAVE")
f.write(b"fmt ")
f.write(struct.pack("<I", 16)) # fmt chunk size
f.write(struct.pack("<H", 1)) # PCM format
f.write(struct.pack("<H", CHANNELS))
f.write(struct.pack("<I", SAMPLE_RATE))
f.write(struct.pack("<I", byte_rate))
f.write(struct.pack("<H", block_align))
f.write(struct.pack("<H", SAMPLE_WIDTH * 8))
f.write(b"data")
f.write(struct.pack("<I", total_data_bytes))
write_wav_header(0)
try:
while True:
data, _ = sock.recvfrom(2048)
f.write(data)
data_bytes += len(data)
except KeyboardInterrupt:
pass
finally:
write_wav_header(data_bytes)
f.close()
sock.close()
duration = data_bytes / byte_rate if byte_rate else 0
print(f"\nSaved {data_bytes} bytes ({duration:.1f}s) to {args.output}")
if __name__ == "__main__":
main()
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
fail() {
echo "[FAIL] $1" >&2
exit 1
}
echo "[CHECK] facade duplicate guards"
if rg -n "domain_digital_input_config_t" components/domain/include/domain.h >/dev/null 2>&1; then
fail "domain facade must not expose platform-owned digital input config"
fi
if rg -n "domain_runtime_stepper_|domain_runtime_platform_init\(" components/domain/include/domain.h >/dev/null 2>&1; then
fail "domain facade must not expose platform primitive runtime adapters"
fi
if rg -n "domain_runtime_storage_" components/domain/include/domain.h >/dev/null 2>&1; then
fail "domain facade must not expose generic platform storage adapters"
fi
if rg -n "domain_runtime_lcd_trigger_slot_animation" components/domain/include/domain.h components/domain/src >/dev/null 2>&1; then
fail "domain must request LCD slot reels through the expression catalog"
fi
if rg -n "play_prompt_named|platform_behavior_play_prompt_named" components/platform/include/platform.h components/platform/internal >/dev/null 2>&1; then
fail "platform facade/internal Wi-Fi behavior hooks must not expose semantic prompt string IDs"
fi
if rg -n "VOICE_PROMPT_WIFI_|voice_prompt_id_t|voice_prompt_play\\(" components/platform/internal components/platform/src >/dev/null 2>&1; then
fail "platform must expose voice prompt playback as a primitive file API, not semantic Wi-Fi prompt IDs"
fi
if rg -n "platform_multimodal_downstream_playback_event|PLATFORM_MULTIMODAL_DOWNSTREAM_PLAYBACK" components/platform/include/platform.h >/dev/null 2>&1; then
fail "platform facade must not duplicate domain-owned multimodal semantic events"
fi
if rg -n "esp-sr|esp_sr|esp_afe|afe_config|afe_sr|model_path\\.h|srmodels|CONFIG_SR_|SR_WN|MULTIMODAL_WAKE_WORD|wake_word|Wake word|Wake-word|wn9_" components main sdkconfig.defaults dependencies.lock >/dev/null 2>&1; then
fail "ESP-SR and wake-word code/dependencies must stay removed"
fi
if rg -n "platform_lcd_trigger_slot_animation|lcd_trigger_slot_animation" components/platform/include components/platform/internal components/platform/src/runtime >/dev/null 2>&1; then
fail "platform must expose LCD presentation primitives, not one-off expression APIs"
fi
if rg -n "control_plane_get_direction_(count|view|current_direction_index)|control_plane_get_current_angle" components/control_plane/include/control_plane.h components/control_plane/src >/dev/null 2>&1; then
fail "control-plane facade/source must not duplicate domain-owned direction catalog query contracts"
fi
if rg -n "control_plane_direction_default_(axis_config|calibration)|CONTROL_PLANE_CAL_" components/control_plane >/dev/null 2>&1; then
fail "direction calibration defaults are domain-owned and must not be duplicated in control-plane"
fi
if rg -n "void\\s+platform_multimodal_(transport_stop|audio_stop_uplink)\\s*\\(|void\\s+domain_runtime_multimodal_(transport_stop|audio_stop_uplink)\\s*\\(" components/platform/include/platform.h components/domain/include/domain.h >/dev/null 2>&1; then
fail "multimodal stop facade APIs must expose timeout/result semantics"
fi
if rg -n "void\\s+(domain_runtime|control_plane)_stop_lcd_render_loop\\s*\\(" components/domain/include/domain.h components/control_plane/include/control_plane.h >/dev/null 2>&1; then
fail "LCD stop facade APIs must expose timeout/result semantics"
fi
echo "[CHECK] component dependency ownership"
if rg -n "^\\s*esp_wifi\\s*$|^\\s*nvs_flash\\s*$" components/domain/CMakeLists.txt >/dev/null 2>&1; then
fail "domain must not depend directly on esp_wifi or nvs_flash"
fi
if rg -n "^\\s*REQUIRES\\s*$" components/control_plane/CMakeLists.txt >/dev/null 2>&1; then
fail "control-plane must not publicly re-export domain include paths; use PRIV_REQUIRES for domain"
fi
if rg -n "#include \"multimodal/(audio_pipeline|transport_ws)\\.h\"" components/domain >/dev/null 2>&1; then
fail "domain must not include platform multimodal internal headers directly"
fi
echo "[CHECK] no cross-component internal includes"
if rg -n "#include \"components/.*/internal/.*\"" components main >/dev/null 2>&1; then
fail "cross-component internal include pattern found"
fi
echo "[CHECK] no unbounded wait in control-plane runtime stop paths"
if rg -n "portMAX_DELAY" components/control_plane/src/runtime >/dev/null 2>&1; then
fail "control-plane runtime paths must avoid unbounded portMAX_DELAY"
fi
if rg -n "CONTROL_PLANE_MODEL_CMD_INTERNAL_STOP|command_type\\s*=\\s*\\(int\\)command_type" components/control_plane/src/runtime/control_plane_runtime_workers.c >/dev/null 2>&1; then
fail "control-plane model command queue must keep typed command contracts and avoid magic sentinels"
fi
if rg -n "FACTORY_CLI_PARK_DELAY_MS|Cooperative exit: park" components/control_plane/src/runtime/control_plane_factory_cli.c >/dev/null 2>&1; then
fail "factory CLI worker must cooperatively exit and clear its handle instead of parking for force-delete"
fi
if rg -n "if \\(s_(model_cmd|ws_wait|direction_console|lcd_render_loop|wifi_state|ptt|slot_switch)_task == NULL\\)" components/control_plane/src/runtime/control_plane_runtime_workers.c >/dev/null 2>&1; then
fail "control-plane runtime worker task handles must be read through the runtime lock helpers"
fi
if ! rg -n "ws_start_async_stop" components/platform/src/multimodal/transport_ws/transport_ws_runtime_impl.c >/dev/null 2>&1; then
fail "websocket zero-timeout stop must start an asynchronous teardown worker"
fi
if rg -n "effective_timeout_ms = timeout_ms > 0 \\? timeout_ms : 1" components/platform/src/multimodal/transport_ws/transport_ws_runtime_impl.c >/dev/null 2>&1; then
fail "zero-timeout stop must be asynchronous, not a 1ms synchronous teardown"
fi
if perl -0ne 'our $found; $found = 1 if /esp_err_t multimodal_audio_stop_uplink\(int timeout_ms\).*?effective_timeout_ms\s*=\s*timeout_ms\s*>\s*0\s*\?\s*timeout_ms\s*:\s*1/s; END { exit($found ? 0 : 1) }' components/platform/src/multimodal/audio_pipeline/audio_pipeline_runtime_impl.c >/dev/null 2>&1; then
fail "zero-timeout stop must be asynchronous, not a 1ms synchronous teardown"
fi
echo "[CHECK] outbound HTTPS owner boundary"
if rg -n "#include \"esp_http_client.h\"|#include <esp_http_client.h>|esp_http_client_" components/control_plane >/dev/null 2>&1; then
fail "control-plane must not use esp_http_client directly; route outbound HTTPS through the owner adapter"
fi
if rg -n "domain_runtime_storage_" components/control_plane >/dev/null 2>&1; then
fail "control-plane must not use generic storage adapters; use owner-scoped domain ports"
fi
echo "[PASS] architecture guards"