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

大音频文件的流式音频波形生成方案需求及现有实现

大音频文件的波形生成优化方案

问题背景

需要为MP3、WAV等格式的音频文件生成波形数据,现有实现使用scipy.io.wavfile.read加载整个文件到内存,处理大于300MB的文件时会出现内存不足问题,需要流式读取或更优实现思路。

现有实现代码

import pandas as pd
import numpy as np
import scipy.signal as sps
from scipy.io.wavfile import read as read_wav
import fsspec

def gen_audio_wave_form(audio_file_path):
    with fsspec.open(audio_file_path) as file_obj:
        sample_rate, audio_buffer = read_wav(file_obj)
        duration = audio_buffer.shape[0] / sample_rate
    # resample to limit number of samples
    data = sps.resample(audio_buffer, 2000)

    # normalize values so that it is in the range 0-1
    normalized_audio_buffer = normalize_nd_array(data)

    target_data = create_target_data(
        audio_data=normalized_audio_buffer, duration=duration
            )
    return target_data

def normalize_nd_array(array):
    abs_array = np.abs(array)
    max_x = np.max(abs_array)
    normalized_array = np.array([x / max_x for x in abs_array])
    return normalized_array

def create_target_data(audio_data, duration: float) -> pd.DataFrame:            
    if np.ndim(audio_data) > 1:
        audio_data = [x[0] for x in audio_data]
    else:
        audio_data = audio_data.tolist()
    df = pd.DataFrame()
    df["waveform_data"] = [audio_data]
    df["duration_in_sec"] = duration
    return df

优化方案

核心思路

波形显示只需要降采样后的稀疏数据(目标2000个点),无需加载全部采样。通过分块读取音频,计算每块的关键统计值(如绝对值峰值、均方根),再将这些统计值组合成波形数据,最后归一化并调整到目标点数,彻底避免大文件内存过载问题。

方案1:使用soundfile分块读取(支持WAV/FLAC等无损格式)

soundfile原生支持流式分块读取,适合处理大体积无损音频:

import pandas as pd
import numpy as np
import soundfile as sf
import scipy.signal as sps
import fsspec

def gen_audio_wave_form(audio_file_path):
    target_points = 2000
    block_stats = []
    max_global = 0.0

    with fsspec.open(audio_file_path, 'rb') as file_obj:
        # 获取音频元信息
        info = sf.info(file_obj)
        sample_rate = info.samplerate
        total_frames = info.frames
        duration = total_frames / sample_rate

        # 计算块大小,使块数接近目标点数
        block_size = max(1, total_frames // target_points)

        # 分块读取并计算每块绝对值峰值
        for block in sf.blocks(file_obj, blocksize=block_size, dtype='float32'):
            # 多声道取第一声道
            if block.ndim > 1:
                block = block[:, 0]
            block_peak = np.max(np.abs(block))
            block_stats.append(block_peak)
            # 更新全局最大值用于归一化
            if block_peak > max_global:
                max_global = block_peak

    # 将块统计值调整到目标点数
    if len(block_stats) != target_points:
        block_stats = sps.resample(np.array(block_stats), target_points)
    
    # 归一化到0-1范围
    normalized_data = np.array(block_stats) / max_global if max_global != 0 else block_stats

    return create_target_data(normalized_data, duration)

def create_target_data(audio_data, duration: float) -> pd.DataFrame:            
    audio_data = audio_data.tolist()
    df = pd.DataFrame()
    df["waveform_data"] = [audio_data]
    df["duration_in_sec"] = duration
    return df

方案2:处理MP3等压缩格式(使用pydub)

如果需要支持MP3等压缩音频,pydub可按需读取音频片段,避免一次性加载全部文件:

import pandas as pd
import numpy as np
from pydub import AudioSegment
import scipy.signal as sps

def gen_audio_wave_form(audio_file_path):
    target_points = 2000
    block_stats = []
    max_global = 0.0

    # 按需加载音频,不一次性读入内存
    audio = AudioSegment.from_file(audio_file_path)
    sample_rate = audio.frame_rate
    duration = len(audio) / 1000  # 转换为秒
    channels = audio.channels

    # 计算块时长(毫秒),使块数接近目标点数
    block_duration_ms = max(1, int(duration * 1000 // target_points))

    # 分块处理音频
    for i in range(0, len(audio), block_duration_ms):
        block = audio[i:i+block_duration_ms]
        samples = np.array(block.get_array_of_samples())
        # 多声道取第一声道
        if channels > 1:
            samples = samples[::channels]
        block_peak = np.max(np.abs(samples))
        block_stats.append(block_peak)
        if block_peak > max_global:
            max_global = block_peak

    # 调整到目标点数
    if len(block_stats) != target_points:
        block_stats = sps.resample(np.array(block_stats), target_points)
    
    # 归一化
    normalized_data = np.array(block_stats) / max_global if max_global != 0 else block_stats

    return create_target_data(normalized_data, duration)

def create_target_data(audio_data, duration: float) -> pd.DataFrame:            
    audio_data = audio_data.tolist()
    df = pd.DataFrame()
    df["waveform_data"] = [audio_data]
    df["duration_in_sec"] = duration
    return df

关键优化点

  • 流式分块读取:彻底避免大文件一次性加载到内存
  • 统计值替代全采样:用每块峰值代表该段波形高度,既减少数据量,又保留波形视觉特征
  • 动态归一化:分块过程中记录全局最大值,确保归一化准确性
  • 多声道兼容:自动处理单/多声道音频,统一输出单声道波形数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:43:19