Javascript的声音问题
我正在用Javascript编写一个游戏引擎的前端。引擎在服务器上运行,并通过“信号器”向web浏览器发送图片和声音。我正在使用React框架 当游戏运行时,服务器发送波形格式的小声音样本,通过AudioPlayerProps传递到此组件 我的声音有两个主要问题。首先,声音听起来“脱节”。 第二个是,过了一段时间,声音就停止了。我可以看到声音在音频队列中排队,但没有调用“playNextAudioTrack”方法。控制台中没有解释这一点的错误 如果这不是为游戏前端提供声音的最佳方式,请让我知道 此外,如果你想看到更多的代码,请让我知道。这是一个巨大的多层项目,所以我只展示我认为您需要看到的内容 现在我正在测试Chrome。在这个阶段,我需要打开开发工具来克服“用户没有与页面交互,所以你不能播放任何声音问题”。我会在适当的时候解决这个问题Javascript的声音问题,javascript,reactjs,html5-audio,Javascript,Reactjs,Html5 Audio,我正在用Javascript编写一个游戏引擎的前端。引擎在服务器上运行,并通过“信号器”向web浏览器发送图片和声音。我正在使用React框架 当游戏运行时,服务器发送波形格式的小声音样本,通过AudioPlayerProps传递到此组件 我的声音有两个主要问题。首先,声音听起来“脱节”。 第二个是,过了一段时间,声音就停止了。我可以看到声音在音频队列中排队,但没有调用“playNextAudioTrack”方法。控制台中没有解释这一点的错误 如果这不是为游戏前端提供声音的最佳方式,请让我知道
import * as React from "react";
import { useEffect, useState } from "react";
export interface AudioPlayerProps {
data: string;
}
export const AudioPlayer = function (props: AudioPlayerProps): JSX.Element {
const [audioQueue, setAudioQueue] = useState<string[]>([])
useEffect(
() => {
if (props.data != undefined) {
audioQueue.push(props.data);
}
}, [props.data]);
const playNextAudioTrack = () => {
if (audioQueue.length > 0) {
const audioBase64 = audioQueue.pop();
const newAudio = new Audio(`data:audio/wav;base64,${audioBase64}`)
newAudio.play().then(playNextAudioTrack).catch(
(error) => {
setTimeout(playNextAudioTrack, 10);
}
)
}
else {
setTimeout(playNextAudioTrack, 10);
}
}
useEffect(playNextAudioTrack, []);
return null;
}
import*as React from“React”;
从“react”导入{useffect,useState};
导出接口AudioPlayerProps{
数据:字符串;
}
export const AudioPlayer=函数(道具:AudioPlayerProps):JSX.Element{
常量[audioQueue,setAudioQueue]=useState([])
使用效果(
() => {
if(props.data!=未定义){
audioQueue.push(props.data);
}
},[道具数据];
const playnex taudiotrack=()=>{
如果(audioQueue.length>0){
const audioBase64=audioQueue.pop();
const newAudio=newAudio(`data:Audio/wav;base64,${audioBase64}`)
newAudio.play().then(playNextAudioTrack).catch(
(错误)=>{
设置超时(playNextAudioTrack,10);
}
)
}
否则{
设置超时(playNextAudioTrack,10);
}
}
useEffect(playNextAudioTrack,[]);
返回null;
}
我解决了自己的问题。下面是我编写的用JavaScript处理分块音频的typescript类
我不是JavaScript专家,因此可能存在错误
编辑:在15分钟内多次运行它之后,它在大约10分钟的时候失败了几次。还需要一些工作
// mostly from https://gist.github.com/revolunet/e620e2c532b7144c62768a36b8b96da2
// Modified to play chunked audio for games
import { setInterval } from "timers";
//
const MaxScheduled = 10;
const MaxQueueLength = 2000;
const MinScheduledToStopDraining = 5;
export class WebAudioStreamer {
constructor() {
this.isDraining = false;
this.isWorking = false;
this.audioStack = [];
this.nextTime = 0;
this.numberScheduled = 0;
setInterval(() => {
if (this.audioStack.length && !this.isWorking) {
this.scheduleBuffers(this);
}
}, 0);
}
context: AudioContext;
audioStack: AudioBuffer[];
nextTime: number;
numberScheduled: number;
isDraining: boolean;
isWorking: boolean;
pushOntoAudioStack(encodedBytes: number[]) {
if (this.context == undefined) {
this.context = new (window.AudioContext)();
}
const encodedBuffer = new Uint8ClampedArray(encodedBytes).buffer;
const streamer: WebAudioStreamer = this;
if (this.audioStack.length > MaxQueueLength) {
this.audioStack = [];
}
streamer.context.decodeAudioData(encodedBuffer, function (decodedBuffer) {
streamer.audioStack.push(decodedBuffer);
}
);
}
scheduleBuffers(streamer: WebAudioStreamer) {
streamer.isWorking = true;
if (streamer.context == undefined) {
streamer.context = new (window.AudioContext)();
}
if (streamer.isDraining && streamer.numberScheduled <= MinScheduledToStopDraining) {
streamer.isDraining = false;
}
while (streamer.audioStack.length && !streamer.isDraining) {
var buffer = streamer.audioStack.shift();
var source = streamer.context.createBufferSource();
source.buffer = buffer;
source.connect(streamer.context.destination);
if (streamer.nextTime == 0)
streamer.nextTime = streamer.context.currentTime + 0.01; /// add 50ms latency to work well across systems - tune this if you like
source.start(streamer.nextTime);
streamer.nextTime += source.buffer.duration; // Make the next buffer wait the length of the last buffer before being played
streamer.numberScheduled++;
source.onended = function () {
streamer.numberScheduled--;
}
if (streamer.numberScheduled == MaxScheduled) {
streamer.isDraining = true;
}
};
streamer.isWorking = false;
}
}
//主要来自https://gist.github.com/revolunet/e620e2c532b7144c62768a36b8b96da2
//修改为播放游戏的分块音频
从“计时器”导入{setInterval};
//
常量MaxScheduled=10;
常量MaxQueueLength=2000;
const minscheduledtospodraining=5;
导出类WebAudioStreamer{
构造函数(){
this.isDraining=false;
this.isWorking=false;
this.audioStack=[];
this.nextTime=0;
this.numberScheduled=0;
设置间隔(()=>{
if(this.audioStack.length&&!this.isWorking){
this.scheduleBuffers(this);
}
}, 0);
}
语境:听觉语境;
audioStack:AudioBuffer[];
下一时间:数字;
编号计划:编号;
istraining:布尔型;
isWorking:布尔;
pushOntoAudioStack(编码字节:数字[]){
if(this.context==未定义){
this.context=new(window.AudioContext)();
}
const encodedBuffer=新的Uint8ClampedArray(encodedBytes).buffer;
const streamer:WebAudioStreamer=this;
if(this.audioStack.length>MaxQueueLength){
this.audioStack=[];
}
拖缆.上下文.解码音频数据(encodedBuffer,函数(decodedBuffer){
拖缆.音频堆栈.推送(解码缓冲);
}
);
}
scheduleBuffers(拖缆:WebAudio拖缆){
streamer.isWorking=true;
if(streamer.context==未定义){
streamer.context=new(window.AudioContext)();
}
如果(streamer.isTraining&&streamer.numbers按计划尝试使用canplay和ended挂钩来播放和清理音频标签。我没有任何实际经验,因此我不知道它对这种实时敏感的声音播放有什么影响,但我无法想象它会与React状态批处理和信号器套接字数据交互一起工作。我我有在多个平台浏览器上移植Web Audio API和声音播放应用程序的经验:在一些有缺陷的平台上,通过将播放放在setTimeout调用中来解决播放问题。不过,我认为,将它们放在错误处理程序中会导致问题。我使用了Web Audio回退