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
相关产品推荐
相关产品推荐

