如何使用网络音频API无缝循环声音

问题描述 投票:0回答:2

我在任何地方都找不到这个问题的明确答案。我正在寻找在 chrome 中加载文档时自动无缝循环 .wav 文件的最简单方法。看来 webaudio api 是最佳实践,但我找不到简单的文档。对 safari 和其他方面的支持也很好,但没那么重要。

我查看了 w3.org 示例,但没有帮助

我认为除了 on.click 按钮之外,这是最接近我想要的:https://forestmist.org/blog/web-audio-api-loops/

在这里,我为自己的音频实现了森林雾,它在 safari 中完美运行,但在 chrome 中停止:http://infinitelimitations.us/mess-motion/web-audio-api-loops-demo/index.html

这是该页面的源代码:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Web Audio API Loops Demo</title>
</head>
<body>

    <form>
        <button id="button-loop-1" type="button" value="1">Loop 1</button>
    </form>

    <script>
    //--------------
    // Audio Object
    //--------------
    var audio = {
        buffer: {},
        compatibility: {},
        files: [
            'hoodie_robot_clipped.wav',
            'beat.wav'
        ],
        proceed: true,
        source_loop: {},

    };

    //-----------------
    // Audio Functions
    //-----------------
    audio.findSync = function(n) {
        var first = 0,
            current = 0,
            offset = 0;

        // Find the audio source with the earliest startTime to sync all others to
        for (var i in audio.source_loop) {
            current = audio.source_loop[i]._startTime;
            if (current > 0) {
                if (current < first || first === 0) {
                    first = current;
                }
            }
        }

        if (audio.context.currentTime > first) {
            offset = (audio.context.currentTime - first) % audio.buffer[n].duration;
        }

        return offset;
    };

    audio.play = function(n) {
        if (audio.source_loop[n]._playing) {
            audio.stop(n);
        } else {
            audio.source_loop[n] = audio.context.createBufferSource();
            audio.source_loop[n].buffer = audio.buffer[n];
            audio.source_loop[n].loop = true;
            audio.source_loop[n].connect(audio.context.destination);

            var offset = audio.findSync(n);
            audio.source_loop[n]._startTime = audio.context.currentTime;

            if (audio.compatibility.start === 'noteOn') {
                /*
                The depreciated noteOn() function does not support offsets.
                Compensate by using noteGrainOn() with an offset to play once and then schedule a noteOn() call to loop after that.
                */
                audio.source_once[n] = audio.context.createBufferSource();
                audio.source_once[n].buffer = audio.buffer[n];
                audio.source_once[n].connect(audio.context.destination);
                audio.source_once[n].noteGrainOn(0, offset, audio.buffer[n].duration - offset); // currentTime, offset, duration
                /*
                Note about the third parameter of noteGrainOn().
                If your sound is 10 seconds long, your offset 5 and duration 5 then you'll get what you expect.
                If your sound is 10 seconds long, your offset 5 and duration 10 then the sound will play from the start instead of the offset.
                */

                // Now queue up our looping sound to start immediatly after the source_once audio plays.
                audio.source_loop[n][audio.compatibility.start](audio.context.currentTime + (audio.buffer[n].duration - offset));
            } else {
                audio.source_loop[n][audio.compatibility.start](0, offset);
            }

            audio.source_loop[n]._playing = true;
        }
    };

    audio.stop = function(n) {
        if (audio.source_loop[n]._playing) {
            audio.source_loop[n][audio.compatibility.stop](0);
            audio.source_loop[n]._playing = false;
            audio.source_loop[n]._startTime = 0;
            if (audio.compatibility.start === 'noteOn') {
                audio.source_once[n][audio.compatibility.stop](0);
            }
        }
    };

    //-----------------------------
    // Check Web Audio API Support
    //-----------------------------
    try {
        // More info at http://caniuse.com/#feat=audio-api
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        audio.context = new window.AudioContext();
    } catch(e) {
        audio.proceed = false;
        alert('Web Audio API not supported in this browser.');
    }

    if (audio.proceed) {
        //---------------
        // Compatibility
        //---------------
        (function() {
            var start = 'start',
                stop = 'stop',
                buffer = audio.context.createBufferSource();

            if (typeof buffer.start !== 'function') {
                start = 'noteOn';
            }
            audio.compatibility.start = start;

            if (typeof buffer.stop !== 'function') {
                stop = 'noteOff';
            }
            audio.compatibility.stop = stop;
        })();

        //-------------------------------
        // Setup Audio Files and Buttons
        //-------------------------------
        for (var a in audio.files) {
            (function() {
                var i = parseInt(a) + 1;
                var req = new XMLHttpRequest();
                req.open('GET', audio.files[i - 1], true); // array starts with 0 hence the -1
                req.responseType = 'arraybuffer';
                req.onload = function() {
                    audio.context.decodeAudioData(
                        req.response,
                        function(buffer) {
                            audio.buffer[i] = buffer;
                            audio.source_loop[i] = {};
                            var button = document.getElementById('button-loop-' + i);
                            button.addEventListener('click', function(e) {
                                e.preventDefault();
                                audio.play(this.value);
                            });
                        },
                        function() {
                            console.log('Error decoding audio "' + audio.files[i - 1] + '".');
                        }
                    );
                };
                req.send();
            })();
        }
    }
    </script>

</body>
</html>
google-chrome loops safari web-audio-api
2个回答
25
投票

花了一个晚上的研究,但我终于找到了最简单的解决方案:

<script type="text/javascript">
    //this is the webaudio loooooppppppp
    //enter url in the next line
    var url  = 'hoodie_robot_clipped.wav';

    /* --- set up web audio --- */
    //create the context
    var context = new AudioContext();
    //...and the source
    var source = context.createBufferSource();
    //connect it to the destination so you can hear it.
    source.connect(context.destination);

    /* --- load buffer ---  */
    var request = new XMLHttpRequest();
    //open the request
    request.open('GET', url, true); 
    //webaudio paramaters
    request.responseType = 'arraybuffer';
    //Once the request has completed... do this
    request.onload = function() {
        context.decodeAudioData(request.response, function(response) {
            /* --- play the sound AFTER the buffer loaded --- */
            //set the buffer to the response we just received.
            source.buffer = response;
            //start(0) should play asap.
            source.start(0);
            source.loop = true;
        }, function () { console.error('The request failed.'); } );
    }
    //Now that the request has been defined, actually make the request. (send it)
    request.send();
</script>

0
投票

以下是我的音频播放器插件的播放方法的部分代码。

loopStart = 0
表示循环将从音频开头开始。 我们的要求之一是在播放过程中将音频循环 n 次。因此,我在
this.src.stop(this.ac.currentTime + this.delay + (this.getDuration() * this.loopCount));
之后调用
src.start
,以便音频在完成 n 次循环后停止。

                    this.createSource();
                    this.src.connect(this.masterGain);
                    if(this.isLooping){
                        this.src.loop = true;
                        this.src.loopStart = 0;
                    }
                    this.startSrc(start);
                    this.src.stop(this.ac.currentTime + this.delay + (this.getDuration() * this.loopCount));
                    this.savePlayPosition();
                    this.fireEvent('playing', this.name);
© www.soinside.com 2019 - 2024. All rights reserved.