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

如何在Python中播放Matplotlib图表选中的音频片段并同步显示进度

实现选中音频片段播放+实时进度竖线

核心思路

用sounddevice非阻塞播放音频片段,通过线程分离播放逻辑与UI更新,实时更新Matplotlib图表中的竖线位置,同步播放进度。

所需依赖

先安装必要库:

pip install soundfile sounddevice matplotlib numpy

完整代码实现

import numpy as np
import soundfile as sf
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import sounddevice as sd
import threading
import time

# ---------------------- 1. 读取音频文件 ----------------------
audio_path = "your_audio.wav"  # 替换为你的WAV文件路径
samplerate, audio_data = sf.read(audio_path)
# 立体声转单声道(如果需要)
if len(audio_data.shape) > 1:
    audio_data = np.mean(audio_data, axis=1)
total_duration = len(audio_data) / samplerate

# ---------------------- 2. 绘制音频特征图(替换为你的OpenSMILE特征图) ----------------------
fig, ax = plt.subplots(figsize=(10, 4))
# 示例:绘制音频波形,替换成你用OpenSMILE提取的特征(如MFCC、能量等)时序图
time_axis = np.linspace(0, total_duration, len(audio_data))
ax.plot(time_axis, audio_data)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Amplitude")
ax.set_title("Audio Feature & Playback Progress")

# ---------------------- 3. 处理片段选择(基于你的现有逻辑,这里做简化示例) ----------------------
selected_start = None  # 选中片段的开始时间(秒)
selected_duration = None  # 选中片段的时长(秒)
click_count = 0
click_times = []

def on_click(event):
    """点击图表两次,选择开始和结束时间,确定播放片段"""
    global click_count, click_times, selected_start, selected_duration
    if event.inaxes == ax:
        click_times.append(event.xdata)
        click_count += 1
        if click_count == 2:
            selected_start = min(click_times)
            selected_duration = max(click_times) - selected_start
            # 高亮选中区域
            ax.axvspan(selected_start, selected_start + selected_duration, color='red', alpha=0.3)
            plt.draw()
            # 重置点击计数器
            click_count = 0
            click_times = []

fig.canvas.mpl_connect('button_press_event', on_click)

# ---------------------- 4. 进度竖线与播放逻辑 ----------------------
# 创建进度竖线,初始隐藏
progress_line = ax.axvline(x=0, color='blue', linestyle='--', linewidth=2, visible=False)

def play_selected_segment():
    """播放选中的音频片段,并同步更新进度竖线"""
    if selected_start is None or selected_duration is None:
        print("请先通过两次点击选择音频片段")
        return
    
    # 转换为采样点索引
    start_sample = int(selected_start * samplerate)
    end_sample = int((selected_start + selected_duration) * samplerate)
    segment_data = audio_data[start_sample:end_sample]
    
    # 初始化进度竖线位置
    progress_line.set_visible(True)
    progress_line.set_xdata(selected_start)
    plt.draw()
    
    # 非阻塞播放音频
    sd.play(segment_data, samplerate)
    
    # 实时更新进度竖线(每10ms更新一次)
    update_interval = 0.01
    start_play_time = time.time()
    while sd.get_stream().active:
        elapsed_time = time.time() - start_play_time
        current_pos = selected_start + elapsed_time
        # 避免超过片段结束时间
        if current_pos > selected_start + selected_duration:
            current_pos = selected_start + selected_duration
        progress_line.set_xdata(current_pos)
        plt.draw()
        time.sleep(update_interval)
    
    # 播放结束后隐藏竖线
    progress_line.set_visible(False)
    plt.draw()

# 添加播放按钮
ax_play_btn = plt.axes([0.81, 0.01, 0.1, 0.05])
play_btn = Button(ax_play_btn, 'Play')
# 用线程启动播放,避免阻塞Matplotlib UI
play_btn.on_clicked(lambda _: threading.Thread(target=play_selected_segment).start())

plt.tight_layout()
plt.show()

关键细节说明

  • 线程分离:播放逻辑放在独立线程中,避免阻塞Matplotlib的UI渲染,保证图表能实时更新进度竖线。
  • 进度更新频率:update_interval设为0.01秒(100次/秒),平衡流畅度与资源占用,可根据需求调整。
  • 音频兼容性:用soundfile读取WAV文件,支持多通道自动转单声道,适配大多数音频格式。
  • 特征图替换:把示例中的波形绘图代码,替换成你用OpenSMILE提取的特征(如MFCC、响度等)的时序绘制代码即可,进度竖线的时间轴逻辑完全通用。

常见问题处理

  • 如果sounddevice报错,需安装PortAudio:Windows下可直接通过pip install sounddevice --user自动安装,或手动下载PortAudio二进制文件添加到系统PATH。
  • 若播放时音频卡顿,可适当增大update_interval的值,减少UI更新频率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:45:36