You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Wavesurfer.js仅展示音视频的特定时间段波形?

实现WaveSurfer.js仅展示特定时间段波形

当然可行,你现有代码的问题在于仅通过缩放和定位播放位置,并没有真正限制波形的渲染范围或视图显示范围。下面提供两种可靠的实现方式:


方式一:仅限制视图显示(不修改原始音频数据)

这种方式会加载完整的音频波形,但通过视图缩放和滚动,让容器只显示指定时间段的内容,同时禁止用户查看其他区域(适合不需要处理音频数据,仅展示特定段波形的场景)。

修改你的JS代码如下:

// 目标时间段:10秒到20秒
const startPoint = 10;
const endPoint = 20;
const segmentDuration = endPoint - startPoint;

const container = document.getElementById('waveform');
const wavesurfer = WaveSurfer.create({
    container: container,
    waveColor: 'blue',
    progressColor: 'gray',
    height: 100,
    responsive: true,
    interact: false, // 禁止用户交互
    cursorWidth: 0, // 隐藏光标
});

const videoSrc = jQuery('#video-element').find('source').attr('src');
wavesurfer.load(videoSrc);

wavesurfer.on('ready', function() {
    const duration = wavesurfer.getDuration();
    // 计算缩放级别:让目标时间段刚好填满容器宽度
    const containerWidth = container.offsetWidth;
    const pxPerSecond = containerWidth / segmentDuration;
    
    // 设置缩放,使目标段占满容器
    wavesurfer.zoom(pxPerSecond);
    // 滚动到起始点的位置(计算起始点对应的像素偏移)
    const scrollOffset = startPoint * pxPerSecond;
    wavesurfer.scrollTo(scrollOffset);
});

方式二:仅渲染特定时间段的波形数据(真正截取音频段)

这种方式会先截取音频的指定时间段,生成新的音频Blob,再加载给WaveSurfer,这样波形只会渲染你需要的那段(适合需要完全隔离其他音频数据的场景)。

const startPoint = 10;
const endPoint = 20;
const container = document.getElementById('waveform');

const wavesurfer = WaveSurfer.create({
    container: container,
    waveColor: 'blue',
    progressColor: 'gray',
    height: 100,
    responsive: true,
    interact: false,
});

// 截取音频指定时间段的函数
async function trimAudio(src, start, end) {
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();
    const response = await fetch(src);
    const arrayBuffer = await response.arrayBuffer();
    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
    
    // 计算起始和结束的样本位置
    const startSample = start * audioBuffer.sampleRate;
    const endSample = end * audioBuffer.sampleRate;
    const durationSample = endSample - startSample;
    
    // 创建新的音频Buffer
    const trimmedBuffer = audioContext.createBuffer(
        audioBuffer.numberOfChannels,
        durationSample,
        audioBuffer.sampleRate
    );
    
    // 复制目标时间段的音频数据
    for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
        trimmedBuffer.copyToChannel(
            audioBuffer.getChannelData(channel).subarray(startSample, endSample),
            channel
        );
    }
    
    // 转换为Blob
    const wav = audioBufferToWav(trimmedBuffer);
    return new Blob([wav], { type: 'audio/wav' });
}

// 将AudioBuffer转换为WAV格式的函数
function audioBufferToWav(buffer) {
    const length = buffer.length * buffer.numberOfChannels * 2 + 44;
    const arrayBuffer = new ArrayBuffer(length);
    const view = new DataView(arrayBuffer);
    const channels = [];
    let offset = 0;
    let pos = 0;

    // 写入WAV头
    const setUint16 = (data) => {
        view.setUint16(pos, data, true);
        pos += 2;
    };
    const setUint32 = (data) => {
        view.setUint32(pos, data, true);
        pos += 4;
    };

    setUint32(0x46464952); // "RIFF"
    setUint32(length - 8); // 文件长度
    setUint32(0x45564157); // "WAVE"
    setUint32(0x20746d66); // "fmt "
    setUint32(16); // PCM格式大小
    setUint16(1); // PCM格式
    setUint16(buffer.numberOfChannels);
    setUint32(buffer.sampleRate);
    setUint32(buffer.sampleRate * 2 * buffer.numberOfChannels); // 比特率
    setUint16(buffer.numberOfChannels * 2); // 块对齐
    setUint16(16); // 比特深度

    setUint32(0x61746164); // "data"
    setUint32(length - pos - 4); // 数据长度

    // 写入音频数据
    for (let i = 0; i < buffer.numberOfChannels; i++) {
        channels.push(buffer.getChannelData(i));
    }

    while (pos < length) {
        for (let i = 0; i < buffer.numberOfChannels; i++) {
            let sample = Math.max(-1, Math.min(1, channels[i][offset]));
            sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
            view.setInt16(pos, sample, true);
            pos += 2;
        }
        offset++;
    }

    return arrayBuffer;
}

// 加载并渲染截取后的音频波形
(async function() {
    const videoSrc = jQuery('#video-element').find('source').attr('src');
    const trimmedBlob = await trimAudio(videoSrc, startPoint, endPoint);
    wavesurfer.loadBlob(trimmedBlob);
})();

说明

  • 方式一实现简单,适合快速展示特定段视图,但底层还是加载了完整音频数据。
  • 方式二更彻底,仅加载目标时间段的音频,生成的波形也只包含该段,但需要处理音频格式转换,代码稍复杂。

内容的提问来源于stack exchange,提问作者CRAIG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 02:23:17