Play QSound from a resource file? - python

I'm trying to load a QSound file from a qrc file but I can't find a way to do it. Is it possible?
Something like:
sound = QSound(":/audio/my_sound.ogg")
sound.play()
Unlike pixmaps, this syntax doesn't work for audio.
I'll be using pyinstaller to package the scripts and it would be useful if there was a way to include the sound files in the exe as well.

First make sure you can play an ogg file...on my system I get a console error with the ogg file...so also with the ogg resource
QSound.play("my_sound.ogg") # Error decoding source file:my_sound.ogg
QSound.play(":/audio/my_sound.ogg") # Error decoding source file::/audio/my_sound.ogg
Using a wav file works for me, so it verifies QSound can indeed play from a resource.
import sys
from PyQt5.QtCore import QCoreApplication, QFile
from PyQt5.QtMultimedia import QSound
import resources # compiled via: pyrcc5 -o resources.py resources.qrc
if __name__ == '__main__':
app = QCoreApplication(sys.argv)
# QSound.play("test.wav") # Plays fine
# Using a wav file in the resource as follows gives the following results
# <!DOCTYPE RCC>
# <RCC version="1.0">
# <qresource prefix="audio">
# <file>test.wav</file>
# </qresource>
# </RCC>
QSound.play(":/audio/test.wav") # Plays fine
sound = QSound(":/audio/test.wav")
sound.play() # Plays fine
QSound(":/audio/test.wav").play() # no sound...no error
# QFile.copy(":/audio/my_sound.ogg", "temp.ogg") # Could also copy to a local file to make sure was found in resource
sys.exit(app.exec_())

I haven't heard of qsound but i know many sound player modules such as pygame,playsound etc you can see the syntax in there sites they support play, stop, pause and much more

Related

Playing audio continuously in Python [duplicate]

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.

Playing Audio with QtMultimedia?

Part of a PyQt5 program I'm writing is to take in an audio stream and play it back. I've searched around and this is the code I have found that is said to work:
url = QtCore.QUrl.fromLocalFile('office theme.mp3')
content = QtMultimedia.QMediaContent(url)
player = QtMultimedia.QMediaPlayer()
player.setMedia(content)
player.play()
However, this does not work for me. I have tried putting the code in a variety of places (after the window.show() call, inside and outside of various classes I have, etc). I can verify that the MP3 is valid as I can play it in Clementine, VLC, and Dolphin. It was also taken directly from my Plex server, so it's definitely a valid MP3 file. I have tried converting this file to OGG and to WAV with no luck. I have also tried FLAC and AAC audio files and they do not work either.
I saw on a forum that someone suggested running a command to check if PyQt could see any audio devices. I ran the following code and it returned multiple audio output devices:
print(QtMultimedia.QAudioDeviceInfo.availableDevices(QtMultimedia.QAudio.AudioOutput))
All I need to do is take in a reference to an audio file (eventually opened from a file dialogue, but I'll cross that bridge when I come to it) and play it. Am I doing it incorrectly? I am by no means an expert on PyQt and have been experimenting for a couple of days only.
I'm currently running on Antergos Arch Linux.
You have to pass the complete path, but if you want to just pass the name of the file and that the program adds the rest you can use QDir::current():
import sys
from PyQt5 import QtCore, QtWidgets, QtMultimedia
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
filename = 'office theme.mp3'
fullpath = QtCore.QDir.current().absoluteFilePath(filename)
url = QtCore.QUrl.fromLocalFile(fullpath)
content = QtMultimedia.QMediaContent(url)
player = QtMultimedia.QMediaPlayer()
player.setMedia(content)
player.play()
sys.exit(app.exec_())

Play m4a in python script with mplayer

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)

Python/gtk gtk.gdk.pixbuf_new_from_file() always fails

Hi I'm trying to use a python gtk script under python 2.7.3; on Linux (under X11) and it needs to load images into pixbufs from disk.
However, python returns a glib.GError: "Couldn't recognize the image file format" regardless of what type of file I try to open;
eg: with: pixbuf = gtk.gdk.pixbuf_new_from_file( filename )
I can do a gtk.gdk.pixbuf_get_formats(), and in the list of dictionaries which is returned, the formats I have tried to load are listed .png, .ppm, .jpg.
When I try a gtk.gdk.pixbuf_get_file_info( filename ), though, it returns None.
Other GTK based programs such as Gimp, load these same images just fine; and gtk scripts that don't load icons from disk, but which draw buttons -- etc. work just fine.
How would I search for the cause of this malfunction?
Am I missing some kind of a mime-type file ?
Is there an alternate path using other gtk function calls that might accomplish the loading another way?
This works for me:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gtk
def win_with_image():
pixbuf = gtk.gdk.pixbuf_new_from_file("photo.png")
print pixbuf
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
image = gtk.Image()
image.set_from_pixbuf(pixbuf)
win.add(image)
win.connect("destroy", gtk.main_quit)
win.show_all()
if __name__ == '__main__':
win_with_image()
gtk.main()
If this doesn`t work for you, try to:
start google and type your error and choose the second link (http://aptosid.com/index.php?name=PNphpBB2&file=viewtopic&t=2246), in general this helps almost always.
reinstall libglib
install gtk (maybe some graphical libs - libpng, libjpeg, f.e.)
reinstall python/gtk package
fix broken package repository
change files permissions

How to watch directory for file modifications

I'm on a mac. I've been using Launchd's WatchPaths directive to watch a directory for file changes. My script only triggers when a file is added or deleted from the watched directory.
However, the script does not trigger when a file is modified..
Essentially, I'm trying to create a DIY Dropbox for syncing my Sites folder.
Is there a way to do this via launchd, bash or python?
I think linux has something like inotify, but I am not aware of a solution for mac.
I tried my hand at the problem, using the MacFSEvents
package (available on PyPI, too):
import os
from fsevents import Observer, Stream
def callback(file_event):
print file_event.name # the path of the modified file
def main():
observer = Observer()
observe_path = os.getcwd() # just for this example
stream = Stream(callback, observe_path, file_events=True)
observer.start()
observer.schedule(stream)
if __name__ == '__main__':
main()
This will call callback any time a file is created, modified, or deleted (you can check which event occurred using the value of file_event.mask).
Note that you will probably want to observe on a thread outside of the main thread (the above program refuses to quit, even on KeyboardInterrupt). More information on the API can be found in the MacFSEvents README. Hope this helps!

Categories