Files
youle_app_ios_v2/ylgamehall/Source
joywayer df2a099fb5 3.F:mediaTypeAudio 完整实现(远端语音下载→转码→播放→反向 callback)
替换 mediaTypeAudio handler 的 stub 为完整链路。至此 Phase 3 录音 + 远端
语音播放整套流程接通;prepareaudio / mediaTypeAudio / VoicePlaying / 反向
callback 全部对齐 daoqi msext 行为。

## 完整链路

1. H5 调 mediaTypeAudio({audiourl, user})
2. 解析入参;参数不全 → log + return(不触发 cb,对齐 daoqi 入参空时
   走到 file 不存在 return 的语义)
3. `VoicePlayer.shared.recordIncomingUser(user)` 更新 lastUserId
   ⚠️ 无论后续是否实际播放都更新(对齐 daoqi gameController.m:438
   `self.User_id=userid;` 总是执行的行为)
4. 注册 VoicePlayer.onFinish 绑定当前 bridge([weak bridge])→ 播完触发
   `gameui_stop_voice(lastUserId)` 反向 callback
5. `URLSession.shared.data(from:)` 异步下载 AMR
   - 下载失败 → log + return,**不触发 cb**
     (对齐 daoqi NewRootVC.m:335-337 file 不存在 return)
6. 写入沙盒 .amr 临时文件
7. `AMRCodec.amrToWav` 转码到 `{tempName}_AmrToWav.wav`
   (文件名约定对齐 daoqi NewRootVC.m:341)
   - 转码失败 → log,**但 cb 仍触发**(对齐 daoqi NewRootVC.m:355-357
     amr转wav失败 NSLog 后仍触发 responseCallback)
8. `await VoiceCenter.shared.isEnabled`(canbofang 等价)+ 实际播放
   - 仅转码成功 + 开关 enabled 时 play + 触发 `gameui_play_voice(user)`
     对齐 daoqi NewRootVC.m:343-353 if (canbofang) { play + callHandler }
   - `gameui_play_voice` data 是 user 字符串(非 dict)
9. 触发 `cb("Response from mediaTypeAudio")` —— **总是**触发
   (除下载失败 return 路径外)

## VoicePlayer.onFinish 绑定时机

每次 mediaTypeAudio 调用时更新 onFinish 闭包,绑定当前 bridge。
[weak bridge] 防止 bridge 释放后 onFinish 触发野指针。

## 关键契约点(已验证字面字符串)

- handler 名:"mediaTypeAudio"
- 入参字段:"audiourl" / "user"
- 回包字面:"Response from mediaTypeAudio"
- 反向 callback:
  - "gameui_play_voice" data = user String
  - "gameui_stop_voice" data = lastUserId String

## 验证

BuildProject 通过 0 错误。至此 Phase 3 全部子任务(3.A / 3.B / 3.C /
3.D / 3.E / 3.F)落地,语音功能完整对齐原项目。
2026-06-24 01:38:17 +08:00
..
2026-06-23 17:24:59 +08:00