【问题标题】:Is there a good way to play raw wav data in the browser?有没有在浏览器中播放原始 wav 数据的好方法?
【发布时间】:2020-08-26 00:39:51
【问题描述】:

我正在尝试在浏览器中播放wav原始数据,它是从nodejs服务器通过socket.io传输的,主要想法是尽快播放接收数据,所以我不需要等到接收数据完成。我尝试在这种情况下使用 blob,但总是会发生下一件事:如果我有一大块原始数据并且我正在使用 blob 转换它以进行播放,那么当第二个块准备好并再次使用 blob 转换时,然后播放音频时中断了一会儿。

我也尝试使用下面的代码并且它有效,但是我在播放它时会听到很多噪音。谁能帮我解决这个问题并告诉我如何在没有噪音的情况下播放音频?

附:传输中的超时是主要思想的一部分,我尝试在没有它的情况下播放音乐,但仍然没有运气。

<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);
}

【问题讨论】:

    标签: javascript node.js socket.io blob web-audio-api


    【解决方案1】:

    下面有一个 WAV 示例。由于带宽和延迟优势,我强烈建议考虑将音频编码为 Opus 而不是 WAV。

    https://fetch-stream-audio.anthum.com ::  (GitHub)

    【讨论】:

    • 感谢您的帮助,但这有点不适合我的情况
    【解决方案2】:

    我自己找到了我需要的解决方案。 此代码可帮助您通过 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      • 2011-11-06
      • 1970-01-01
      相关资源
      最近更新 更多