4

我需要分层循环 .wav 轨道,最终我需要能够打开和关闭并保持同步。

首先,我加载轨道并停止BufferLoader将加载的轨道arraybuffer变成AudioBuffer(因此false

        function loadTracks(data) {
            for (var i = 0; i < data.length; i++) {
                trackUrls.push(data[i]['url']);
            };
            bufferLoader = new BufferLoader(context, trackUrls, finishedLoading);
            bufferLoader.load(false);
            return loaderDefered.promise;
        }

当您单击屏幕上的按钮时,它会调用startStop().

    function startStop(index, name, isPlaying) {
        if(!activeBuffer) {
            activeBuffer = bufferList[index];
        }else{
            activeBuffer = appendBuffer(activeBuffer, bufferList[index]);
        }
        context.decodeAudioData(activeBuffer, function(buffer){
            audioBuffer = buffer;
            play();
        })


    function play() {
        var scheduledTime = 0.015;
        try {
            audioSource.stop(scheduledTime);
        } catch (e) {}

        audioSource = context.createBufferSource();
        audioSource.buffer = audioBuffer;
        audioSource.loop = true;
        audioSource.connect(context.destination);
        var currentTime = context.currentTime + 0.010 || 0;
        audioSource.start(scheduledTime - 0.005, currentTime, audioBuffer.duration - currentTime);
        audioSource.playbackRate.value = 1;
    }

我在这个家伙 github 上找到的大部分代码。在演示中,您可以听到他正在对 AudioBuffers 进行分层。

在主机上尝试过同样的方法。

不管这些argularJS东西,网络音频的东西正在发生service.js在:

/js/angular/service.js 

如果您打开控制台并单击按钮,您可以看到activeBuffer.byteLength(ArrayBuffer 类型)正在递增,但是即使在通过该context.decodeAudioData方法解码后,它仍然只播放您单击的第一个声音,而不是合并的声音AudioBuffer

4

2 回答 2

6

我不确定我是否完全理解您的情况-您不希望这些同时播放吗?(即贝司在鼓的顶部分层)。

每当您点击该文件的按钮时,您当前的代码都会尝试连接其他音频文件。您不能只连接音频文件(以其 ENCODED 形式)然后通过 decode 运行它 - decodeAudioData 方法正在解码 arraybuffer 中的第一个完整声音,然后停止(因为它已经完成了对声音的解码)。

您应该做的是更改逻辑以连接来自结果 AudioBuffers 的缓冲区数据(见下文)。即使这个逻辑也不是你应该做的——这仍然是缓存编码的音频文件,并在你每次按下按钮时解码。相反,您应该缓存解码的音频缓冲区,然后将其连接起来。

function startStop(index, name, isPlaying) {

    // Note we're decoding just the new sound
    context.decodeAudioData( bufferList[index], function(buffer){
        // We have a decoded buffer - now we need to concatenate it
        audioBuffer = buffer;

        if(!audioBuffer) {
            audioBuffer = buffer;
        }else{
            audioBuffer = concatenateAudioBuffers(audioBuffer, buffer);
        }

        play();
    })
}

function concatenateAudioBuffers(buffer1, buffer2) {
    if (!buffer1 || !buffer2) {
        console.log("no buffers!");
        return null;
    }

    if (buffer1.numberOfChannels != buffer2.numberOfChannels) {
        console.log("number of channels is not the same!");
        return null;
    }

    if (buffer1.sampleRate != buffer2.sampleRate) {
        console.log("sample rates don't match!");
        return null;
    }

    var tmp = context.createBuffer(buffer1.numberOfChannels, buffer1.length + buffer2.length, buffer1.sampleRate);

    for (var i=0; i<tmp.numberOfChannels; i++) {
        var data = tmp.getChannelData(i);
        data.set(buffer1.getChannelData(i));
        data.set(buffer2.getChannelData(i),buffer1.length);
    }
    return tmp;
};
于 2013-09-20T15:18:14.517 回答
-1

解决了:

让多个相同持续时间的循环同时播放并保持同步,即使您随机启动和停止它们也是如此。

首先,创建所有缓冲区源,其中bufferList是一个数组,AudioBuffers第一个声音是您要从中读取并用其他声音覆盖的声音。

    function createAllBufferSources() {
        for (var i = 0; i < bufferList.length; i++) {
            var source = context.createBufferSource();
            source.buffer = bufferList[i];
            source.loop = true;
            bufferSources.push(source);
        };
        console.log(bufferSources)
    }

然后:

    function start() {
        var rewrite = bufferSources[0];
        rewrite.connect(context.destination);
        var processNode = context.createScriptProcessor(2048, 2, 2);
        rewrite.connect(processNode)
        processNode.onaudioprocess = function(e) {
                            //getting the left and right of the sound we want to overwrite
            var left = rewrite.buffer.getChannelData(0);
            var right = rewrite.buffer.getChannelData(1);
            var overL = [],
                overR = [],
                i, a, b, l;

            l = bufferList.length,
                            //storing all the loops channel data
            for (i = 0; i < l; i++) {
                    overL[i] = bufferList[i].getChannelData(0);
                    overR[i] = bufferList[i].getChannelData(1);
            }
                            //looping through the channel data of the sound we are going to overwrite
            a = 0, b = overL.length, l = left.length;
            for (i = 0; i < l; i++) {
                            //making sure its a blank before we start to write
                left[i] -= left[i];
                right[i] -= right[i];
                            //looping through all the sounds we want to add and assigning the bytes to the old sound, both at the same position
                for (a = 0; a < b; a++) {
                    left[i] += overL[a][i];
                    right[i] += overR[a][i];
                }
                left[i] /= b;
                right[i] /= b);
            }
        };

        processNode.connect(context.destination);
        rewrite.start(0)
    }

如果您从 bufferList 中删除 AudioBuffer 并在任何时候再次添加它,它将始终保持同步。

编辑:

请记住:-processor 节点会奇怪地收集垃圾。- 这很费力,可能要考虑以某种方式使用 WebWorkers

于 2013-09-24T13:27:58.877 回答