Playing .mp3 files with PyAudio

前端 未结 4 1451
忘掉有多难
忘掉有多难 2020-12-17 05:58

Can pyaudio play .mp3 files? If yes, may I ask to write an example please. If no, what is the simplest way to convert .mp3 to .wav?

I have tried to use PyDub, could

相关标签:
4条回答
  • 2020-12-17 06:46

    My gist is here, enjoy

    #!/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)
    
    0 讨论(0)
  • 2020-12-17 06:53

    If you're using Windows with Windows Media Player installed here's a simple solution:

    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 = ""
    

    Unfortunately when i try to replace mp3 file with a new one it sometimes cannot be written. I think WMP blocks it or something. So i decided to create a new file every time and call it "caching".)

    0 讨论(0)
  • 2020-12-17 06:56

    We can use VLC bindings to support more file formats.

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

    0 讨论(0)
  • 2020-12-17 06:58

    Here's the short answer:

    ffmpeg -i song.mp3 -acodec pcm_u8 -ar 22050 song.wav
    

    TL;DR: I'm assuming you want to play an audio file, without a front-end.

    There's a library for that, called The Snack Sound Toolkit which does this beautifully:

    player = Sound() 
    player.read('song.wav') 
    player.play()
    

    I know I used this with both streams and I think mp3 files, can't remember how or in which project, I might have to look into this though. Think it was mumble related.. anyhow..

    If you're completely fine with using a front-end code such as pyglet (which is my pick of the heard), you need some options and some code for this to work as best as possible.

    import pyglet
    from pyglet.gl import *
    pyglet.options['audio'] = ('openal', 'directsound', 'silent')
    
    music = pyglet.resource.media('music.mp3')
    music.play()
    
    pyglet.app.run()
    

    Dependencies: * OpenAL (for cross-platform compatibility)

    Your problem with threading, is that Pyglet is an OpenGL library. Which doesn't take too kindly to Threading at all. Unless you let Pyglet fetch the data you need. Also, you will most likely bump into the problem of "pyglet blocks my code" (all graphic libraries do. so here's a workaround)

    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()
    

    This is totally fine, since you're not trying to update THE graphics from another thread.
    Instead, you're fetching data on the graphics terms. You can however from worker() update a variable/list/array/w/e inside AudioWindow() without any problems, the only thing you can't do is call any graphical function from outside the graphic-class.

    Without front-end the fun approach:

    The most ideal way however, would to be going old-school as hell and use pyaudio and fiddle with audio frames manually. This way you can read literally any audio-file as long as you decode the data properly. I use this one(Tread lightly, cause it ain't pretty) for transporting audio myself:

    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)
    

    This should produce something close to audio :)
    The reason why wave is so overutilized in examples etc, is because it's basically a unencoded stream of sound, not encoding in any way. mp3 however is a heavily compressed audio format, format being the key-word here. You need some way of read the mp3 data and reverse it from a compact state into a stream of data which you can squeeze into the speakers.

    I'm no audio expert, but this is a rough explanation of how audio works from someone fiddling about with it for a bit and got it working.

    Last note:

    If you're expecting to play compressed audio-files with Pyglet, you can use AVbin. A library used for compressed files.

    0 讨论(0)
提交回复
热议问题