Skip to content

Voice Replay

unicbm edited this page Jul 4, 2026 · 6 revisions

Voice and Chat Replay

Voice Replay 回放的是 demo 里真实存在的游戏内语音。它不录麦克风、不生成语音、 不做语音转文字,也不从 Discord、TeamSpeak、视频或外部录音里抽音频。

文字聊天 replay 回放的是 demo 里的 chat event。它不手工伪造 UI 文本,而是让匹配到的 安全 replay bot 走 CS2 原生 say / say_team 路径。

两者的时间线策略不同:

  • Voice 是连续音频流,严格按当前播放窗口回放。
  • Chat 是瞬发事件,早于当前 playback anchor 的 freezetime 消息会在 replay 开始时补发一次。

它做了什么

链路是:

  1. converter 读取 demo 中的 voice netmessage。
  2. converter 写出紧凑的每回合 .dtv sidecar。
  3. CounterStrikeSharp runtime 在加载回合时加载对应 sidecar。
  4. replay bot 按 demo speaker 身份发送原始 voice payload。

Voice Replay 只会把安全 replay bot 当作 sender;不会把 replay 控制权或 voice sender 权限分配给真人玩家。

Chat Replay 做了什么

链路是:

  1. converter 读取 demo 中的 chat_message / server_message event。
  2. converter 把轻量聊天 metadata 写入 manifest.jsonrounds[].chat_messages
  3. CounterStrikeSharp runtime 在加载回合时读取对应 round 的聊天消息。
  4. replay 开始后,匹配到安全 replay bot 的玩家消息通过 saysay_team 发出。
  5. server / admin 类型消息作为全局 DemoTracer server message 打印。

聊天 metadata 默认导出,不需要 CLI 或 GUI 额外开关。

最短导出命令

正常 round replay 语音导出只需要:

cs2-demotracer.exe convert --demo "<demo.dem>" --output "<output-dir>" --export-voice

输出里应该出现:

voice/round00.dtv
voice/round01.dtv

如果没有 voice/,先确认命令里确实有 --export-voice,然后再判断 demo 本身是否包含 游戏内语音。更短的操作页见 Export Voice

哪些 demo 会有语音

并非所有 .dem 都有语音。只有 demo 中存在可用的 CCLCMsg_VoiceData、 非空音频 payload 和有效 speaker XUID 时,才可能导出 voice sidecar。

更可能有语音的 demo:

  • 社区服 demo。
  • FACEIT demo。
  • 5E demo。
  • 其他保留游戏内语音 netmessage 的服务器录像。

常见没有语音的原因:

  • 平台或服务器没有录 voice data。
  • voice data 被剥离。
  • 玩家没有使用游戏内语音。
  • voice frame 没有有效 XUID。
  • parser 没看到可用 payload。

显式导出:

cs2-demotracer.exe convert --demo "<demo.dem>" --output "<output-dir>" --export-voice

如果没有可用 frame,转换时可能看到:

warning: voice sidecar export skipped: no voice frames matched the requested filters

或:

warning: voice sidecar export skipped: no voice sidecar windows contained frames

Sidecar 布局

推荐布局:

