- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在构建一个项目,该项目需要用于特定目的的音频播放器。我目前正在使用 WxPython、Matplotlib 和 PyAudio 包。我还在使用 Matplotlib 的 WXAgg 后端 (backend_wxagg
)。
基本思想非常简单:音频数据将绘制在主窗口上并同时通过 PyAudio 播放,同时在绘图上显示播放进度 — 我打算用动画垂直线来做(水平滑动光标),例如您在 Audacity 中看到的那种类型。我已经尝试过通用的 Matplotlib 动画示例,还有其他一些通过网络传播的示例,但它们要么太慢(没有 blitting),要么依赖于 FuncAnimation(一种不适合我的项目的架构),或者利用我正在尝试使用的技术(目前还没有用)。
屏幕上确实有东西在移动,但整体画面一团糟……在我的 Ubuntu 16 桌面上,一个白色填充矩形出现在绘图上,而 < strong>black filled rectangle 出现在我的 Mint 笔记本电脑上。尽管已经努力工作了几天并且几乎让它工作了,但现在是时候谦虚地向您寻求帮助了...:/
我坚持使用 blit()
方法,因为据我所知 (i) 允许我刷新自定义事件下的绘图(在本例中为音频帧消耗)和 ( ii) 具有良好的性能(由于数据集的大小可变,这是一个问题)。
将我的项目精简到最低限度,这里是代码,一旦处理完毕,有望让我修复整个应用程序(2000 多行):
# -*- 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()
非常感谢您的帮助和耐心等待!希望这不仅能帮助我,还能帮助其他与 WxPython WXAgg 后端 blitting 作斗争的人。
最佳答案
经过更多的研究,我终于发现解决方案是在从bbox有效复制之前调用canvas对象的draw()
方法。因此,这里的中间线是答案(其他线仅作为放置修复的正确位置的引用):
(...)
self.Layout()
self.figure.canvas.draw() # THIS is the solution.
self.background = self.figure.canvas.copy_from_bbox(self.axes1.bbox)
但我必须在这里补充一点,虽然这可能适用于某些情况,但您的绘图调整大小的任何情况都可能再次导致图像损坏。因此,为了解决这个问题,将您的方法绑定(bind)到图形 Canvas 的 "resize_event"
,并在您的方法内部强制重绘和新副本:
self.playback_line = self.axes1.axvline(color="y", animated=True)
# New line here:
self.figure.canvas.mpl_connect("resize_event", self.on_resize_canvas)
# New method here:
def on_resize_canvas(self, event):
self.figure.canvas.draw()
self.background = self.figure.canvas.copy_from_bbox(self.axes1.bbox)
(...)
好了!这个问题占用了我项目的大量时间,所以我特意与其他人分享解决方案,因为这可能是 Internet 上第一个使用 WxPython、Matplotlib 和 PyAudio 提供的功能性音频播放器模板。希望你觉得它有用!
关于python - Matplotlib:音频播放器光标(垂直滑动线)的动画在 WxPython 上看起来很破损,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39523011/
谁能详细说明以下问题? 蓝牙堆栈如何处理音频数据? 如何处理音频命令? 我们需要任何服务来处理音频数据吗? 提前致谢。 最佳答案 基本上,通过 BLE 的语音命令需要: 一些用于减少所需带宽的音频编解
我正在使用Player库以实现全屏视频播放。我相信它在幕后使用 AVFoundation。 我可以使用Float(self.player.maximumDuration)来实现视频的完整持续时间。但是
我正在制作一种宏记录器/播放器 我已经使用 java.awt.Robot() 等实用程序完成播放器部分,它模拟基本的人类鼠标/键盘输出命令,读取 XML 文件。 我卡在了必须记录该 XML 文件的部分
我目前有以下代码可以在页面上播放 youtube 视频。 //Load player api asynchronously. var tag = document.createElement('s
我需要提供音频内容(但不是音乐,更像是播客;人类语音),我正在考虑使用基于 Flash 的播放器让用户无需下载即可收听内容。 我需要一个免费的可嵌入 Flash 的 mp3 播放器。有什么建议? 因为
html5 player/api 更新了吗?事件 SC.Widget.Events.PLAY, SC.Widget.Events.PAUSE, SC.Widget.Events.FINISH, htm
我想在 Lubuntu VMware 中自动打开和关闭 vlc 播放器。我试过一个shell脚本代码,如: vlc rtmp://code sleep(5) exit 0 or vl
我有一个只支持纵向模式的应用程序,它有一个表格,每个单元格包含一个标题和一个带有 YouTube 视频的 web View 。 现在您将如何让 Youtube 播放器同时处于横向和纵向模式?
我正在尝试在我的应用程序中使用 YouTube 播放器 API,但我不知道如何确定视频是否为直播。如果有人知道如何获得视频的真实持续时间。 更新: 我想出了一种方法来确定内容是否是实时的,我使用我的后
我想创建一个能够播放 YouTube 视频的音频并将下载的内容保存在本地缓存中的应用程序,因此当用户决定恢复或再次播放视频时,它不必再次下载部分视频而只需下载剩余部分(用户可以决定如何处理缓存,以及如
我希望我的页面将 div 显示为模态,然后播放 YouTube 视频。我能够按预期播放视频(下面的代码),但是当我在过滤操作时切换到隐藏的 div 时,页面加载时隐藏的 div 不会将 data-sr
我正在尝试使用 AngularJS 和 WP API 构建 SPA。我使用部分在 ng-view 中加载我需要通过路由显示的所有内容。在此基础上,我添加了 Plangular,它是一个使用 Sound
我找到了一个不错的 HTML 5 音频播放器,它带有基于 plyr 的播放列表和艺术品。它在我的桌面浏览器上运行良好,但在我的移动设备 (iOS) 上,按播放后无法播放。有一个codepen来演示:
我正在尝试通过pyglet在Python 3中播放歌曲。我可以播放和停止播放一首歌曲,但是当我尝试播放下一首歌曲时会产生错误。 I followed these instructions.我将在tki
如何将嵌入的 Vimeo 视频重置为播放完毕后的加载状态? Vimeo API 提供了卸载方法 player.api("unload") 但它不适用于非 Flash 播放器。 最佳答案 使用Vimeo
我有一个用于音频录制和播放的网络应用程序。为此,我正在使用 html5 播放器。 现在我必须开发 Phonegap Android 应用程序。我已将插件(org.apache.cordova.medi
有人知道如何像 SuperFlix 一样将自己的字幕加载到 Netflix 播放器吗?关于 Netflix HTML5 播放器的信息很少,其中之一是我应该可以使用 操作播放器 netflix.cadm
如何将新的黑色 YouTube 播放器嵌入到我的网站(刚刚推出的网站)中? 我以前曾问过这个问题,但它已关闭,因为在投票否决和关闭之前没有人愿意真正阅读该问题。不,我没有问如何嵌入V2或V3播放器,我
几个小时以来,我一直在尝试添加一种打开我的 mp3 文件的方法并在队列中一一打开它们。但我不知道该怎么做。当涉及到单个文件时,我打开并播放不是问题。所以我正在考虑 Media(JavaFX) 类中的线
我知道这个函数 (setFullscreen) 只适用于 HTML5,但它对我不起作用。这是我使用的方式: setFullscreen: true 我希望 JW Player 在页面加载后立即以全屏模
我是一名优秀的程序员,十分优秀!