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

