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

如何从音频numpy数组中提取指定偏移量和时长的片段?

从已加载的音频numpy数组中提取指定区间

我使用audiofile库(底层依赖soundfile)处理音频,已将完整音频保存为numpy数组。希望实现类似audiofile.read()的功能:传入偏移量(offset)和时长(duration,单位秒),返回对应音频区间的numpy数组,但无需读取.wav文件,直接从已加载的numpy数组中提取。

尝试复制audiofile.read()中计算起止位置的逻辑对numpy数组执行切片,但未成功。所有加载的文件均为原96KHz重采样至16KHz后保存的numpy数组,求解决方法。

现有尝试代码

from audiofile.core.utils import duration_in_seconds
import audmath

def read_from_np(
    file,
    duration,
    offset,
    sampling_rate = 16000
):

    if duration is not None:
        duration = duration_in_seconds(duration, sampling_rate)
        if np.isnan(duration):
            duration = None
    if offset is not None and offset != 0:
        offset = duration_in_seconds(offset, sampling_rate)
        if np.isnan(offset):
            offset = None

    # Support for negative offset/duration values
    # by counting them from end of signal
    #
    if offset is not None and offset < 0 or duration is not None and duration < 0:
        # Import duration here to avoid circular imports
        from audiofile.core.info import duration as get_duration

        signal_duration = get_duration(file)
    # offset | duration
    # None   | < 0
    if offset is None and duration is not None and duration < 0:
        offset = max([0, signal_duration + duration])
        duration = None
    # None   | >= 0
    if offset is None and duration is not None and duration >= 0:
        if np.isinf(duration):
            duration = None
    # >= 0   | < 0
    elif offset is not None and offset >= 0 and duration is not None and duration < 0:
        if np.isinf(offset) and np.isinf(duration):
            offset = 0
            duration = None
        elif np.isinf(offset):
            duration = 0
        else:
            if np.isinf(duration):
                offset = min([offset, signal_duration])
                duration = np.sign(duration) * signal_duration
            orig_offset = offset
            offset = max([0, offset + duration])
            duration = min([-duration, orig_offset])
    # >= 0   | >= 0
    elif offset is not None and offset >= 0 and duration is not None and duration >= 0:
        if np.isinf(offset):
            duration = 0
        elif np.isinf(duration):
            duration = None
    # < 0    | None
    elif offset is not None and offset < 0 and duration is None:
        offset = max([0, signal_duration + offset])
    # >= 0    | None
    elif offset is not None and offset >= 0 and duration is None:
        if np.isinf(offset):
            duration = 0
    # < 0    | > 0
    elif offset is not None and offset < 0 and duration is not None and duration > 0:
        if np.isinf(offset) and np.isinf(duration):
            offset = 0
            duration = None
        elif np.isinf(offset):
            duration = 0
        elif np.isinf(duration):
            duration = None
        else:
            offset = signal_duration + offset
            if offset < 0:
                duration = max([0, duration + offset])
            else:
                duration = min([duration, signal_duration - offset])
            offset = max([0, offset])
    # < 0    | < 0
    elif offset is not None and offset < 0 and duration is not None and duration < 0:
        if np.isinf(offset):
            duration = 0
        elif np.isinf(duration):
            duration = -signal_duration
        else:
            orig_offset = offset
            offset = max([0, signal_duration + offset + duration])
            duration = min([-duration, signal_duration + orig_offset])
            duration = max([0, duration])

    # Convert to samples
    #
    # Handle duration first
    # and returned immediately
    # if duration == 0
    if duration is not None and duration != 0:
        duration = audmath.samples(duration, sampling_rate)
    if duration == 0:
        from audiofile.core.info import channels as get_channels

        channels = get_channels(file)
        if channels > 1 or always_2d:
            signal = np.zeros((channels, 0))
        else:
            signal = np.zeros((0,))
        return signal, sampling_rate
    if offset is not None and offset != 0:
        offset = audmath.samples(offset, sampling_rate)
    else:
        offset = 0


    start = offset
    # duration == 0 is handled further above with immediate return
    if duration is not None:
        stop = duration + start

    return np.expand_dims(file[0, start:stop], 0)

修复后的代码

from audiofile.core.utils import duration_in_seconds
import audmath
import numpy as np

