用PyAudio播放.mp3文件

时间:2014-10-31 11:33:44

标签: python-3.x mp3 wav pyglet pyaudio

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示例(其中从mp3转换)如果运行良好。

我正在使用gTTS库为我的应用程序将文本转换为语音。它会创建我需要播放的短.mp3文件。现在我只使用

os.system("start english.mp3")

我想找到一个更好的方法来做到这一点。首先,我不想限制平台。其次,我不喜欢当文件开始播放时播放器弹出,我希望它留在后台。

我试图为此找到极简主义的解决方案,因为除了简单的游戏之外,我不需要任何其他东西。

UPD:我设法用pyglet播放它。看起来很好,除了它需要太长时间......我听到声音之前有大约10秒的延迟。并且它不适用于线程(我想在程序运行时播放.mp3)。 有没有办法让玩家留在后台而不是弹出所有其他窗户?

4 个答案:

答案 0 :(得分:7)

这是简短的回答:

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()中的变量/ list / array / w / e而不会出现任何问题,您唯一不能做的就是从图形类外部调用任何图形函数。

没有前端的有趣方法:

然而,最理想的方式是将老派当作地狱并使用pyaudio并手动调整音频框架。这样,只要正确解码数据,您就可以读取任何音频文件。我自己传输音频时I use this one(轻轻一点,因为它并不吝啬):

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)

这应该产生接近音频的东西:) wav在例子等中如此过分的原因是因为它基本上是一个未编码的声音流,而不是以任何方式编码。然而,mp3是一种高度压缩的音频格式,格式是这里的关键词。您需要某种方式来读取mp3数据并将其从紧凑状态转换为可以挤入扬声器的数据流。

我不是音频专家,但这是一个粗略的解释,说明音频是如何起作用的,因为有些人正在使用音频并使其工作。

最后一点:

如果您希望使用Pyglet播放压缩音频文件,可以使用AVbin。用于压缩文件的库。

答案 1 :(得分:2)

我的要点是here,享受

#!/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)

答案 2 :(得分:0)

我们可以使用VLC绑定来支持更多文件格式。

https://gist.github.com/elibroftw/b803191c3b919a67b6a65668572680cc

答案 3 :(得分:-3)

如果您使用安装了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可以阻止它。所以我决定每次都创建一个新文件并称之为“缓存”。)