manifest.json
round00/t/*.dtr
round00/ct/*.dtr
voice/round00.dtv
voice/round01.dtv
voice/round02.dtv

voice/roundXX.dtv 是 voice sidecar,不是路线 replay 文件。它保存:

  • demo source metadata。
  • map、tick rate、clip start/end tick。
  • speaker table:XUID、client id、frame count。
  • demo timeline 上的 frame timing。
  • 原始 voice audio bytes。
  • sample rate、voice level、sequence、section、packet count、packet offsets 等元数据。

核心原则是无损搬运。DemoTracer 不 decode、不 resample、不 denoise、不重新编码语音; 它保留 demo-backed Opus payload 和 runtime 回放所需的元数据。.dtv 是二进制格式, 因此体积应明显小于把每个 frame 和 audio byte 展开成 JSON。

聊天不使用 .dtv。它保存在:

manifest.json -> rounds[].chat_messages

每条消息包含 tick、sender SteamID、可选 sender name、scope 和文本。导出侧会清理空文本、 去重,并跳过没有 sender SteamID 的普通玩家消息。server / admin 范围可以没有玩家 SteamID。

回合和 sequence 对齐

自动播放由 dtr_voice_auto 控制:

dtr_voice_auto status
dtr_voice_auto on
dtr_voice_auto off

自动文字聊天由 dtr_chat_auto 控制,默认开启:

dtr_chat_auto status
dtr_chat_auto on
dtr_chat_auto off

加载回合时:

  1. DemoTracer 从 selected source round 加载 .dtr replay。
  2. 在 manifest 附近查找 voice/roundXX.dtv
  3. replay 开始时,把 sidecar speaker XUID 映射到已加载的 replay bot slot。
  4. 未映射的 speaker 会跳过。
  5. 已映射的 voice frame 按回合时间线发送。

sequence 回放里的 from_source_round 指 demo 的 source round。每一步都会加载对应 source round、对应 voice/roundXX.dtv,以及 manifest 中的 rounds[].chat_messages

成功启动时日志类似:

voice_auto=started file=roundXX.dtv speakers=... frames=... recipients=... anchor=live offset=...

Freeze Time 和 Preroll

路线 replay 可以包含 bounded freeze-time preroll,让开局道具和站位有足够上下文。 自动导出的 voice/roundXX.dtv 对齐的是 live round window。

runtime 行为:

  • live replay start 时,语音按 live timeline 播放。
  • freeze-preroll start 时,如果 .dtv 从 live start 或更晚开始,语音不会提前播。
  • 这种情况下,语音会 defer 到 live anchor。

你可能看到:

voice_auto=deferred_live file=roundXX.dtv clip_start=... live_start=...

这是正常状态,意思是移动 replay 正在做 freeze-preroll,但语音等待 live timing, 避免同一句话在 freeze time 和 live start 后各播一次。

文字聊天不同。它是瞬发事件,不是连续媒体。如果 replay 从 live anchor 开始,而某条 freezetime chat 早于当前 anchor,DemoTracer 会把它的播放时间压到 0,在 replay 开始时 补发一次。这样不会丢掉开局文字交流,也不会像语音那样破坏连续时序。

多人同时说话和听音规则

一个 round .dtv 可以包含多个 speaker。

播放规则:

  • 每个 speaker XUID 映射到一个已加载 replay bot。
  • 多个 speaker 的 frame 可以按同一时间线交错发送。
  • 找不到 loaded replay bot 的 speaker 会跳过。
  • 只有 live human client 会收到语音;bot 和 HLTV 不作为 recipient。

听音规则按当前本地 CS2 队伍语音语义过滤:

  • 观察者可以听到所有 replay voice。
  • T 玩家只能听到 T replay bot 的语音。
  • CT 玩家只能听到 CT replay bot 的语音。
  • 如果某个 speaker 被映射到错误队伍,能否听到取决于该 bot 当前所在队伍。

文字聊天显示和观察者可见性

玩家 chat 使用 CS2 原生 say / say_team,最终显示格式由游戏自己决定。DemoTracer 不用 白字 mirror 伪造聊天 UI。

因此 spectator 是否能看到玩家文字聊天,受 CS2 server cvar 影响。本地测试中如果希望观察者 看到 replay bot 发出的 native text chat,设置:

sv_full_alltalk 1

单独 sv_allchat 1 不足以保证 spectator 可见。

手动测试一条聊天:

dtr_chat_test <loaded|any|slot> [all|team] <message>

例如:

dtr_chat_test loaded all hello
dtr_chat_test 3 team rush B

该命令使用和自动聊天回放相同的 server-side say / say_team 路径。

手动测试命令

正常 round/sequence 回放不需要手搓 voice debug 命令;它们主要用于 debug .dtv 文件。

单 speaker 测试:

dtr_voice_test <voice_clip.dtv> <sender_slot> [recipient_slot|all]

多 speaker 混合测试:

dtr_voice_mix <voice_clip.dtv> <xuid=slot[,xuid=slot...]|loaded> [recipient_slot|all]

停止当前 voice test/auto playback:

dtr_voice_stop

dtr_voice_mix ... loaded 会用当前 loaded replay bot 的 SteamID64 自动匹配 sidecar speaker。

限制

  • demo 没有 voice data 就无法回放语音。
  • 当前 runtime 支持 demo-backed Opus voice payload。
  • speaker 必须能映射到已加载的安全 replay bot。
  • recipient 必须是 live human client。
  • 队伍语音按当前本地队伍过滤,不复原 demo 中每个 packet 的原始 recipient list。
  • 不做转写、降噪、音量归一化或语义分析。
  • 只复制 .dtr 不会带上语音;必须把 voice/roundXX.dtv 和 manifest 输出包一起保留。
  • 如果 BotController 没有 voice send capability,路线 replay 仍可运行,但语音不能发送。
  • chat 使用原生 say / say_team,所以可见性受 CS2 server cvar 和队伍/观察者规则影响。
  • 找不到匹配安全 replay bot 的玩家 chat 会被跳过;server/admin chat 会作为 [DTR] server message 打印。

排错

voice sidecars 0:选中的 demo/round 没有可用 voice frame。

voice_auto=unavailable:native runtime 没暴露 voice send capability,或 CSS/native ABI 不匹配。

unsupported voice clip format; expected DTRVOICE v2 .dtv:文件不是当前 DTV sidecar 格式,或文件损坏。

voice frame N format=...; only Opus format=2 is supported:frame 音频格式不是当前 runtime sender 支持的 Opus。

voice_auto=map_failed:sidecar speaker 和当前 loaded replay bot 对不上。检查 source round、partial replay skip 和 identity mapping。

unmapped voice speakers will be skipped:部分 speaker 被跳过;已映射 speaker 仍可播放。

voice_auto=no_human_recipients:没有可接收语音的 live human client。

map mismatch:sidecar/manifest 地图和当前服务器地图不同,或用了错误输出目录。

chat_auto=off:自动文字聊天关闭。执行 dtr_chat_auto on

chat_auto=no_messages:当前 round 的 manifest 没有可用 rounds[].chat_messages

观察者看不到文字聊天:先确认聊天区不是被 HUD/过滤隐藏,再设置 sv_full_alltalk 1sv_allchat 1 单独不够。

某些玩家聊天缺失:通常是 sender 没有映射到当前 loaded replay bot,或该 slot 不再是安全 replay bot。检查 dtr_status、partial replay skip 和当前 source round 是否匹配。

Clone this wiki locally