// // LocalAudioHandler.swift // ylgamehall // // H5 → Native handler:srcIsloop — 本地音频播放(背景循环 / 按钮单次) // 契约:docs/H5-Native-Contract.md §3.1 [3] // 原项目: // - 大厅 daoqi/msext NewRootVC.m 用 lobby gamedir/gamestart // - 子游戏 daoqi/msext gameController.m:359-419 用 self.gamefilepath/self.game_name // 两边各自注册,路径根不同。新外壳保留同一 enum 注册器,但调用方注入 assetsRoot。 // // 音频文件实际路径:`{assetsRoot}/assets/wav/{src}` // - lobby:assetsRoot = SandboxPaths.lobbyIndex.deletingLastPathComponent() // - subGame:assetsRoot = SandboxPaths.subGameIndex(dir,name).deletingLastPathComponent() // import Foundation @MainActor public enum LocalAudioHandler { /// 注册 srcIsloop handler。 /// - Parameters: /// - bridge: 桥接器 /// - assetsRoot: 闭包,返回该容器(lobby / subGame)的 H5 资源根目录; /// 每次 srcIsloop 调用时调一次,让子游戏 zip 升级(effectiveGameDir 末尾追加 "1") /// 后能立刻拿到新目录路径。音频文件实际路径会拼接 `assets/wav/{src}` 在该根下查找。 public static func register( on bridge: any BridgeProtocol, assetsRoot: @escaping @MainActor @Sendable () -> URL ) { // 【3】srcIsloop — 本地音频播放 // 入参 src : string 音频文件名(位于 {assetsRoot}/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 = await assetsRoot() .appendingPathComponent("assets") .appendingPathComponent("wav") .appendingPathComponent(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 } } } }