How can I play audio (it would be like a 1 second sound) from a Python script?
It would be best if it was platform independent, but firstly it needs to work on a Mac.
I know I could just execute the afplay file.mp3 command from within Python, but is it possible to do it in raw Python? I would also be better if it didn't rely on external libraries.
Your best bet is probably to use pygame/SDL. It's an external library, but it has great support across platforms.
pygame.mixer.init()
pygame.mixer.music.load("file.mp3")
pygame.mixer.music.play()
You can find more specific documentation about the audio mixer support in the pygame.mixer.music documentation
Try playsound which is a Pure Python, cross platform, single function module with no dependencies for playing sounds.
Install via pip:
$ pip install playsound
Once you've installed, you can use it like this:
from playsound import playsound
playsound('/path/to/a/sound/file/you/want/to/play.mp3')
Take a look at Simpleaudio, which is a relatively recent and lightweight library for this purpose:
> pip install simpleaudio
Then:
import simpleaudio as sa
wave_obj = sa.WaveObject.from_wave_file("path/to/file.wav")
play_obj = wave_obj.play()
play_obj.wait_done()
Make sure to use uncompressed 16 bit PCM files.
You can find information about Python audio here: http://wiki.python.org/moin/Audio/
It doesn't look like it can play .mp3 files without external libraries. You could either convert your .mp3 file to a .wav or other format, or use a library like PyMedia.
In pydub we've recently opted to use ffplay (via subprocess) from the ffmpeg suite of tools, which internally uses SDL.
It works for our purposes – mainly just making it easier to test the results of pydub code in interactive mode – but it has it's downsides, like causing a new program to appear in the dock on mac.
I've linked the implementation above, but a simplified version follows:
import subprocess
def play(audio_file_path):
subprocess.call(["ffplay", "-nodisp", "-autoexit", audio_file_path])
The -nodisp flag stops ffplay from showing a new window, and the -autoexit flag causes ffplay to exit and return a status code when the audio file is done playing.
edit: pydub now uses pyaudio for playback when it's installed and falls back to ffplay to avoid the downsides I mentioned. The link above shows that implementation as well.
Sorry for the late reply, but I think this is a good place to advertise my library ...
AFAIK, the standard library has only one module for playing audio: ossaudiodev.
Sadly, this only works on Linux and FreeBSD.
UPDATE: There is also winsound, but obviously this is also platform-specific.
For something more platform-independent, you'll need to use an external library.
My recommendation is the sounddevice module (but beware, I'm the author).
The package includes the pre-compiled PortAudio library for Mac OS X and Windows, and can be easily installed with:
pip install sounddevice --user
It can play back sound from NumPy arrays, but it can also use plain Python buffers (if NumPy is not available).
To play back a NumPy array, that's all you need (assuming that the audio data has a sampling frequency of 44100 Hz):
import sounddevice as sd
sd.play(myarray, 44100)
For more details, have a look at the documentation.
It cannot read/write sound files, you'll need a separate library for that.
Aaron's answer appears to be about 10x more complicated than necessary. Just do this if you only need an answer that works on OS X:
from AppKit import NSSound
sound = NSSound.alloc()
sound.initWithContentsOfFile_byReference_('/path/to/file.wav', True)
sound.play()
One thing... this returns immediately. So you might want to also do this, if you want the call to block until the sound finishes playing.
from time import sleep
sleep(sound.duration())
Edit: I took this function and combined it with variants for Windows and Linux. The result is a pure python, cross platform module with no dependencies called playsound. I've uploaded it to pypi.
pip install playsound
Then run it like this:
from playsound import playsound
playsound('/path/to/file.wav', block = False)
MP3 files also work on OS X. WAV should work on all platforms. I don't know what other combinations of platform/file format do or don't work - I haven't tried them yet.
You can see this: http://www.speech.kth.se/snack/
s = Sound()
s.read('sound.wav')
s.play()
It is possible to play audio in OS X without any 3rd party libraries using an analogue of the following code. The raw audio data can be input with wave_wave.writeframes. This code extracts 4 seconds of audio from the input file.
import wave
import io
from AppKit import NSSound
wave_output = io.BytesIO()
wave_shell = wave.open(wave_output, mode="wb")
file_path = 'SINE.WAV'
input_audio = wave.open(file_path)
input_audio_frames = input_audio.readframes(input_audio.getnframes())
wave_shell.setnchannels(input_audio.getnchannels())
wave_shell.setsampwidth(input_audio.getsampwidth())
wave_shell.setframerate(input_audio.getframerate())
seconds_multiplier = input_audio.getnchannels() * input_audio.getsampwidth() * input_audio.getframerate()
wave_shell.writeframes(input_audio_frames[second_multiplier:second_multiplier*5])
wave_shell.close()
wave_output.seek(0)
wave_data = wave_output.read()
audio_stream = NSSound.alloc()
audio_stream.initWithData_(wave_data)
audio_stream.play()
This is the easiest & best iv'e found. It supports Linux/pulseaudio, Mac/coreaudio, and Windows/WASAPI.
import soundfile as sf
import soundcard as sc
default_speaker = sc.default_speaker()
samples, samplerate = sf.read('bell.wav')
default_speaker.play(samples, samplerate=samplerate)
See https://github.com/bastibe/PySoundFile and https://github.com/bastibe/SoundCard for tons of other super-useful features.
Also on OSX - from SO, using OSX's afplay command:
import subprocess
subprocess.call(["afplay", "path/to/audio/file"])
UPDATE: All this does is specify how to do what the OP wanted to avoid doing in the first place. I guess I posted this here because what OP wanted to avoid was the info I was looking for. Whoops.
Install playsound package using :
pip install playsound
Usage:
from playsound import playsound
playsound("file location\audio.p3")
Try PySoundCard which uses PortAudio for playback which is available on many platforms.
In addition, it recognizes "professional" sound devices with lots of channels.
Here a small example from the Readme:
from pysoundcard import Stream
"""Loop back five seconds of audio data."""
fs = 44100
blocksize = 16
s = Stream(samplerate=fs, blocksize=blocksize)
s.start()
for n in range(int(fs*5/blocksize)):
s.write(s.read(blocksize))
s.stop()
Mac OS I tried a lot of codes but just this works on me
import pygame
import time
pygame.mixer.init()
pygame.init()
pygame.mixer.music.load('fire alarm sound.mp3') *On my project folder*
i = 0
while i<10:
pygame.mixer.music.play(loops=10, start=0.0)
time.sleep(10)*to protect from closing*
pygame.mixer.music.set_volume(10)
i = i + 1
It's Simple. I did it this way.
For a wav file
from IPython.display import Audio
from scipy.io.wavfile import read
fs, data = read('StarWars60.wav', mmap=True) # fs - sampling frequency
data = data.reshape(-1, 1)
Audio(data = data[:, 0], rate = fs)
For mp3 file
import IPython.display import Audio
Audio('audio_file_name.mp3')
Pypi has a list of modules for python in music. My favorite would be jython because it has more resources and libraries for music. As example of of code to play a single note from the textbook:
# playNote.py
# Demonstrates how to play a single note.
from music import * # import music library
note = Note(C4, HN) # create a middle C half note
Play.midi(note) # and play it!
To play a notification sound using python, call a music player, such as vlc. VLC prompted me to use its commandline version, cvlc, instead.
from subprocess import call
call(["cvlc", "--play-and-exit", "myNotificationTone.mp3"])
It requires vlc to be preinstalled on the device. Tested on Linux(Ubuntu 16.04 LTS); Running Python 3.5.
Try sounddevice
If you don't have the module enter
pip install sounddevice in your terminal.
Then in your preferred Python script (I use Juypter), enter
import sounddevice as sd
sd.play(audio, sr) will play what you want through Python
The best way to get the audio and samplerate you want is with the librosa module. Enter this in terminal if you don't have the librosa module.
pip install librosa
audio, sr = librosa.load('wave_file.wav')
Whatever wav file you want to play, just make sure it's in the same directory as your Python script. This should allow you to play your desired wav file through Python
Cheers,
Charlie
P.S.
Once audio is a "librosa" data object, Python sees it as a numpy array. As an experiment, try playing a long (try 20,000 data points) thing of a random numpy array. Python should play it as white noise. The sounddevice module plays numpy arrays and lists as well.
In a Colab notebook you can do:
from IPython.display import Audio
Audio(waveform, Rate=16000)
This library aims to be simple, cross-platform and have many features: https://github.com/libwinmedia/libwinmedia-py
It requires a libwinmedia shared library, which you can download in Releases tab.
You can install it using pip install libwinmedia
Example:
import libwinmedia
player = libwinmedia.Player(True)
player.set_position_callback(lambda position: print(f"{position} ms."))
media = libwinmedia.Media("test.mp3")
player.open(media)
This should work on Linux, Mac or Windows:
from preferredsoundplayer import *
soundplay("audio.wav")
Should work for mp3 also.
In Linux it will try up to 4 different methods. In Windows it uses winmm.dll. In Mac it uses afplay.
I wrote it because:
I kept having issues with cross-compatibility for playing sounds.
It also manually garbage collects calls to the winmm.dll player in Windows and appropriate closes finished sounds.
It has no dependencies, other than what comes with Windows 10, the standard Linux kernel, MacOS 10.5 or later, and the Python Standard Library.
You can install using pip install preferredsoundplayer (see project) or just utilize the source code which is a single file (source code) .
Put this at the top of your python script you are writing:
import subprocess
If the wav file IS in the directory of the python script:
f = './mySound.wav'
subprocess.Popen(['aplay','-q',f)
If the wav file IS NOT in the directory of the python script:
f = 'mySound.wav'
subprocess.Popen(['aplay','-q', 'wav/' + f)
If you want to learn more about aplay:
man aplay
I recently made my Music Player support all audio files locally. I did this by figuring out a way to use the vlc python module and also the VLC dll files.
You can check it out:
https://github.com/elibroftw/music-caster/blob/master/audio_player.py
For those who use Linux and the other packages haven't worked on MP3 files, audioplayer worked fine for me:
https://pypi.org/project/audioplayer/
from audioplayer import AudioPlayer
AudioPlayer("path/to/somemusic.mp3").play(block=True)
If you're on OSX, you can use the "os" module or "subprocess" etc. to call the OSX "play" command. From the OSX shell, it looks like
play "bah.wav"
It starts to play in about a half-second on my machine.
Simply You can do it with the help of cvlc-
I did it in this way:
import os
os.popen2("cvlc /home/maulo/selfProject/task.mp3 --play-and-exit")
/home/maulo/selfProject/task.mp3. This is the location of my mp3 file.
with the help of "--play-and-exit" you will be able to play again the sound without ending the vlc process.
I'm posting to a URL, downloading an audio file (m4a) and trying to play it from the terminal with a Python script. When I type
mplayer asdf.m4a
in the terminal it plays fine. But when I execute the following code
from mplayer import Player
player = Player()
player.loadfile('asdf.m4a')
as shown in the mplayer guide, I get the following errors:
mplayer: could not connect to socket
mplayer: No such file or directory
I've been trying to figure this out for a couple days now and it seems like it should be real simple. I don't know what's wrong. I was able to use pygame to play mp3's and ogg's but I need to play m4a and I just can't seem to get mplayer to work for me.
The only related issues I've seen suggested adding nolirc=yes to the mplayer config file. Didn't help.
Any help would be greatly appreciated.
Worst way, but could be usefull:
from subprocess import Popen, PIPE
pipes = dict(stdin=PIPE, stdout=PIPE, stderr=PIPE)
mplayer = Popen(["mplayer", "asdf.m4a"], **pipes)
# to control u can use Popen.communicate
mplayer.communicate(input=b">")
sys.stdout.flush()
Try using the absolute path to the file. If you are running this script in an IDE or debugger, sometimes it can mess up the relative path.
I would try:
import os
from mplayer import Player
player = Player()
abspath = os.path.join(os.path.dirname(__file__), 'asdf.m4a')
player.loadfile(abspath)
Im using Ubuntu 12.04. I want to access Rhythymbox using Python .
This is how I've proceeded so far:
Ive gone through this site
https://live.gnome.org/RhythmboxPlugins/WritingGuide , but it gives details on how to write plugins , which Im not interested in right now. Ive gone through a few tutorials which tells me to do this.
import dbus
session_bus = dbus.SessionBus()
proxy_obj = session_bus.get_object(
'org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Player')
But I am getting the following error
DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Rhythmbox was not provided by any .service files.
Could someone please point me in the right direction of what I would like to achieve?
A workaround, used by lyricsdownloader.py, is:
import subprocess
import shlex
proc = subprocess.Popen(shlex.split('rhythmbox-client --no-start --print-playing-format %tt')))
title, err = proc.communicate()
Note: This does not work with Ubuntu 11.10, which shipped without rhythmbox-client.
This might be useful. https://github.com/aliva/rhythmbox-microblogger
It is a twitter plugin for RhythmBox. So instead of twitter and Gtk, you can just take the current song.
from gi.repository import RB
RB.RhythmDBPropType.TITLE will give enum which you can use to get the title.
I think that you've encountered a bug in Rhythmbox DBus interface described on Launchpad. Tracker says that fix is committed, but possibly your version doesn't have that fix.
I'm having an interesting problem with threads and the tempfile module in Python. Something doesn't appear to be getting cleaned up until the threads exit, and I'm running against an open file limit. (This is on OS X 10.5.8, Python 2.5.1.)
Yet if I sort of replicate what the tempfile module is doing (not all the security checks, but just generating a file descriptor and then using os.fdopen to produce a file object) I have no problems.
Before filing this as a bug with Python, I figured I'd check here, as it's much more likely that I'm doing something subtly wrong. But if I am, a day of trying to figure it out hasn't gotten me anywhere.
#!/usr/bin/python
import threading
import thread
import tempfile
import os
import time
import sys
NUM_THREADS = 10000
def worker_tempfile():
tempfd, tempfn = tempfile.mkstemp()
tempobj = os.fdopen(tempfd, 'wb')
tempobj.write('hello, world')
tempobj.close()
os.remove(tempfn)
time.sleep(10)
def worker_notempfile(index):
tempfn = str(index) + '.txt'
# The values I'm passing os.open may be different than tempfile.mkstemp
# uses, but it works this way as does using the open() function to create
# a file object directly.
tempfd = os.open(tempfn,
os.O_EXCL | os.O_CREAT | os.O_TRUNC | os.O_RDWR)
tempobj = os.fdopen(tempfd, 'wb')
tempobj.write('hello, world')
tempobj.close()
os.remove(tempfn)
time.sleep(10)
def main():
for count in range(NUM_THREADS):
if count % 100 == 0:
print('Opening thread %s' % count)
wthread = threading.Thread(target=worker_tempfile)
#wthread = threading.Thread(target=worker_notempfile, args=(count,))
started = False
while not started:
try:
wthread.start()
started = True
except thread.error:
print('failed starting thread %s; sleeping' % count)
time.sleep(3)
if __name__ == '__main__':
main()
If I run it with the worker_notempfile line active and the worker_tempfile line commented-out, it runs to completion.
The other way around (using worker_tempfile) I get the following error:
$ python threadtempfiletest.py
Opening thread 0
Opening thread 100
Opening thread 200
Opening thread 300
Exception in thread Thread-301:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/threading.py", line 460, in __bootstrap
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/threading.py", line 440, in run
File "threadtempfiletest.py", line 17, in worker_tempfile
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/tempfile.py", line 302, in mkstemp
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/tempfile.py", line 236, in _mkstemp_inner
OSError: [Errno 24] Too many open files: '/var/folders/4L/4LtD6bCvEoipksvnAcJ2Ok+++Tk/-Tmp-/tmpJ6wjV0'
Any ideas what I'm doing wrong? Is this a bug in Python, or am I being bone-headed?
UPDATE 2009-12-14:
I think I've found the answer, but I don't like it. Since nobody was able to replicate the problem, I went hunting around our office for machines. It passed on everything except my machine. I tested on a Mac with the same software versions I was using. I even went hunting for a Desktop G5 with the EXACT same hardware and software config I had -- same result. Both tests (with tempfile and without tempfile) succeeded on everything.
For kicks, I downloaded Python 2.6.4, and tried it on my desktop, and same pattern on my system as Python 2.5.1: tempfile failed, and notempfile succeeded.
This is leading me to the conclusion that something's hosed on my Mac, but I sure can't figure out what. Any suggestions are welcome.
I am unable to reproduce the problem with (Apple's own build of) Python 2.5.1 on Mac OS X 10.5.9 -- runs to completion just fine!
I've tried both on a Macbook Pro, i.e., an Intel processor, and an old PowerMac, i.e., a PPC processor.
So I can only imagine there must have been a bug in 10.5.8 which I never noticed (don't have any 10.5.8 around to test, as I always upgrade promptly whenever software update offers it). All I can suggest is that you try upgrading to 10.5.9 and see if the bug disappears -- if it doesn't, I have no idea how this behavior difference between my machines and yours is possible.
I think your answer can be found here. You have to explicitly os.close() the file descriptor given as the first part of the tuple that mkstemp gives you.
Edit: no, the OP is already doing what is supposed to be done. I'm leaving the answer up for the nice link.
I just tested your code on my Ubuntu Linux computer here, and it worked perfectly for me.
I have one suggestion for you to try. I don't know that it will help but it can't hurt. Rewrite your code to use with:
from __future__ import with_statement
def worker_tempfile():
tempfd, tempfn = tempfile.mkstemp()
with os.fdopen(tempfd, 'wb') as tempobj:
tempobj.write('hello, world')
os.remove(tempfn)
time.sleep(10)
The with statement is supposed to make sure that the file object gets closed no matter what. Perhaps it might help?
Good luck. Great job on the question, by the way.
Why do you think the error is not genuine? You are launching 10000 threads, each opening a file, while the maximum number of open files is typically 1024 under Unix systems.
First try to keep manually track of the number of files currently open and check whether it bumps past the OS limit.
Since nobody was able to replicate the problem, I went hunting around our office for machines. It passed on everything except my machine. I tested on a Mac with the same software versions I was using. I even went hunting for a Desktop G5 with the EXACT same hardware and software config I had -- same result. Both tests (with tempfile and without tempfile) succeeded on everything.
For kicks, I downloaded Python 2.6.4, and tried it on my desktop, and same pattern on my system as Python 2.5.1: tempfile failed, and notempfile succeeded.
This is leading me to the conclusion that something's hosed on my Mac, so this isn't likely to be a problem that anyone else runs into ever.
Thanks VERY much to everyone (especially Alex Martelli) who helped on this!