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

Python处理音频文件:信号图表与Cool Edit Pro显示不一致问题

Python绘制音频波形与Cool Edit Pro存在差异的问题

问题描述

使用Python脚本绘制WAV音频信号波形时,约半数文件的生成效果与Cool Edit Pro显示的正常波形存在差异:

  • 正常效果:连续完整的音频信号波形,清晰展示信号的起伏变化
  • 异常效果:波形存在断裂、局部失真,无法准确还原原始音频的信号特征

所用代码

import os
import glob
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import wavfile

INPUT_FOLDER = "aaaaa"
OUTPUT_FOLDER = "aaaaa"
os.makedirs(OUTPUT_FOLDER, exist_ok=True)

def load_wav_normalized(filepath):
    fs, data = wavfile.read(filepath)
    if data.dtype == np.int32:
        data = data.astype(np.float32) / 2147483648.0
    elif data.dtype == np.int16:
        data = data.astype(np.float32) / 32768.0
    elif data.dtype == np.uint8:
        data = (data.astype(np.float32) - 128) / 128.0
    if len(data.shape) > 1:
        data = np.mean(data, axis=1)
    return data, fs

def draw_exactly_like_cool_edit(signal, fs, output_path):
    n_samples = len(signal)
    target_width = 2000
    if n_samples <= target_width:
        samples_per_pixel = 1
        min_vals = signal
        max_vals = signal
        time = np.arange(n_samples) / fs
    else:
        samples_per_pixel = n_samples // target_width
        n_blocks = target_width
        trimmed_len = n_blocks * samples_per_pixel
        trimmed = signal[:trimmed_len]
        blocks = trimmed.reshape(n_blocks, samples_per_pixel)
        min_vals = np.min(blocks, axis=1)
        max_vals = np.max(blocks, axis=1)
        time = np.arange(n_blocks) * samples_per_pixel / fs
    fig, ax = plt.subplots(figsize=(16, 5), dpi=100)
    fig.patch.set_facecolor('#0A0A0A')
    ax.set_facecolor('#0A0A0A')
    for i in range(len(time)):
        ax.plot([time[i], time[i]], [min_vals[i], max_vals[i]], 
                color='#1EFF00', linewidth=1.5, alpha=0.9, solid_capstyle='butt')
    ax.axhline(y=0, color='#666666', linewidth=0.8)
    ax.axhline(y=1.0, color='#FF4444', linewidth=0.5, linestyle='--', alpha=0.6)
    ax.axhline(y=-1.0, color='#FF4444', linewidth=0.5, linestyle='--', alpha=0.6)
    for level in [0.5, 0.25]:
        ax.axhline(y=level, color='#2A2A2A', linewidth=0.3, linestyle=':', alpha=0.5)
        ax.axhline(y=-level, color='#2A2A2A', linewidth=0.3, linestyle=':', alpha=0.5)
    ax.set_title(f"{os.path.basename(output_path).replace('.png', '')} | {fs} Hz", 
                 color='white', fontsize=10)
    ax.set_xlabel("Время (секунды)", color='white', fontsize=9)
    ax.set_ylabel("Амплитуда", color='white', fontsize=9)
    ax.tick_params(colors='white', labelsize=8)
    ax.grid(True, color='#2A2A2A', alpha=0.3, linestyle='-', linewidth=0.3)
    for spine in ax.spines.values():
        spine.set_color('#333333')
    ax.set_xlim(time[0], time[-1])
    ax.set_ylim(-1.1, 1.1)
    plt.tight_layout()
    plt.savefig(output_path, facecolor='#0A0A0A', dpi=100)
    plt.close()
    return min_vals, max_vals, time

wav_files = glob.glob(os.path.join(INPUT_FOLDER, "*.wav"))
for filepath in wav_files:
    filename = os.path.splitext(os.path.basename(filepath))[0]
    signal, fs = load_wav_normalized(filepath)
    output_path = os.path.join(OUTPUT_FOLDER, f"{filename}_cooledit_style.png")
    min_vals, max_vals, time = draw_exactly_like_cool_edit(signal, fs, output_path)

问题原因及解决方案

1. 音频样本截断导致信息丢失

原代码处理长音频时,直接截断末尾剩余样本(trimmed = signal[:trimmed_len]),丢失部分音频数据,导致波形不完整。

修正方案:覆盖所有样本分块计算,不截断:

# 替换draw_exactly_like_cool_edit函数中的else块
else:
    # 生成覆盖所有样本的分块索引
    indices = np.linspace(0, n_samples, target_width + 1, dtype=int)
    min_vals = []
    max_vals = []
    for i in range(target_width):
        start_idx = indices[i]
        end_idx = indices[i+1]
        block = signal[start_idx:end_idx]
        min_vals.append(np.min(block))
        max_vals.append(np.max(block))
    min_vals = np.array(min_vals)
    max_vals = np.array(max_vals)
    # 基于分块中心计算时间轴
    time = np.array([(indices[i] + indices[i+1]) / 2 / fs for i in range(target_width)])

2. 未支持24位深度音频

原代码仅处理int32、int16、uint8格式音频,未覆盖常见的24位WAV文件,导致这类文件解析错误。

