Files
youle_app_ios_v2/ylgamehall/Source/Bridge/Handlers/LocalAudioHandler.swift
T
joywayerandClaude Opus 4.7 0602b9d4b6 Phase 3.A:srcIsloop 本地音频 + 3.B/3.C/3.D handler stub
3.A 本地音频完整落地(与 msext NewRootVC initJSdata 等价):

  Source/Audio/AudioPlayer.swift(@MainActor 状态机):
    - playOnce(url) 单次按钮音;buttonPlayers 数组保活池避免多次点击
      互相打断(duration + 0.5s 后清理)
    - loopBackground(url, type:) 循环背景音;numberOfLoops = -1 +
      backgroundType 记录用于同名停止
    - stopBackground(type:) 同名校验后才停止(msext 同款语义)

  Source/Bridge/Handlers/LocalAudioHandler.swift:
    - 注册 srcIsloop handler(@MainActor enum)
    - 入参 src + isloop,按 0/1/-1 分支调 AudioPlayer.shared 对应方法
    - audioFileURL 路径:{Caches}/{gamedir}/{gamestart}/assets/wav/{src}
      与 msext NewRootVC 路径约定 1:1(H5 zip 内 assets/wav 子目录)
    - audioFileURL 标 nonisolated 因 SandboxPaths 是 nonisolated 静态
    - cb 字面 "Response from srcIsloop" 严格保持

  Source/Bridge/Handlers/RemoteAudioHandler.swift(3.B/3.C/3.D stub):
    - prepareaudio + mediaTypeAudio 仅 cb 维持契约,无真实业务
    - 待 Phase 3.B(opencore-amr .a 接入)+ 3.C(七牛 SPM + token)+
      3.D(远端播放)完整后升级
    - 按 CLAUDE.md 原则 A:H5 调啥就得有人接,即使是 stub

WebContainerViewController.registerBridgeHandlers 接入:
  - LocalAudioHandler.register(完整实现)
  - RemoteAudioHandler.register(stub)

Plan §5 Phase 3.1 勾选;3.2 真机验证留待 H5 提供音频文件后做。

Verification-Checklist.md 新增 Phase 3 章节:
  - H. 本地音频(E.14-E.16 srcIsloop 三个 isloop 值)
  - I. prepareaudio / mediaTypeAudio stub(E.17 E.18)

BuildProject 通过

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-22 19:11:34 +08:00

58 lines
2.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// LocalAudioHandler.swift
// ylgamehall
//
// H5 → Native handlersrcIsloop — 本地音频播放(背景循环 / 按钮单次)
// 契约:docs/H5-Native-Contract.md §3.1 3
// 原项目:daoqi/msext NewRootVC.m initJSdata 关联 buttunPlayer / backgroundPlayer
//
// 本地音频文件路径:`{Caches}/{gamedir}/{gamestart}/assets/wav/{src}`
// 与 SandboxPaths.lobbyIndex 同一根目录下 assets/wav 子目录
//
import Foundation
@MainActor
public enum LocalAudioHandler {
public static func register(on bridge: any BridgeProtocol) {
// 【3】srcIsloop — 本地音频播放
// 入参 src : string 音频文件名(位于 lobbyAssets/assets/wav/{src}
// 入参 isloop : int 0 单次 / 1 循环背景 / -1 停同名循环
// cb: "Response from srcIsloop"
bridge.register("srcIsloop") { data, callback in
defer { callback?(.string("Response from srcIsloop")) }
guard let obj = data?.asObject,
let src = obj["src"]?.asString,
let isloop = obj["isloop"]?.asInt
else {
return
}
let url = audioFileURL(src: src)
switch isloop {
case 0:
await AudioPlayer.shared.playOnce(url)
case 1:
await AudioPlayer.shared.loopBackground(url, type: src)
case -1:
await AudioPlayer.shared.stopBackground(type: src)
default:
break
}
}
}
/// `{lobbyAssets}/assets/wav/{src}` —— 与 msext NewRootVC 路径约定一致。
/// H5 业务调用前会先用 zip 升级(gamehall.zip 内含 assets/wav/*.wav 音频文件)。
nonisolated private static func audioFileURL(src: String) -> URL {
SandboxPaths.lobbyIndex
.deletingLastPathComponent() // {Caches}/{gamedir}/{gamestart}/
.appendingPathComponent("assets")
.appendingPathComponent("wav")
.appendingPathComponent(src)
}
}