用 matplotlib 制作动态音频波形图(代码)

import librosa
import matplotlib.patches as ptc
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation as anm
import numpy as np

#-=-=================
def gettimetext(t): # 用于画布显示时间的字符
        h=t//3600
        m=(t%3600)//60
        s=t %60
        h=int(h)
        m=int(m)
        s=round(s,2) # 小数取2位
        return '{:0>2d}:{:0>2d}:{:0>5.2f}'.format(h,m,s)

'''
在一个窗口【静态】 展示 n 秒音频的波形, 
在窗口【动态】展示 n 秒音频的波形,
是两种不同的模式。
--------------------
动态意味着时空的变化,速度与尺寸的调整。
----------------
运动,有匀速运动,有变速运动        speed
观察,有近距离观察,有远距离观察 zoom
-----------
窗口数据,有两种处理方法。一是用“邻域”的思想,从中间到两边扩展;
另一种是,栈的用法,先进先出。考虑到数据的分散性,这方法的流量不好控制 。
--------------
 下面用第一种方法。
'''


 
def haliluya(ad_file,mod="c",savename="untitle"): # c= see / s=save
    # 获取数据
    au, sr = librosa.load (ad_file) 
    #从新采样数据
    auu=[]
    k=40
    for i in range ( 0,len(au),k):
        auu.append(au[i])
    sr=sr//k
    au=auu
    #----------------------------------------------------------------
    #设定参数
    w_width=6                 # 窗口尺寸,宽 (刻度个数)
    w_height=3
    #------
    cut_time=10                     #音频时间,取段 观察
    OAT=round(len(au)/sr,3) #原时长
    #investigate
    survey_border=cut_time/2 # 秒
    #-------------------------------
    update_frq=32         #         视觉窗口刷新频率>24每秒
    v_d_time_ms=1000/update_frq  # 刷新的时间,间隔  ms
    v_d_time_sc=1/update_frq         # 刷新的时间,间隔  s
    #--------------------可以调节-------------------------------------
    speed=2  #     播放速度
    zoom=2  #    观看细节放大倍数
    raise_y=3  #    幅度提升
    #--------------------------------------
    au=[i*raise_y for i in au] #把 np 数组改成 list,同时放大 y 绝对值
    #---------------------------------------------------------------------
    mass=(cut_time*sr)*speed/zoom #固定的窗口里,被观察的数据量,将会随着播放速度变大而变大,随着细节放大而减少
    windata_bd= int(mass //2 ) # 半个窗口需要的数据 边界 border
    PlayTimeAll= len(au)/sr/speed #    音频总时长 (被以某种速度播放时),即动画 videao 时间长
    #framesCt=  PlayTimeAll*1000 / v_d_time_ms  #     计算All可更新次数 =>视频的frames
    framesCt=PlayTimeAll * update_frq
    framesCt=int(framesCt)+2 #
    #----------------------------------------------
    '''把某一时刻的 ti 对应数据放在窗口中间观察'''
    #------------------------------------------------
   
    def po_ti(ti):  #中间 ti 对应的 au 数据位置    
            return int(ti*sr)
    
    def bo_ti(ti):  #中间 ti 对应的 au 数据位置 边界
            return      po_ti(ti) -windata_bd,     po_ti(ti)+windata_bd
     
    def get(a,b): # 取数据,同时填补空白处
        _pad=[]; pad_=[];st=b[0];end=b[1]
        if st<0 :
            if end>0:
                _pad=[None]*(-st)
                st=0
            else:
                return [None]*(end-st)
        if end>len(a):
            pad_=[None]*(end-len(a))   if st<len(a) else [None]*(2*windata_bd)
            end=len(a)
        return _pad +a[st:end]+ pad_     
#-----------------------------------------------------
    msg=f'''
    采样率:      {sr} |                            
    视窗更新频率 :{update_frq}Hz
    刷新时间:{v_d_time_ms}ms
    总时长:{round(PlayTimeAll,4)}s = {gettimetext(PlayTimeAll)}  
    原时长:{OAT}
    视频总帧数:    {round(framesCt,3)} 
    音频数据量:     {len(au)}              
    一倍速率窗口代表时间: {cut_time} s  
    播放速度:{speed}x
    zoom:{zoom}                    
    '''
    print(msg)

    #---------------
    # 设置画布
    fig,ax=plt.subplots(   figsize=(w_width,w_height) )
    #设置 x 轴刻度,y轴刻度
    lim_border= survey_border /zoom # zoom可以改变【数据边界data border】,但改不了数据中心   # 又 mk*sr=windata_bd
    lim_bd=lim_border
    ax.set_xlim(-lim_bd, lim_bd  )
    ax.set_ylim(-2, 2) 
    #plt.axis("off")
    #ax.axis("off") # 不显示坐标轴
    #设置函数图像(根据 时间(x) <---> 音频数据(y))
    line, = ax.plot([],[], lw=0.5,color='blue') 
    TimeShow=plt.text(3,1.5,'',fontsize=12,color="g") # 显示时间
    #设置观察点 sentry post ;observation post;
    bw,bh=0.01, 2
    sentryPost=ptc.Rectangle((-bw/2,0+bh/2),bw,-bh,edgecolor='#FF0010',facecolor="none",lw=0.5) #中间线
    ax.add_patch(sentryPost)
    
    # 定义更新函数 ti
    def update( fct ):
     
        if fct==framesCt-1:# 如果 ==ect :好像 quit 不了
            quit()
        
        real_world_time= fct * v_d_time_sc         #i.e.: video time
        time4audioMass= real_world_time * speed    
        #ax.set_title("video time :"+gettimetext(time ))
        TimeShow.set_text(gettimetext(time4audioMass ))  # 显示’磁带‘时间
        TimeShow.set_x(time4audioMass)  
        sentryPost.set_x(time4audioMass)
        #------更新坐标轴刻度------------
    
        ax.set_xlim(time4audioMass -lim_border, time4audioMass +lim_border ) # 以【数据边界】作为窗口首尾刻度
        #------------------------------更新数据----------------------------
        c=int(time4audioMass*sr)
        b=[c-windata_bd,c+windata_bd]
        data= get(au,b)
        #-------------------------- 新构 X 部
        xzo_twd=np.linspace(time4audioMass-lim_bd,time4audioMass+lim_bd,windata_bd*2)
       #--------------------------- 新构 Y 部
        line.set_data(xzo_twd, data)
       
        return line,  #更新波形图像
        
    #===========
    # 创建动画对象
    ani_1 = anm(fig, update, frames=framesCt, interval=v_d_time_ms) #ms
    # 显示动画
    if mod=="s":
         ani_1.save(f'{savename}_w.mp4', writer='ffmpeg')
    elif mod=="c":
        plt.show()
       
#---------------------------------运用------------------------
svn="te" # 你的 mp3 文件名
haliluya(f"{svn}.mp3","c") # 查看模式
#haliluya(f"{svn}.mp3","s","x2") # 保存模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值