我正在尝试在浏览器中播放wav原始数据,该数据是通过socket.io从nodejs服务器传输的,主要思想是尽快播放接收的数据,因此我不需要等到接收到数据完成。在这种情况下,我尝试使用blob,但总是会发生以下情况:如果我有一块原始数据,并且正在使用blob对其进行转换以进行播放,那么当第二块准备就绪并再次使用blob进行转换时,在播放音频时会中断一段时间。
[我也尝试使用下面的代码,它已经起作用了,但是在播放它时我听到很多杂音。谁能帮我解决这个问题,并告诉我如何播放没有噪音的音频?
P.S。传输中的超时是主要思想的一部分,我尝试在没有它的情况下播放音乐,但仍然没有运气。
<html>
<head>
<script src="socket.io.js"></script>
<script src=" https://cdnjs.cloudflare.com/ajax/libs/socket.io-stream/0.9.1/socket.io-stream.js">
</script>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
</head>
<body>
<button id="start">Play music!</button>
<script>
$(function () {
var socket = io.connect("http://localhost:3000");
var stream = ss.createStream();
// receive data
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
var audioBuffer = audioCtx.createBuffer(2, 22050, 44100);
var source = audioCtx.createBufferSource();
source.buffer = audioBuffer;
// source.connect(audioCtx.destination);
gainNode = audioCtx.createGain();
gainNode.gain.value = 0;
gainNode.connect(audioCtx.destination);
source.connect(gainNode);
source.start(0);
socket.on('chat message', function (msg) {
console.log(".");
console.log(msg);
playsound(msg)
});
function playsound(raw) {
var context = audioCtx;
var buffer = new Int16Array(raw);
console.log(buffer);
var channel0buffer = new Float32Array(buffer.length / 2);
var channel1buffer = new Float32Array(buffer.length / 2);
var j = 0;
for (var i = 0; i < buffer.length; i++) {
channel0buffer[j] = buffer[i];
i++;
channel1buffer[j] = buffer[i];
j++;
}
var src = context.createBufferSource(),
audioBuffer = context.createBuffer(2, buffer.length, context.sampleRate);
audioBuffer.getChannelData(0).set(channel1buffer);
audioBuffer.getChannelData(1).set(channel0buffer);
src.buffer = audioBuffer;
src.connect(gainNode);
src.start(0);
}
document.getElementById("start").addEventListener('click', function () {
audioCtx.resume().then(() => {
console.log('Playback resumed successfully');
});
});
});
</script>
<p>Volume</p>
<input id="volume" type="range" min="0" max="1" step="0.1" value="0.0" />
<script>
document.getElementById('volume').addEventListener('change', function () {
gainNode.gain.value = this.value;
});
function touchStarted() {
getAudioContext().resume();
}
</script>
</body>
</html>
我的服务器代码如下:
const fs = require("fs");
var app = require("express")();
var http = require("http").createServer(app);
var io = require("socket.io")(http);
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
io.on("connection", (socket) => {
console.log("a user connected");
socket.on("disconnect", () => {
console.log("user disconnected");
});
socket.on("chat message", (msg) => {
console.log("message: " + msg);
io.emit("chat message", msg);
});
});
http.listen(3000, () => {
console.log("listening on *:3000");
});
const stream = fs.createReadStream("Original1.wav", {
highWaterMark: 640000, // internal buffer size
});
stream.on("data", async (chunk) => {
stream.pause();
let newChunk = new Int16Array(chunk);
await asyncHandle(chunk);
setTimeout(() => {
stream.resume();
}, 1000);
});
async function asyncHandle(chunk) {
console.log(chunk);
io.emit("chat message", chunk);
}
下面有一个WAV示例。我强烈建议您考虑将音频编码为Opus而不是WAV,因为它具有带宽和延迟优势。
我自己找到了需要的解决方案。此代码可帮助您通过socket.io和Web Audio Api播放所有wav文件,而不会出现任何问题。
客户端:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test</title>
<script src="socket.io.js"></script>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var socket = io.connect("http://localhost:3000");
var channels = 1;
let subcounter = 0;
let audiobuffer = [];
socket.on('chat message', function (msg) {
audiobuffer.push(new Uint16Array(msg));
console.log(".");
console.log(msg);
});
function play(soundName) {
var frameCount = audiobuffer[subcounter].length;
var myAudioBuffer = audioCtx.createBuffer(channels, frameCount, 22050);
for (var channel = 0; channel < channels; channel++) {
var nowBuffering = myAudioBuffer.getChannelData(channel);
for (var i = 0; i < frameCount; i++) {
// audio needs to be in [-1.0; 1.0]
var word = audiobuffer[subcounter][i];
nowBuffering[i] = ((word + 32768) % 65536 - 32768) / 32768.0;
}
}
subcounter += 1;
var source = audioCtx.createBufferSource();
source.buffer = myAudioBuffer;
source.connect(audioCtx.destination);
source.onended = play;
source.start();
}
</script>
</head>
<div>
<button id="play-sinewave" type="button" onclick="play('sinewave')">Play sound 'sinewave'</button>
</div>
</body>
</html>
服务器端:
const fs = require("fs");
var app = require("express")();
var http = require("http").createServer(app);
var io = require("socket.io")(http);
var cors = require("cors");
app.use(cors());
io.on("connection", (socket) => {
console.log("a user connected");
socket.on("disconnect", () => {
console.log("user disconnected");
});
socket.on("chat message", (msg) => {
console.log("message: " + msg);
io.emit("chat message", msg);
});
});
http.listen(3000, () => {
console.log("listening on *:3000");
});
const stream = fs.createReadStream("Original122050.wav", {
highWaterMark: 128000, // internal buffer size
});
let flag = true;
stream.on("data", async (chunk) => {
stream.pause();
setTimeout(async () => {
await asyncHandle(chunk);
flag = false;
stream.resume();
}, 1000); // I need this timeout because of my reasons, if u don't need it, just remove.
});
async function asyncHandle(chunk) {
if (flag) {
//U may have another header size.
chunk = chunk.slice(206); // Because I don't wanna play header as music!
}
console.log(chunk);
io.emit("chat message", chunk);
}
希望它会帮助任何人!有关流式pcm数据的更多信息,您可能会发现,这要感谢以下链接的注释部分的好心人,此处Play PCM with javascript