日常搬运wxpython融合matplotlib显示实时声音波形
wx_plot.py
# -*- coding: utf-8 -*-
import wx
import os
import wave
import pyaudio
import random
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.lines as line
from matplotlib.backends import backend_wxagg
from multiprocessing import Process, Queue, freeze_support
from display_ui import DspUI
from threading import Thread
import time
import ctypes
q = Queue(10)
def cb(cb_q):
def callback(in_data, frame_count, time_info, status): # all parameters are need!
rt_data = np.frombuffer(in_data, np.dtype('))
val_list = np.hsplit(rt_data, 32)
avg_list = [each.mean() for each in val_list] # data average
if cb_q.full():
cb_q.get()
cb_q.put(avg_list)
return None, pyaudio.paContinue
return callback
class Lintener(Process):
def __init__(self, _q):
Process.__init__(self)
self.q = _q
self.CHUNK = 1024
self.FORMAT = pyaudio.paInt16
self.CHANNELS = 2
self.RATE = 44100
def run(self):
p = pyaudio.PyAudio()
p.open(format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
frames_per_buffer=self.CHUNK,
stream_callback=cb(self.q))
print("\nListening...")
while True:
pass
class Player(object):
def __init__(self):
self.waveOutGetVolume = ctypes.windll.winmm.waveOutGetVolume
self.waveOutSetVolume = ctypes.windll.winmm.waveOutSetVolume
self.MINIMUM_VOLUME = 0
self.MAXIMUM_VOLUME = 65535
self.CHUNK = 1024
self.busy = False
self.p = pyaudio.PyAudio()
def __del__(self):
self.p.terminate()
def set_volume(self, volume):
volume = int(volume * self.MAXIMUM_VOLUME / 100)
volume = (volume << 16) | volume
ret = self.waveOutSetVolume(0, volume)
get_val = ctypes.c_long(0)
print(self.waveOutGetVolume(0, ctypes.byref(get_val))) # b'\xff\xff\xff\xff' 高16位,低16位代表两个声道
print(bytes(get_val))
if ret != 0:
raise WindowsError("Error %d while setting volume" % ret)
def _play(self, _cb, select_file_path):
wf = wave.open(select_file_path, 'rb')
stream = self.p.open(format=self.p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
data = wf.readframes(self.CHUNK)
while data != b'':
stream.write(data)
data = wf.readframes(self.CHUNK)
stream.stop_stream()
stream.close()
if _cb:
_cb()
time.sleep(1)
self.busy = False
def play(self, _cb=None):
self.busy = True
dir_path = './audio'
files = os.listdir(dir_path)
select_file = random.randint(0, len(files) - 1)
select_file_path = os.path.join(dir_path, files[select_file])
print('Selected:' + files[select_file])
th_play = Thread(target=self._play, args=(_cb,select_file_path,))
th_play.setDaemon(True)
th_play.start()
return files[select_file]
class Displayer(DspUI):
def __init__(self, _q, player):
DspUI.__init__(self, None)
self.player = player
self.trig_threshold = 0
self.__thresh_items = ['1%', '2%', '5%', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%']
self.m_threshold.SetItems(self.__thresh_items)
self.m_threshold.Select(0) # init to select the first one
self.update_trig_threshold(0)
self.voice_power = 0
self.m_playshow.Value = 0 #
self.m_voice_pow.Enable(False)
self.x_length = 768
self.fig = plt.figure(1, figsize=(5, 2), facecolor='#000000',) # (171,171,171)
self.panel = backend_wxagg.FigureCanvasWxAgg(self.m_animCtrl4, -1, self.fig)
self.ax = self.panel.figure.gca() # *
self.ax = plt.axes(xlim=(0, self.x_length), ylim=(-20000, 20000)) # , facecolor='#ABABAB') # (-32768, 32767)
self.linem = line.Line2D([], [], linewidth='0.5', color='#00FFFF')
self.q = _q
self.y_data = [0 for _ in range(self.x_length)]
plt.axis('off') # hide axis
plt.xticks([]) # hide coordinate scale
plt.yticks([]) # hide coordinate scale
plt.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
self.panel.draw()
self.ani = animation.FuncAnimation(self.fig, self.update(),
init_func=self.init,
frames=1,
interval=1,
blit=True)
def close_even(self, event):
self.ani.event_source.stop()
event.Skip()
def update_trig_threshold(self, item_index):
selected_item = self.__thresh_items[item_index].replace('%', '')
self.trig_threshold = int(10000. * int(selected_item) / 100.)
print(selected_item, self.trig_threshold)
def threshold_choice_handle(self, event):
self.update_trig_threshold(self.m_threshold.GetSelection())
event.Skip()
def init(self):
self.ax.add_line(self.linem)
return self.linem,
def cb_form_player(self):
self.m_playshow.Value = 0
self.m_voice_pow.SetValue('')
self.m_playing_file.SetLabel('')
print('Done!')
def update(self):
def inner_update(i):
if not q.empty():
self.y_data = self.y_data[32:] + q.get()
self.linem.set_xdata(np.arange(0, self.x_length, 1))
self.linem.set_ydata(self.y_data)
if not self.player.busy and (self.y_data[0] > self.trig_threshold):
self.m_playshow.Value = 1
self.voice_power = np.mean([abs(each) for each in self.y_data if abs(each) > 30])
self.m_voice_pow.SetValue(str(self.voice_power))
self.player.set_volume(min(100, int(self.voice_power/20)))
play_file = self.player.play(self.cb_form_player)
self.m_playing_file.SetLabel('Playing: ' + str(play_file))
return self.linem,
return inner_update
if __name__ == '__main__':
freeze_support()
listener = Lintener(q)
listener.start()
app = wx.App()
display = Displayer(q, Player())
display.Show(True)
app.MainLoop()
listener.terminate()
display_ui.py
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version 3.9.0 Dec 4 2019)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################
import wx
import wx.xrc
import wx.adv
###########################################################################
## Class DspUI
###########################################################################
class DspUI ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"Listener", pos = wx.DefaultPosition, size = wx.Size( 524,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL, name = u"Listener" )
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
bSizer1 = wx.BoxSizer( wx.VERTICAL )
self.m_panel2 = wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer5 = wx.BoxSizer( wx.VERTICAL )
self.m_panel3 = wx.Panel( self.m_panel2, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
bSizer6 = wx.BoxSizer( wx.HORIZONTAL )
bSizer11 = wx.BoxSizer( wx.VERTICAL )
bSizer11.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_staticText3 = wx.StaticText( self.m_panel3, wx.ID_ANY, u"阈值:", wx.DefaultPosition, wx.Size( -1,-1 ), 0 )
self.m_staticText3.Wrap( -1 )
self.m_staticText3.SetFont( wx.Font( 13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
bSizer11.Add( self.m_staticText3, 0, wx.ALL, 5 )
bSizer11.Add( ( 0, 0), 1, wx.EXPAND, 5 )
bSizer6.Add( bSizer11, 1, wx.EXPAND, 5 )
bSizer14 = wx.BoxSizer( wx.VERTICAL )
bSizer14.Add( ( 0, 0), 1, wx.EXPAND, 5 )
m_thresholdChoices = []
self.m_threshold = wx.Choice( self.m_panel3, wx.ID_ANY, wx.DefaultPosition, wx.Size( 78,-1 ), m_thresholdChoices, 0 )
self.m_threshold.SetSelection( 0 )
bSizer14.Add( self.m_threshold, 0, wx.ALL, 5 )
bSizer14.Add( ( 0, 0), 1, wx.EXPAND, 5 )
bSizer6.Add( bSizer14, 1, wx.EXPAND, 5 )
bSizer15 = wx.BoxSizer( wx.VERTICAL )
bSizer15.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_playshow = wx.RadioButton( self.m_panel3, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_playshow.SetFont( wx.Font( 18, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
bSizer15.Add( self.m_playshow, 0, wx.ALL, 5 )
bSizer15.Add( ( 0, 0), 1, wx.EXPAND, 5 )
bSizer6.Add( bSizer15, 1, wx.EXPAND, 5 )
bSizer10 = wx.BoxSizer( wx.VERTICAL )
bSizer10.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_staticText6 = wx.StaticText( self.m_panel3, wx.ID_ANY, u"声强:", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText6.Wrap( -1 )
self.m_staticText6.SetFont( wx.Font( 13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
bSizer10.Add( self.m_staticText6, 0, wx.ALL, 5 )
bSizer10.Add( ( 0, 0), 1, wx.EXPAND, 5 )
bSizer6.Add( bSizer10, 1, wx.EXPAND, 5 )
bSizer9 = wx.BoxSizer( wx.VERTICAL )
bSizer9.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_voice_pow = wx.TextCtrl( self.m_panel3, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size( 78,-1 ), 0 )
self.m_voice_pow.SetFont( wx.Font( 13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, wx.EmptyString ) )
bSizer9.Add( self.m_voice_pow, 0, wx.ALL, 5 )
bSizer9.Add( ( 0, 0), 1, wx.EXPAND, 5 )
bSizer6.Add( bSizer9, 1, wx.EXPAND, 5 )
bSizer8 = wx.BoxSizer( wx.VERTICAL )
bSizer8.Add( ( 0, 0), 1, wx.EXPAND, 5 )
self.m_playing_file = wx.StaticText( self.m_panel3, wx.ID_ANY, u"Playing: xxxxxxx.wav", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_playing_file.Wrap( -1 )
bSizer8.Add( self.m_playing_file, 0, wx.ALL, 5 )
bSizer8.Add( ( 0, 0), 1, wx.EXPAND, 5 )
bSizer6.Add( bSizer8, 5, wx.EXPAND, 5 )
self.m_panel3.SetSizer( bSizer6 )
self.m_panel3.Layout()
bSizer6.Fit( self.m_panel3 )
bSizer5.Add( self.m_panel3, 1, wx.EXPAND |wx.ALL, 5 )
self.m_panel2.SetSizer( bSizer5 )
self.m_panel2.Layout()
bSizer5.Fit( self.m_panel2 )
bSizer1.Add( self.m_panel2, 1, wx.EXPAND |wx.ALL, 5 )
self.m_animCtrl4 = wx.adv.AnimationCtrl( self, wx.ID_ANY, wx.adv.NullAnimation, wx.DefaultPosition, wx.Size( 500,200 ), wx.adv.AC_DEFAULT_STYLE )
bSizer1.Add( self.m_animCtrl4, 0, wx.ALL, 5 )
self.SetSizer( bSizer1 )
self.Layout()
self.Centre( wx.BOTH )
# Connect Events
self.Bind( wx.EVT_CLOSE, self.close_even )
self.m_threshold.Bind( wx.EVT_CHOICE, self.threshold_choice_handle )
def __del__( self ):
pass
# Virtual event handlers, overide them in your derived class
def close_even( self, event ):
event.Skip()
def threshold_choice_handle( self, event ):
event.Skip()