使用 Web Audio API 时的播放会在每个块的开头跳过
Playback when using Web Audio API skips at the beginning of every chunk
我一直在构建一个音乐应用程序,今天我终于开始尝试在其中播放音乐了。
作为我的环境设置方式的概述,我将音乐文件存储为 MP3,我已使用 GridFS 将其上传到 MongoDB 数据库中。然后,我使用 socket.io 服务器从 MongoDB 数据库下载块,并将它们作为单独的发射发送到前端,由网络音频 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);
});
任何帮助将不胜感激,谢谢!
As an outline of how my environment is set up, I am storing the music files as MP3s which I have uploaded into a MongoDB database using GridFS.
如果你愿意,你可以这样做,但现在我们有像 Minio 这样的工具,使用更常见的 APIs 可以使这更容易。
I then use a socket.io server to download the chunks from the MongoDB database and send them as individual emits to the front end
不要走这条路。 Web 套接字的开销或 Socket.IO 没有理由。正常的 HTTP 请求就可以了。
where the are processed by the Web Audio API and scheduled to play.
你不能用这种方式直播。网络音频 API 不支持有用的流式传输,除非您碰巧有原始 PCM 块,而您没有。
As far as I can tell, they are all scheduled right up next to each other so I can't find a reason why there should be any sort of gap or overlap between them.
有损编解码器不会为您提供 sample-accurate 输出。特别是对于 MP3,如果你给它一些任意数量的样本,你最终会得到至少一个完整的 MP3 帧(~576 个样本)输出。事实上,您需要在第一个音频帧之前提供数据才能正常工作。如果你想解码一个流,你需要一个流开始。你不能用这种方式独立解码 MP3。
幸运的是,该解决方案还简化了您的工作。只需 return 来自您服务器的 HTTP 流,并使用 HTML 音频元素 <audio>
或 new Audio(url)
。浏览器将处理所有缓冲。只要确保您的服务器处理范围请求,您就可以开始了。
我一直在构建一个音乐应用程序,今天我终于开始尝试在其中播放音乐了。
作为我的环境设置方式的概述,我将音乐文件存储为 MP3,我已使用 GridFS 将其上传到 MongoDB 数据库中。然后,我使用 socket.io 服务器从 MongoDB 数据库下载块,并将它们作为单独的发射发送到前端,由网络音频 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);
});
任何帮助将不胜感激,谢谢!
As an outline of how my environment is set up, I am storing the music files as MP3s which I have uploaded into a MongoDB database using GridFS.
如果你愿意,你可以这样做,但现在我们有像 Minio 这样的工具,使用更常见的 APIs 可以使这更容易。
I then use a socket.io server to download the chunks from the MongoDB database and send them as individual emits to the front end
不要走这条路。 Web 套接字的开销或 Socket.IO 没有理由。正常的 HTTP 请求就可以了。
where the are processed by the Web Audio API and scheduled to play.
你不能用这种方式直播。网络音频 API 不支持有用的流式传输,除非您碰巧有原始 PCM 块,而您没有。
As far as I can tell, they are all scheduled right up next to each other so I can't find a reason why there should be any sort of gap or overlap between them.
有损编解码器不会为您提供 sample-accurate 输出。特别是对于 MP3,如果你给它一些任意数量的样本,你最终会得到至少一个完整的 MP3 帧(~576 个样本)输出。事实上,您需要在第一个音频帧之前提供数据才能正常工作。如果你想解码一个流,你需要一个流开始。你不能用这种方式独立解码 MP3。
幸运的是,该解决方案还简化了您的工作。只需 return 来自您服务器的 HTTP 流,并使用 HTML 音频元素 <audio>
或 new Audio(url)
。浏览器将处理所有缓冲。只要确保您的服务器处理范围请求,您就可以开始了。