def read_from_np(
    audio_array,
    duration=None,
    offset=None,
    sampling_rate=16000
):
    # 确保传入的numpy数组格式正确:(通道数, 采样点数)
    if len(audio_array.shape) != 2:
        raise ValueError("音频数组需为二维格式:(通道数, 采样点数)")
    
    total_samples = audio_array.shape[1]
    signal_duration = total_samples / sampling_rate

    # 处理duration参数
    if duration is not None:
        duration = duration_in_seconds(duration, sampling_rate)
        if np.isnan(duration):
            duration = None
    
    # 处理offset参数
    if offset is not None and offset != 0:
        offset = duration_in_seconds(offset, sampling_rate)
        if np.isnan(offset):
            offset = None

    # 处理负偏移/时长(从音频末尾计数)
    if offset is not None and offset < 0 or duration is not None and duration < 0:
        # offset | duration
        # None   | < 0
        if offset is None and duration is not None and duration < 0:
            offset = max(0, signal_duration + duration)
            duration = None
        # >= 0   | < 0
        elif offset is not None and offset >= 0 and duration is not None and duration < 0:
            if np.isinf(offset) and np.isinf(duration):
                offset = 0
                duration = None
            elif np.isinf(offset):
                duration = 0
            else:
                if np.isinf(duration):
                    offset = min(offset, signal_duration)
                    duration = np.sign(duration) * signal_duration
                orig_offset = offset
                offset = max(0, offset + duration)
                duration = min(-duration, orig_offset)
        # < 0    | None
        elif offset is not None and offset < 0 and duration is None:
            offset = max(0, signal_duration + offset)
        # < 0    | > 0
        elif offset is not None and offset < 0 and duration is not None and duration > 0:
            if np.isinf(offset) and np.isinf(duration):
                offset = 0
                duration = None
            elif np.isinf(offset):
                duration = 0
            elif np.isinf(duration):
                duration = None
            else:
                offset = signal_duration + offset
                if offset < 0:
                    duration = max(0, duration + offset)
                else:
                    duration = min(duration, signal_duration - offset)
                offset = max(0, offset)
        # < 0    | < 0
        elif offset is not None and offset < 0 and duration is not None and duration < 0:
            if np.isinf(offset):
                duration = 0
            elif np.isinf(duration):
                duration = -signal_duration
            else:
                orig_offset = offset
                offset = max(0, signal_duration + offset + duration)
                duration = min(-duration, signal_duration + orig_offset)
                duration = max(0, duration)
    else:
        # 处理非负参数的特殊情况
        if offset is not None and offset >= 0 and duration is None:
            if np.isinf(offset):
                duration = 0
        if offset is None and duration is not None and duration >= 0:
            if np.isinf(duration):
                duration = None
        if offset is not None and offset >= 0 and duration is not None and duration >= 0:
            if np.isinf(offset):
                duration = 0
            elif np.isinf(duration):
                duration = None

    # 处理时长为0的情况
    if duration == 0:
        channels = audio_array.shape[0]
        signal = np.zeros((channels, 0))
        return signal, sampling_rate

    # 转换为采样点索引
    start_sample = int(audmath.samples(offset, sampling_rate)) if offset is not None else 0
    if duration is not None:
        duration_samples = int(audmath.samples(duration, sampling_rate))
        end_sample = start_sample + duration_samples
    else:
        end_sample = total_samples

    # 确保索引在有效范围内
    start_sample = max(0, start_sample)
    end_sample = min(total_samples, end_sample)

    # 如果起始索引大于等于结束索引,返回空数组
    if start_sample >= end_sample:
        channels = audio_array.shape[0]
        return np.zeros((channels, 0)), sampling_rate

    # 提取切片并返回
    return audio_array[:, start_sample:end_sample], sampling_rate

关键修复说明

  1. 移除文件依赖函数:原代码使用get_duration和get_channels读取音频文件信息,现直接从numpy数组的形状计算:
    • 音频总时长:signal_duration = audio_array.shape[1] / sampling_rate
    • 通道数:audio_array.shape[0]
  2. 补全缺失依赖:添加numpy导入,修复原代码中未定义的always_2d变量问题
  3. 索引边界检查:确保采样点索引在数组有效范围内,避免切片越界
  4. 格式统一:假设输入数组为(通道数, 采样点数)的标准格式,符合audiofile返回的数组格式
  5. 异常处理:添加数组格式检查,避免传入错误维度的数组

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:02:05