fix(录音): gameui_play_voice/stop_voice 对齐 Android play() 时机

play_voice 改为进入 mediaTypeAudio 即立刻同步回调(对齐 Android play()在播放前调),
不再等 AVPlayer prepared(网络AMR延迟致动画起得晚/疑似没调);打断旧语音补发旧
user 的 stop_voice;创建失败也补 stop_voice;抽出 endVoice 统一收尾(完成/出错)防重复。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lanterngamescn
2026-06-27 18:19:21 +08:00
co-authored by Claude Opus 4.8
parent 7ae58e90ca
commit 64c0c621bc
@@ -134,14 +134,21 @@ export class AudioProvider implements CapabilityProvider {
return;
}
const user: string = req.user;
// 打断正在播的旧语音:先通知 H5 停旧座位动画,再释放(对齐"同一时刻只播一条")。
if (this.voicePlayer !== undefined && this.currentVoiceUser !== '') {
this.bridge?.callHandler(OutboundHandlers.GameUiStopVoice, this.currentVoiceUser);
}
this.releaseVoice();
// 对齐 Android play():进入即立刻同步通知 H5 开始播放(座位说话动画),再创建播放器;
// 不等到 prepared 才报——网络 AMR 加载可能延迟数百 ms,晚报会被认为"没调/动画起得晚"。
this.currentVoiceUser = user;
this.bridge?.callHandler(OutboundHandlers.GameUiPlayVoice, user);
media.createAVPlayer().then((player: media.AVPlayer) => {
if (this.disposed) {
player.release(); // 创建期间已销毁:直接释放,勿播放/勿挂回调(防泄漏 + 打已弃桥
if (this.disposed || this.currentVoiceUser !== user) {
player.release(); // 创建期间已销毁或已被新语音取代:释放孤儿播放器(旧 user 的 stop 已发
return;
}
this.voicePlayer = player;
this.currentVoiceUser = user;
player.on('stateChange', (state: string) => {
if (state === 'initialized') {
player.prepare();
@@ -149,19 +156,28 @@ export class AudioProvider implements CapabilityProvider {
player.loop = false;
player.setVolume(this.effVolume());
player.play();
this.bridge?.callHandler(OutboundHandlers.GameUiPlayVoice, user);
} else if (state === 'completed') {
this.bridge?.callHandler(OutboundHandlers.GameUiStopVoice, user);
this.releaseVoice();
this.endVoice(user); // 播放完成:gameui_stop_voice + 释放
}
});
player.on('error', (e: BusinessError) => {
this.log.w(`voice play error: ${e.code} ${e.message}`);
this.bridge?.callHandler(OutboundHandlers.GameUiStopVoice, user);
this.releaseVoice();
this.endVoice(user);
});
player.url = req.audiourl;
}).catch((e: BusinessError) => this.log.w(`createAVPlayer(voice) failed: ${e.message}`));
}).catch((e: BusinessError) => {
this.log.w(`createAVPlayer(voice) failed: ${e.message}`);
this.endVoice(user); // 创建失败也补 stop_voice,避免座位动画卡在"说话中"
});
}
/** 语音播放完成/出错:通知 H5 停座位动画(gameui_stop_voice) + 释放播放器。一次性,防重复。 */
private endVoice(user: string): void {
if (this.currentVoiceUser !== user) {
return; // 已被新语音取代或已结束
}
this.bridge?.callHandler(OutboundHandlers.GameUiStopVoice, user);
this.releaseVoice();
}
/** 播放本地音效/背景乐:<urlpath>/<gameContentDir>/assets/wav/<src>。isloop<0 停止该 src。 */