【问题标题】:How can i copy the audio data from a wavesurfer region into a new audio buffer?如何将来自 wavesurfer 区域的音频数据复制到新的音频缓冲区中?
【发布时间】:2020-12-22 22:35:23
【问题描述】:

Wavesurfer.js 很棒,但有点令人困惑。

我已经实现了 wavesurfer,但我想构建一种方法来剪切用户选择的音频区域并将其粘贴到新的音频缓冲区中。目前,我正在控制台登录区域 js 文件以尝试获取开始和结束时间,但变得非常困惑。

这是波形,我单击并拖动以创建区域。我希望能够运行将此部分复制到新音频缓冲区中的功能。

我已经在区域 JS 中登录控制台。

将其输出到控制台。

但是。当我将 region.start 或 region.end 打印到控制台时,它始终为 0 和 0.0800 .... 您可以在日志的预览中看到,但是一旦您展开查看更多实际区域的开始和结束,就会出现。我不知道为什么会这样。

此链接说明复制和粘贴整个文件。 Cut and Paste audio using web audio api and wavesurfer.js

有什么线索吗?我假设我需要获取开始和结束的数字,然后使用 ffmpeg 将其删除,或者如果有一种方法可以让我自己通过 Web Audio API 和 Wavesurfer 本身做到这一点,那将是最好的。

谢谢

【问题讨论】:

  • 以文本而不是图像的形式提供代码会更有用。

标签: node.js audio web-audio-api


【解决方案1】:

我对wavesurfer一无所知,但我可以根据您在记录对象或其值时看到不同值的困惑来猜测这个问题。

console.dir()console.log() 类似,我们可以使用它更方便地探究问题。

在控制台中试试这个:

var o = { n: 42 };
console.dir(o);
// -> now expand the logged object, you should see that n is 42
o.n = 43;
// notice that the previously logged object still states that n is 42

并将其与控制台中的此会话进行对比:

var o = { n: 42 };
console.dir(o);
// don't expand the logged object just yet...
o.n = 84;
// -> now expand the logged object, you should see that n is 84
o.n = 43;
// notice that the previously logged object still states that n is 84

由此可以看出,Chrome 在记录的对象第一次被扩展时捕获字典/对象的值,而不是之前。

根据您的报告,我猜测该区域对象是可变的,并且其中的值在记录语句之后会继续更改。

当您console.log(region.end) 时,您会立即捕获“end”值,而当您 console.log(region)...time-elapses...expand 时,您看到的值是在扩展对象时捕获的。

也许您需要仔细查看 wavesurfer 的 API。作为一个可怕的黑客,你可以以延迟的方式捕获值(使用超时或同样可怕的东西),但我想一定有更好的方法。

