pyaudio可以播放.mp3文件吗?如果是的话,我可以要求写一个例子吗?如果不是,将 .mp3 转换为 .wav 的最简单方法是什么?
我尝试使用 PyDub,可以获取我的 .wav 文件,但是当我尝试使用 PyAudio 播放它时,出现以下错误:
File "C:\Python33\lib\wave.py", line 130, in initfp
raise Error('file does not start with RIFF id')
wave.Error: file does not start with RIFF id
与其他 .wav 样本(其中 not 从 mp3 转换而来)如果效果良好。
我正在使用 gTTS 库将我的应用程序的文本转换为语音。它创建了我需要播放的短 .mp3 文件。现在我只使用
os.system("start english.mp3")
我想找到更好的方法来做到这一点。首先我不想受到平台的限制。其次,我不喜欢文件开始播放时弹出播放器,我希望它留在后台。
我尝试为此找到简约的解决方案,因为除了简单的演奏之外我不需要任何其他东西。
UPD:我设法用 pyglet 玩它。看起来不错,只是时间太长了……在听到声音之前我有大约 10 秒的延迟。而且它不能与线程一起正常工作(我想在程序仍在运行时播放.mp3)。 有没有办法让播放器停留在后台而不是在所有其他窗口上弹出?
这是简短的答案:
ffmpeg -i song.mp3 -acodec pcm_u8 -ar 22050 song.wav
TL;DR:我假设您想在没有前端的情况下播放音频文件。
有一个库,名为 The Snack Sound Toolkit,它可以完美地完成此操作:
player = Sound()
player.read('song.wav')
player.play()
我知道我在两个流中都使用了它,并且我认为 mp3 文件,不记得如何或在哪个项目中,但我可能需要研究一下。认为这与咕哝有关..无论如何..
如果您完全可以使用 pyglet(这是我听说的)之类的前端代码,那么您需要一些选项和一些代码才能使其尽可能地工作。
import pyglet
from pyglet.gl import *
pyglet.options['audio'] = ('openal', 'directsound', 'silent')
music = pyglet.resource.media('music.mp3')
music.play()
pyglet.app.run()
依赖关系: * OpenAL(用于跨平台兼容性)
您的线程问题是 Pyglet 是一个 OpenGL 库。这对线程来说一点也不友好。除非你让 Pyglet 获取你需要的数据。 另外,您很可能会遇到“pyglet 阻止我的代码”的问题(所有图形库都会这样做。所以这里有一个解决方法)
import pyglet, os
from time import sleep
from threading import *
from pyglet.gl import *
pyglet.options['audio'] = ('openal', 'directsound', 'silent')
class worker(Thread):
def __init__(self):
Thread.__init__(self)
self.audio_frames = []
self.run()
def add_frame(self, filename):
self.audio_frames.append(filename)
def get_frame(self):
if len(self.audio_frames) > 0:
return self.audio_frames.pop(0)
return None
def run(self):
while 1:
for root, folders, files in os.walk('./audio_files/'):
for f in file:
self.add_frame(f)
sleep(1)
class AudioWindow(pyglet.window.Window):
def __init__(self):
self.audio_worker = worker()
def render(self):
frame = self.audio_frames.get_frame()
if frame:
music = pyglet.resource.media('music.mp3')
music.play()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
这完全没问题,因为您没有尝试从另一个线程更新图形。
相反,您正在获取有关图形术语的数据。
然而,您可以从worker()中更新AudioWindow()内的变量/列表/数组/w/e,没有任何问题,您唯一不能做的就是从图形类外部调用任何图形函数。
然而,最理想的方法是继续老派,使用 pyaudio 并手动调整音频帧。这样,只要正确解码数据,您就可以读取任何音频文件。 我自己用这个(小心点,因为它不漂亮)来传输音频:
import pyaudio
import wave
CHUNK_SIZE = 1024
FORMAT = pyaudio.paInt16
RATE = 44100
p = pyaudio.PyAudio()
output = p.open(format=FORMAT,
channels=1,
rate=RATE,
output=True) # frames_per_buffer=CHUNK_SIZE
with open('audio.wav', 'rb') as fh:
while fh.tell() != FILE_SIZE: # get the file-size from the os module
AUDIO_FRAME = fh.read(CHUNK_SIZE)
output.write(AUDIO_FRAME)
这应该会产生接近音频的东西:)
波在示例等中被过度利用的原因是因为它基本上是未编码的声音流,而不是以任何方式编码。然而 mp3 是一种高度压缩的音频格式,format 是这里的关键词。您需要某种方式来读取 mp3 数据并将其从紧凑状态反转为可以挤入扬声器的数据流。
我不是音频专家,但这是对音频如何工作的粗略解释,由某人摆弄了一下并让它工作。
如果您希望使用 Pyglet 播放压缩音频文件,您可以使用 AVbin。用于压缩文件的库。
我的要点是这里,享受吧
#!/usr/bin/env python3
"""
Play a file continously, and exit gracefully on signal
Based on https://github.com/steveway/papagayo-ng/blob/working_vol/SoundPlayer.py
@author Guy Sheffer (GuySoft) <guysoft at gmail dot com>
"""
import signal
import time
import os
import threading
import pyaudio
from pydub import AudioSegment
from pydub.utils import make_chunks
class GracefulKiller:
kill_now = False
def __init__(self):
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self,signum, frame):
self.kill_now = True
def play(self):
"""
Just another name for self.start()
"""
self.start()
def stop(self):
"""
Stop playback.
"""
self.loop = False
class PlayerLoop(threading.Thread):
"""
A simple class based on PyAudio and pydub to play in a loop in the backgound
"""
def __init__(self, filepath, loop=True):
"""
Initialize `PlayerLoop` class.
PARAM:
-- filepath (String) : File Path to wave file.
-- loop (boolean) : True if you want loop playback.
False otherwise.
"""
super(PlayerLoop, self).__init__()
self.filepath = os.path.abspath(filepath)
self.loop = loop
def run(self):
# Open an audio segment
sound = AudioSegment.from_file(self.filepath)
player = pyaudio.PyAudio()
stream = player.open(format = player.get_format_from_width(sound.sample_width),
channels = sound.channels,
rate = sound.frame_rate,
output = True)
# PLAYBACK LOOP
start = 0
length = sound.duration_seconds
volume = 100.0
playchunk = sound[start*1000.0:(start+length)*1000.0] - (60 - (60 * (volume/100.0)))
millisecondchunk = 50 / 1000.0
while self.loop:
self.time = start
for chunks in make_chunks(playchunk, millisecondchunk*1000):
self.time += millisecondchunk
stream.write(chunks._data)
if not self.loop:
break
if self.time >= start+length:
break
stream.close()
player.terminate()
def play(self):
"""
Just another name for self.start()
"""
self.start()
def stop(self):
"""
Stop playback.
"""
self.loop = False
def play_audio_background(audio_file):
"""
Play audio file in the background, accept a SIGINT or SIGTERM to stop
"""
killer = GracefulKiller()
player = PlayerLoop(audio_file)
player.play()
print(os.getpid())
while True:
time.sleep(0.5)
# print("doing something in a loop ...")
if killer.kill_now:
break
player.stop()
print("End of the program. I was killed gracefully :)")
return
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(add_help=True, description="Play a file continously, and exit gracefully on signal")
parser.add_argument('audio_file', type=str, help='The Path to the audio file (mp3, wav and more supported)')
args = parser.parse_args()
play_audio_background(args.audio_file)
我们可以使用VLC绑定来支持更多文件格式。
https://gist.github.com/elibroftw/b803191c3b919a67b6a65668572680cc
您可以安装 pyav 库,它可以解码任何文件中的音频,包括 mp3、mp4、wav...
然后你用 pyaudio 播放音频帧,如下所示:
import av
import pyaudio
container = av.open("song.mp3")
audio_stream = container.streams.audio[0]
samplerate = audio_stream.rate
channels = audio_stream.channels
p = pyaudio.PyAudio()
audio_device = p.open(format=pyaudio.paFloat32,
channels=channels,
rate=samplerate,
output=True)
while True:
try:
frame = next(container.decode(audio=0))
audio_data = frame.to_ndarray().astype('float32')
interleaved_data = audio_data.T.flatten().tobytes()
audio_device.write(interleaved_data)
time = round(float(frame.pts * audio_stream.time_base), 2)
print(time) # display current time
except (StopIteration, av.error.EOFError):
break
audio_stream.close()
container.close()
audio_device.stop_stream()
audio_device.close()
p.terminate()
pyav 容器中有更多功能,例如查找,这将帮助您从任何部分播放文件。
如果您使用的是安装了 Windows Media Player 的 Windows,这里有一个简单的解决方案:
wmp = Dispatch('WMPlayer.OCX')
wmp.settings.autoStart = True
wmp.settings.volume = 50
wmp.URL = r"tts_cache\gtts.mp3" #auto-start
while wmp.PlayState != 1: #wait until stopped
pythoncom.PumpWaitingMessages()
time.sleep(0.1)
wmp.URL = ""
不幸的是,当我尝试用新文件替换 mp3 文件时,有时无法写入。我认为 WMP 会阻止它或其他什么。所以我决定每次都创建一个新文件并将其称为“缓存”。)