매우 특정한 목적으로 오디오 플레이어가 필요한 프로젝트를 작성 중입니다. 현재 WxPython, Matplotlib 및 PyAudio 패키지를 사용하고 있습니다. Matplotlib의 WXAgg 백엔드 (backend_wxagg
)도 사용하고 있습니다.Matplotlib : WxPython에서 오디오 플레이어 커서 (수직선 슬라이딩) 애니메이션이 깨졌습니다.
기본적인 아이디어는 매우 간단하다 오디오 데이터는 메인 윈도우에 그려 동시에 플롯 재생하고 표시하는 동안 PyAudio 통해 재생한다 - 가로 슬라이딩 I는 애니메이션 수직선 할 의도 (을 커서), 예를 들어 Audacity에서 볼 수있는 유형입니다. 필자는 이미 일반적인 Matplotlib 애니메이션 예제를 시도해 보았지만 다른 일부는 웹을 통해 퍼져 나갔지 만 너무 느리거나 blitting되지 않고 FuncAnimation (프로젝트에 맞지 않는 아키텍처)에 의존하거나 기술 (내가 지금까지 작동하지 않는) 사용하려고합니다.
무언가가 실제로 화면에 이동하지만 검은 채워진 사각형 내 민트 노트북에 나타납니다 동안 전체 그림이 엉망 ... 흰색 채워진 사각형 내 우분투 (16) 바탕 화면에 플롯에 걸쳐 나타납니다 . 몇 일 동안 열심히 노력하고 ALMOST가 작동하도록 노력했지만, 겸손하게 도움을 청할 때가 왔습니다./
제가 아는 한 blit()
방법을 고집합니다. 그것은 (i) 맞춤 이벤트 (이 경우 오디오 프레임 소비)에서 플로팅을 새로 고침하고 (ii) 큰 성능의 가변 데이터 세트로 인해 좋은 성능을 보입니다.
# -*- coding: UTF-8 -*-
#
import wx
import gettext
import struct
import matplotlib
matplotlib.use('WX')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import pyaudio
import numpy as np
import time
CHUNK_SIZE = 1024 # Size (in samples) of each audio_callback reading.
BYTES_PER_FRAME = 2 # Number of bytes in each audio frame.
CHANNELS = 1 # Number of channels.
SAMPLING_RATE = 11025 # Audio sampling rate.
audio_chunks = []
# A simple frame with a tab (generated by WxGlade and simplified for example purposes):
class PlayerFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE | wx.MAXIMIZE_BOX | wx.SYSTEM_MENU | wx.RESIZE_BORDER | wx.CLIP_CHILDREN
wx.Frame.__init__(self, *args, **kwds)
self.main_notebook = wx.Notebook(self, wx.ID_ANY, style=0)
self.__set_properties()
self.__do_layout()
self.initAudio() # Initiates audio variables and event binding.
self.initPlotting() # Initiates plotting variables and widgets.
self.startPlayback() # Starts audio playback.
def __set_properties(self):
self.SetTitle(_("Audio signal plotting and playback with cursor"))
self.SetSize((720, 654))
def __do_layout(self):
sizer_main = wx.BoxSizer(wx.VERTICAL)
sizer_main.Add(self.main_notebook, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 25)
self.SetSizer(sizer_main)
self.Layout()
# Audio stuff initialization:
def initAudio(self):
# Binds the playback move event to a handler:
self.Bind(EVT_PLAYBACK_MOVE, self.OnPlaybackMove)
# Creates an empty audio chunk with "CHUNK_SIZE" samples of zero value ([0, 0, ..., 0, 0]):
empty_chunk = struct.pack("<h", 0)*CHUNK_SIZE
# Initializes audio chunk array with 20 empty audio chunks:
audio_chunks.extend([empty_chunk]*20)
# Points playback_counter to the first audio chunk:
global playback_counter; playback_counter = 0
def startPlayback(self):
# Initializes audio playback:
global p; p = pyaudio.PyAudio()
global audio_stream; audio_stream = p.open (format = p.get_format_from_width(BYTES_PER_FRAME)
, channels = CHANNELS
, rate = SAMPLING_RATE
, output = True
, stream_callback = audio_callback
, frames_per_buffer = CHUNK_SIZE)
# Plotting stuff initialization:
def initPlotting(self):
# Converts the raw audio chunks to a normal array:
samples = np.fromstring(b''.join(audio_chunks), dtype=np.int16)
# Creates plot supporting widgets:
self.pane = wx.Panel(self.main_notebook, wx.ID_ANY)
self.canvas = FigureCanvas(self.pane, wx.ID_ANY, Figure())
self.figure = self.canvas.figure
self.pane.SetMinSize((664, 355))
sizer_15 = wx.BoxSizer(wx.HORIZONTAL)
sizer_16 = wx.BoxSizer(wx.VERTICAL)
sizer_10 = wx.BoxSizer(wx.HORIZONTAL)
sizer_10.Add(self.canvas, 1, wx.EXPAND, 0)
sizer_16.Add(sizer_10, 2, wx.BOTTOM | wx.EXPAND, 25)
sizer_15.Add(sizer_16, 1, wx.ALL | wx.EXPAND, 25)
self.pane.SetSizer(sizer_15)
self.main_notebook.AddPage(self.pane, _("my_audio.wav"))
# ================================================
# Initializes plotting (is the problem in here???)
# ================================================
t = range(len(samples))
self.axes1 = self.figure.add_subplot(111)
self.axes1.set_xlim(0, len(samples))
self.axes1.set_ylim(-32768, 32767)
self.line1, = self.axes1.plot(t, samples)
self.Layout()
self.background = self.figure.canvas.copy_from_bbox(self.axes1.bbox)
self.playback_line = self.axes1.axvline(color="y", animated=True)
# For each new chunk read by the audio_callback function, we update the cursor position on the plot.
# It's important to notice that the audio_callback function CANNOT manipulate UI's widgets on it's
# own, because they live in different threads and Wx allows only the main thread to perform UI changes.
def OnPlaybackMove(self, event):
# =================================================
# Updates the cursor (vertical line) at each event:
# =================================================
self.figure.canvas.restore_region(self.background)
new_position = playback_counter*CHUNK_SIZE
self.playback_line.set_xdata(new_position)
self.axes1.draw_artist(self.playback_line)
self.canvas.blit(self.axes1.bbox)
# Playback move event (for indicating that a chunk has just been played and so the cursor must be moved):
EVT_PLAYBACK_MOVE = wx.PyEventBinder(wx.NewEventType(), 0)
class PlaybackMoveEvent(wx.PyCommandEvent):
def __init__(self, eventType=EVT_PLAYBACK_MOVE.evtType[0], id=0):
wx.PyCommandEvent.__init__(self, eventType, id)
# Callback function for audio playback (called each time the sound card needs "frame_count" more samples):
def audio_callback(in_data, frame_count, time_info, status):
global playback_counter
# In case we've run out of samples:
if playback_counter == len(audio_chunks):
print "Playback ended."
# Returns an empty chunk, thus ending playback:
return ("", pyaudio.paComplete)
else:
# Gets the next audio chunk, increments the counter and returns the new chunk:
new_chunk = audio_chunks[playback_counter]
main_window.AddPendingEvent(PlaybackMoveEvent())
playback_counter += 1
return (new_chunk, pyaudio.paContinue)
# WxGlade default initialization instructions:
if __name__ == "__main__":
gettext.install("app")
app = wx.PySimpleApp(0)
wx.InitAllImageHandlers()
main_window = PlayerFrame(None, wx.ID_ANY, "")
app.SetTopWindow(main_window)
main_window.Show()
app.MainLoop() # UI's main loop. Checks for events and stuff.
# Final lines (if we're executing here, this means the program is closing):
audio_stream.close()
p.terminate()
이 당신을 순전히 감사 : 최소한 아래로 내 프로젝트를 스트리핑
이
, 여기 한 번 돌보아 코드입니다, 잘하면 내 전체 응용 프로그램 (2000 + 선)을 고정 할 수 있습니다 너의 도움과 인내! 다행히도 이것은 나뿐 아니라 WxPython WXAgg 백엔드 블리 팅을 고민하는 다른 사람들에게 도움이되기를 바랍니다.