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

torchaudio.io.StreamReader超音频时长seek无报错,如何检测偏移越界

问题:如何检测FLAC音频(BytesIO格式)的提取偏移是否越界

我需要从音频文件中提取特定时间段的片段,比如从10秒长的音频里提取4-7秒的内容。但处理io.BytesIO格式的FLAC音频时,torchaudio.info无法正确返回num_frames,导致没法获取音频总帧数来判断起始偏移是否超出音频时长。

我写了这段提取音频片段的代码:

def read_audio(audio_file, start, end):
    audio_file.seek(0)
    reader = StreamReader(audio_file)
    sample_rate = int(reader.get_src_stream_info(reader.default_audio_stream).sample_rate)

    reader.seek(start * sample_rate)
    reader.add_basic_audio_stream(frames_per_chunk=(end - start) * sample_rate)

    return list(reader.stream())[0].pop()

这段代码在起始时间小于音频时长时能正常工作,但起始时间超过音频时长时,既不抛出错误也不返回空张量,请问该怎么检测给定的偏移是否越界?


解决方案

方法1:检查读取结果的实际帧数

读取完成后对比返回张量的实际帧数和预期帧数,如果实际帧数远小于预期,说明起始偏移已经越界:

def read_audio(audio_file, start, end):
    audio_file.seek(0)
    reader = StreamReader(audio_file)
    stream_info = reader.get_src_stream_info(reader.default_audio_stream)
    sample_rate = int(stream_info.sample_rate)
    expected_frames = (end - start) * sample_rate

    reader.seek(start * sample_rate)
    reader.add_basic_audio_stream(frames_per_chunk=expected_frames)

    chunks = list(reader.stream())
    if not chunks:
        raise ValueError("起始偏移超出音频时长,无法读取任何数据")
    
    audio_tensor = chunks[0].pop()
    actual_frames = audio_tensor.size(1)  # 张量形状为(通道数, 帧数)
    if actual_frames == 0:
        raise ValueError("起始偏移超出音频时长")
    # 若允许返回不足长度的片段,可跳过以下判断;否则抛出错误
    if actual_frames < expected_frames:
        raise ValueError(f"起始偏移越界,仅读取到{actual_frames/sample_rate:.2f}秒的音频,预期{(end-start):.2f}秒")
    
    return audio_tensor

方法2:预读取音频总帧数(适合小文件)

如果音频文件不大,可以先完整读取一次获取总帧数,再判断偏移是否合法:

def get_total_frames(audio_file):
    audio_file.seek(0)
    reader = StreamReader(audio_file)
    reader.add_basic_audio_stream()
    total_frames = 0
    for chunk in reader.stream():
        total_frames += chunk[0].size(1)
    audio_file.seek(0)  # 重置文件指针
    return total_frames

def read_audio(audio_file, start, end):
    reader = StreamReader(audio_file)
    sample_rate = int(reader.get_src_stream_info(0).sample_rate)
    total_frames = get_total_frames(audio_file)
    start_frames = start * sample_rate
    end_frames = end * sample_rate

    if start_frames >= total_frames:
        raise ValueError(f"起始时间{start}秒超出音频总时长{total_frames/sample_rate:.2f}秒")
    if end_frames > total_frames:
        # 可选:截断到音频末尾,或者抛出错误
        end_frames = total_frames
        end = total_frames / sample_rate

    audio_file.seek(0)
    reader = StreamReader(audio_file)
    reader.seek(start_frames)
    reader.add_basic_audio_stream(frames_per_chunk=end_frames - start_frames)

    return list(reader.stream())[0].pop()

方法3:利用seek的返回值判断

StreamReader.seek()方法会返回实际跳转的帧数,对比该值和预期起始帧数即可判断是否越界:

def read_audio(audio_file, start, end):
    audio_file.seek(0)
    reader = StreamReader(audio_file)
    stream_info = reader.get_src_stream_info(reader.default_audio_stream)
    sample_rate = int(stream_info.sample_rate)
    start_frames = int(start * sample_rate)
    expected_end_frames = int(end * sample_rate)

    # 执行seek并获取实际跳转的帧数
    actual_seek_frames = reader.seek(start_frames)
    if actual_seek_frames < start_frames:
        raise ValueError(f"起始时间{start}秒超出音频时长,实际仅能跳转到{actual_seek_frames/sample_rate:.2f}秒")
    
    # 计算实际可读取的帧数(避免超出音频剩余长度)
    # 注意:若stream_info.num_frames不可靠,此部分需替换为其他方式计算剩余帧数
    remaining_frames = stream_info.num_frames - actual_seek_frames
    read_frames = min(expected_end_frames - start_frames, remaining_frames)
    if read_frames <= 0:
        raise ValueError("起始偏移超出音频时长,无数据可读取")

    reader.add_basic_audio_stream(frames_per_chunk=read_frames)
    return list(reader.stream())[0].pop()

注:方法3中stream_info.num_frames对于BytesIO格式的FLAC可能仍然不准确,更推荐使用方法1或方法2。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 21:32:23