修正方案:在load_wav_normalized函数中添加24位音频处理逻辑:

elif data.dtype == np.int24:
    data = data.astype(np.float32) / 8388608.0  # 2^23 = 8388608

3. 立体声混合方式与Cool Edit Pro不一致

原代码用np.mean(data, axis=1)混合立体声,而Cool Edit Pro默认采用取声道最大绝对值的方式展示,导致波形差异。

修正方案:调整立体声混合逻辑:

if len(data.shape) > 1:
    # 取每个样本的最大绝对值,并保留对应声道的符号
    abs_data = np.abs(data)
    max_abs_idx = np.argmax(abs_data, axis=1)
    data = np.take_along_axis(data, max_abs_idx[:, np.newaxis], axis=1).flatten()

4. 时间轴计算偏差

原代码时间轴基于分块起始位置,Cool Edit Pro通常采用分块中心位置作为时间标记,导致波形对齐偏差。

修正方案:使用分块中心位置计算时间轴,如上述修正后的else块中的time计算方式。

修正后的完整代码

import os
import glob
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import wavfile

INPUT_FOLDER = "aaaaa"
OUTPUT_FOLDER = "aaaaa"
os.makedirs(OUTPUT_FOLDER, exist_ok=True)

def load_wav_normalized(filepath):
    fs, data = wavfile.read(filepath)
    # 支持更多音频位深度
    if data.dtype == np.int32:
        data = data.astype(np.float32) / 2147483648.0
    elif data.dtype == np.int24:
        data = data.astype(np.float32) / 8388608.0
    elif data.dtype == np.int16:
        data = data.astype(np.float32) / 32768.0
    elif data.dtype == np.uint8:
        data = (data.astype(np.float32) - 128) / 128.0
    # 调整立体声混合逻辑,贴近Cool Edit Pro
    if len(data.shape) > 1:
        abs_data = np.abs(data)
        max_abs_idx = np.argmax(abs_data, axis=1)
        data = np.take_along_axis(data, max_abs_idx[:, np.newaxis], axis=1).flatten()
    return data, fs

def draw_exactly_like_cool_edit(signal, fs, output_path):
    n_samples = len(signal)
    target_width = 2000
    if n_samples <= target_width:
        samples_per_pixel = 1
        min_vals = signal
        max_vals = signal
        time = np.arange(n_samples) / fs
    else:
        # 生成覆盖所有样本的分块索引
        indices = np.linspace(0, n_samples, target_width + 1, dtype=int)
        min_vals = []
        max_vals = []
        for i in range(target_width):
            start_idx = indices[i]
            end_idx = indices[i+1]
            block = signal[start_idx:end_idx]
            min_vals.append(np.min(block))
            max_vals.append(np.max(block))
        min_vals = np.array(min_vals)
        max_vals = np.array(max_vals)
        # 基于分块中心计算时间轴
        time = np.array([(indices[i] + indices[i+1]) / 2 / fs for i in range(target_width)])
    fig, ax = plt.subplots(figsize=(16, 5), dpi=100)
    fig.patch.set_facecolor('#0A0A0A')
    ax.set_facecolor('#0A0A0A')
    for i in range(len(time)):
        ax.plot([time[i], time[i]], [min_vals[i], max_vals[i]], 
                color='#1EFF00', linewidth=1.5, alpha=0.9, solid_capstyle='butt')
    ax.axhline(y=0, color='#666666', linewidth=0.8)
    ax.axhline(y=1.0, color='#FF4444', linewidth=0.5, linestyle='--', alpha=0.6)
    ax.axhline(y=-1.0, color='#FF4444', linewidth=0.5, linestyle='--', alpha=0.6)
    for level in [0.5, 0.25]:
        ax.axhline(y=level, color='#2A2A2A', linewidth=0.3, linestyle=':', alpha=0.5)
        ax.axhline(y=-level, color='#2A2A2A', linewidth=0.3, linestyle=':', alpha=0.5)
    ax.set_title(f"{os.path.basename(output_path).replace('.png', '')} | {fs} Hz", 
                 color='white', fontsize=10)
    ax.set_xlabel("Время (секунды)", color='white', fontsize=9)
    ax.set_ylabel("Амплитуда", color='white', fontsize=9)
    ax.tick_params(colors='white', labelsize=8)
    ax.grid(True, color='#2A2A2A', alpha=0.3, linestyle='-', linewidth=0.3)
    for spine in ax.spines.values():
        spine.set_color('#333333')
    ax.set_xlim(time[0], time[-1])
    ax.set_ylim(-1.1, 1.1)
    plt.tight_layout()
    plt.savefig(output_path, facecolor='#0A0A0A', dpi=100)
    plt.close()
    return min_vals, max_vals, time

wav_files = glob.glob(os.path.join(INPUT_FOLDER, "*.wav"))
for filepath in wav_files:
    filename = os.path.splitext(os.path.basename(filepath))[0]
    signal, fs = load_wav_normalized(filepath)
    output_path = os.path.join(OUTPUT_FOLDER, f"{filename}_cooledit_style.png")
    min_vals, max_vals, time = draw_exactly_like_cool_edit(signal, fs, output_path)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.03 14:05:55