【讨论】:

    【解决方案2】:

    我已经实现了基于 wavesurfer 的剪切、复制、粘贴功能。剪切、复制是返回您想要剪切或复制的区域的函数。而 paste 函数会返回新形成的 audioBuffer。

     export function paste(instance,cutSelection){
        var offlineAudioContext = instance.backend.ac
        var originalAudioBuffer = instance.backend.buffer;
    
        let cursorPosition = instance.getCurrentTime()
        var newAudioBuffer = offlineAudioContext.createBuffer(
            originalAudioBuffer.numberOfChannels,
            originalAudioBuffer.length + cutSelection.length,
            originalAudioBuffer.sampleRate);
    
        for (var channel = 0; channel < originalAudioBuffer.numberOfChannels;channel++) {
    
            var new_channel_data = newAudioBuffer.getChannelData(channel);
            var empty_segment_data = cutSelection.getChannelData(channel);
            var original_channel_data = originalAudioBuffer.getChannelData(channel);
    
            var before_data = original_channel_data.subarray(0, cursorPosition * originalAudioBuffer.sampleRate);
            var mid_data = empty_segment_data
            var after_data = original_channel_data.subarray(Math.floor(cursorPosition * originalAudioBuffer.sampleRate), (originalAudioBuffer.length * originalAudioBuffer.sampleRate));
    
    
            new_channel_data.set(before_data);
            new_channel_data.set(mid_data,(cursorPosition * newAudioBuffer.sampleRate));
            new_channel_data.set(after_data,(cursorPosition + cutSelection.duration)* newAudioBuffer.sampleRate);
        }
        return newAudioBuffer
    }
    
    export function cut(params,instance){
        /*
        ---------------------------------------------
        The function will take the buffer used to create the waveform and will
        create
        a new blob with the selected area from the original blob using the
        offlineAudioContext
        */
    
        // var self = this;
        var start = params.start;
        var end = params.end;
    
        var originalAudioBuffer = instance.backend.buffer;
    
        var lengthInSamples = Math.floor( (end - start) * originalAudioBuffer.sampleRate );
        if (! window.OfflineAudioContext) {
            if (! window.webkitOfflineAudioContext) {
                // $('#output').append('failed : no audiocontext found, change browser');
                alert('webkit context not found')
            }
            window.OfflineAudioContext = window.webkitOfflineAudioContext;
        }
        // var offlineAudioContext = new OfflineAudioContext(1, 2,originalAudioBuffer.sampleRate );
        var offlineAudioContext = instance.backend.ac
    
        var emptySegment = offlineAudioContext.createBuffer(
            originalAudioBuffer.numberOfChannels,
            lengthInSamples,
            originalAudioBuffer.sampleRate );
    
        var newAudioBuffer = offlineAudioContext.createBuffer(
            originalAudioBuffer.numberOfChannels,
            (start === 0 ? (originalAudioBuffer.length - emptySegment.length) :originalAudioBuffer.length),
            originalAudioBuffer.sampleRate);
    
        for (var channel = 0; channel < originalAudioBuffer.numberOfChannels;channel++) {
    
            var new_channel_data = newAudioBuffer.getChannelData(channel);
            var empty_segment_data = emptySegment.getChannelData(channel);
            var original_channel_data = originalAudioBuffer.getChannelData(channel);
    
            var before_data = original_channel_data.subarray(0, start * originalAudioBuffer.sampleRate);
            var mid_data = original_channel_data.subarray( start * originalAudioBuffer.sampleRate, end * originalAudioBuffer.sampleRate);
            var after_data = original_channel_data.subarray(Math.floor(end * originalAudioBuffer.sampleRate), (originalAudioBuffer.length * originalAudioBuffer.sampleRate));
    
            empty_segment_data.set(mid_data);
            if(start > 0){
                new_channel_data.set(before_data);
                new_channel_data.set(after_data,(start * newAudioBuffer.sampleRate));
            } else {
                new_channel_data.set(after_data);
            }
        }
        return {
            newAudioBuffer,
            cutSelection:emptySegment
        }
    
    }
    
    export function copy(region, instance){
        var segmentDuration = region.end - region.start
    
        var originalBuffer = instance.backend.buffer;
        var emptySegment = instance.backend.ac.createBuffer(
            originalBuffer.numberOfChannels,
            segmentDuration * originalBuffer.sampleRate,
            originalBuffer.sampleRate
        );
        for (var i = 0; i < originalBuffer.numberOfChannels; i++) {
            var chanData = originalBuffer.getChannelData(i);
            var emptySegmentData = emptySegment.getChannelData(i);
            var mid_data = chanData.subarray( region.start * originalBuffer.sampleRate, region.end * originalBuffer.sampleRate);
            emptySegmentData.set(mid_data);
        }
    
        return emptySegment
    }
    
    export function bufferToWave(abuffer, offset, len) {
    
        var numOfChan = abuffer.numberOfChannels,
            length = len * numOfChan * 2 + 44,
            buffer = new ArrayBuffer(length),
            view = new DataView(buffer),
            channels = [], i, sample,
            pos = 0;
    
        // write WAVE header
        setUint32(0x46464952);                         // "RIFF"
        setUint32(length - 8);                         // file length - 8
        setUint32(0x45564157);                         // "WAVE"
    
        setUint32(0x20746d66);                         // "fmt " chunk
        setUint32(16);                                 // length = 16
        setUint16(1);                                  // PCM (uncompressed)
        setUint16(numOfChan);
        setUint32(abuffer.sampleRate);
        setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
        setUint16(numOfChan * 2);                      // block-align
        setUint16(16);                                 // 16-bit (hardcoded in this demo)
    
        setUint32(0x61746164);                         // "data" - chunk
        setUint32(length - pos - 4);                   // chunk length
    
        // write interleaved data
        for(i = 0; i < abuffer.numberOfChannels; i++)
            channels.push(abuffer.getChannelData(i));
    
        while(pos < length) {
            for(i = 0; i < numOfChan; i++) {             // interleave channels
                sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
                sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0; // scale to 16-bit signed int
                view.setInt16(pos, sample, true);          // update data chunk
                pos += 2;
            }
            offset++                                     // next source sample
        }
    
        // create Blob
        return new Blob([buffer], {type: "audio/mpeg"});
    
        function setUint16(data) {
            view.setUint16(pos, data, true);
            pos += 2;
        }
    
        function setUint32(data) {
            view.setUint32(pos, data, true);
            pos += 4;
        }
    }
    

    你可以调查一下。 https://github.com/vikasmagar512/wavesurfer-audio-editor

    【讨论】:

      【解决方案3】:

      使用

      this.wavesurfer.on("region-update-end", reg => {
      //this will allow you to see the start date correctly, 
      //even if you dragged the region it will display right.
          console.log(reg.start)
          
        });
      

      这仅在区域创建的函数完成它的工作时才有效,因此它已填满所有变量,现在您可以轻松访问它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-14
        • 2011-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-21
        相关资源
        最近更新 更多