Javascript 使用Web音频API时,播放会在每个块的开头跳过
我一直在开发一个音乐应用程序,今天我终于开始尝试在其中播放音乐 作为对我的环境设置的概述,我将音乐文件存储为MP3,并使用GridFS上传到MongoDB数据库中。然后,我使用一个socket.io服务器从MongoDB数据库下载块,并将它们作为单独的发射发送到前端,由Web Audio API处理并安排播放 当他们玩的时候,他们的顺序都是正确的,但是每次都有一个很小的小故障或是在同一个地方跳过(大概是在块之间),我似乎无法摆脱。据我所知,它们都排在一起,所以我找不到它们之间有任何间隙或重叠的原因。任何帮助都将不胜感激。代码如下: 插座布线Javascript 使用Web音频API时,播放会在每个块的开头跳过,javascript,web-audio-api,Javascript,Web Audio Api,我一直在开发一个音乐应用程序,今天我终于开始尝试在其中播放音乐 作为对我的环境设置的概述,我将音乐文件存储为MP3,并使用GridFS上传到MongoDB数据库中。然后,我使用一个socket.io服务器从MongoDB数据库下载块,并将它们作为单独的发射发送到前端,由Web Audio API处理并安排播放 当他们玩的时候,他们的顺序都是正确的,但是每次都有一个很小的小故障或是在同一个地方跳过(大概是在块之间),我似乎无法摆脱。据我所知,它们都排在一起,所以我找不到它们之间有任何间隙或重叠的原
socket.on('stream-audio', () => {
db.client.db("dev").collection('music.files').findOne({"metadata.songId": "3"}).then((result) =>{
const bucket = new GridFSBucket(db.client.db("dev"), {
bucketName: "music"
});
bucket.openDownloadStream(result._id).on('data',(chunk) => {
socket.emit('audio-chunk',chunk)
});
});
});
前端
//These variable are declared as object variables, hence all of the "this" keywords
context: new (window.AudioContext || window.webkitAudioContext)(),
freeTime: null,
numChunks: 0,
chunkTracker: [],
...
this.socket.on('audio-chunk', (chunk) => {
//Keeping track of chunk decoding status so that they don't get scheduled out of order
const chunkId = this.numChunks
this.chunkTracker.push({
id: chunkId,
complete: false,
});
this.numChunks += 1;
//Callback to the decodeAudioData function
const decodeCallback = (buffer) => {
var shouldExecute = false;
const trackIndex = this.chunkTracker.map((e) => e.id).indexOf(chunkId);
//Checking if either it's the first chunk or the previous chunk has completed
if(trackIndex !== 0){
const prevChunk = this.chunkTracker.filter((e) => e.id === (chunkId-1))
if (prevChunk[0].complete) {
shouldExecute = true;
}
} else {
shouldExecute = true;
}
//THIS IS THE ACTUAL WEB AUDIO API STUFF
if (shouldExecute) {
if (this.freeTime === null) {
this.freeTime = this.context.currentTime
}
const source = this.context.createBufferSource();
source.buffer = buffer
source.connect(this.context.destination)
if (this.context.currentTime >= this.freeTime){
source.start()
this.freeTime = this.context.currentTime + buffer.duration
} else {
source.start(this.freeTime)
this.freeTime += buffer.duration
}
//Update the tracker of the chunks that this one is complete
this.chunkTracker[trackIndex] = {id: chunkId, complete: true}
} else {
//If the previous chunk hasn't processed yet, check again in 50ms
setTimeout((passBuffer) => {
decodeCallback(passBuffer)
},50,buffer);
}
}
decodeCallback.bind(this);
this.context.decodeAudioData(chunk,decodeCallback);
});
任何帮助都将不胜感激,谢谢
作为对我的环境设置的概述,我将音乐文件存储为MP3,并使用GridFS上传到MongoDB数据库中
如果你愿意,你可以这样做,但是现在我们有了像Minio这样的工具,可以使用更常见的API使这变得更容易
然后,我使用一个socket.io服务器从MongoDB数据库下载数据块,并将它们作为单个发射发送到前端
不要走这条路。没有理由要增加web套接字或Socket.IO的开销。正常的HTTP请求就可以了
其中,网络音频API处理并计划播放
你不能这样流。Web音频API不支持有用的流,除非您碰巧有原始PCM块,而您没有
据我所知,它们都排在一起,所以我找不到它们之间有任何间隙或重叠的原因
有损编解码器不会为您提供精确的样本输出。特别是对于MP3,如果你给它一些任意数量的样本,你将得到至少一个完整的MP3帧(~576个样本)输出。现实情况是,您需要在第一个音频帧之前提供数据,以便它正常工作。如果你想解码一个流,你需要一个流开始。你不能用这种方式独立解码MP3
幸运的是,该解决方案还简化了您的工作。只需从服务器返回HTTP流,并使用HTML音频元素
或新音频(url)
。浏览器将处理所有缓冲。只要确保您的服务器处理范围请求,您就可以开始了。谢谢您提供的所有信息!我昨天才开始学习如何做这一点,所以我知道可能有些事情我需要改变。这回答了我的问题,至少现在是这样,但要明确的是,跳过的原因是有损格式?出于好奇,如果我使用像WAV这样的格式,你认为可以解决这个问题吗?@matthauf从技术上讲,你仍然无法以样本精确的方式安排时间。。。您只能设置一个非常精确的时间戳,并希望它能够取整到您想要的正确样本。如果原始浮点PCM样本的采样率正确,则可以使用ScriptProcessorNode播放它们。当然,您还必须自己处理所有缓冲和其他逻辑。这种方法有一些用例,但你的听起来不像是其中之一。是的,我知道我只是好奇。谢谢你的帮助,我已经实现了你的解决方案,而且它更简单,花费了十分之一的时间。@Matthauf很酷,很高兴你发现它有帮助!另外,欢迎使用堆栈溢出!如果您愿意,您可以单击答案旁边的复选标记,将其标记为“已接受”,这将使未来遇到您的问题的人更容易找到。