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.