如何从音频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
关键修复说明
- 移除文件依赖函数:原代码使用
get_duration和get_channels读取音频文件信息,现直接从numpy数组的形状计算:- 音频总时长:
signal_duration = audio_array.shape[1] / sampling_rate - 通道数:
audio_array.shape[0]
- 音频总时长:
- 补全缺失依赖:添加
numpy导入,修复原代码中未定义的always_2d变量问题 - 索引边界检查:确保采样点索引在数组有效范围内,避免切片越界
- 格式统一:假设输入数组为
(通道数, 采样点数)的标准格式,符合audiofile返回的数组格式 - 异常处理:添加数组格式检查,避免传入错误维度的数组
内容的提问来源于stack exchange,提问作者skidjoe
相关产品推荐
相关产品推荐

