PySImpleGUI -> How to play audio in the Background - python

I'm using PySimpleGUI for creation of a simple program where a music should be played in the background of the GUI, but the problem is that Audio is played first and after that only the GUI appears, Is there any method to solve this Issue ?
Here I'm using pydub for playing audio since others may result in an error when converted to .exe
from pydub import AudioSegment
from pydub.playback import play
from PySimpleGUI import *
st="W"
if st=="W":
path_to_file="congratulation.mp3"
song = AudioSegment.from_mp3(path_to_file)
elif st=="F":
path_to_file="fail.mp3"
song = AudioSegment.from_mp3(path_to_file)
layout=[[Text("You've "+st)],[Button("OK")]]
window=Window("Test",layout)
while True:
play(song)
e,v=window2.read()
if e==None or e=="OK":
exit()

Example code to play mp3, but only for non-stop play even after window closed, or next song played. Bugs or functions of pydub ignored here.
import threading
from pathlib import Path
from pydub import AudioSegment
from pydub.playback import play
import PySimpleGUI as sg
font = ('Courier New', 11)
sg.theme('DarkBlue3')
sg.set_options(font=font)
layout = [
[sg.Text('Song file to play')],
[sg.Input(enable_events=True, key='-SONG-'),
sg.FileBrowse(file_types=(("All MP3 Files", "*.mp3"),))],
[sg.Button('Play'), sg.Button('Exit')],
]
window = sg.Window("Pydub MP3 Player", layout, finalize=True)
song = None
while True:
event, values = window.read()
if event in (sg.WINDOW_CLOSED, 'Exit'):
break
elif event == '-SONG-':
path_to_file = values['-SONG-']
if Path(path_to_file).is_file():
try:
song = AudioSegment.from_mp3(path_to_file)
except:
pass
elif event == 'Play':
if song:
threading.Thread(target=play, args=(song,), daemon=True).start()
window.close()

I believe the easiest way to solve your problem would be to create a thread that plays the sound.
import threading as thread
# Your code
while True:
thread.start_new_thread(play, (song, ))
# The rest of your code
The above should work, if what you wanted to do was to play the sound in the background while the program kept going. This wouldn't allow you to stop the sound in the middle, however, and it could feasibly play several overlapping instances of the sound if the loop runs several times in quick succession.
Pydub has a closed issue regarding something similar: https://github.com/jiaaro/pydub/issues/160
The answer there is a bit more comprehensive than what you asked for in your question, though.
Edited to add: I believe there are other, non-default players that does this or something similar by default, but if the sound is a simple, uplifting "ding" or a sad buzzer, more complex solutions could be superfluous.

Related

|src/libmpg123/id3.c:559| error: No extra frame text / valid description? [duplicate]

I'm trying to play sound files (.wav) with pygame but when I start it I never hear anything.
This is the code:
import pygame
pygame.init()
pygame.mixer.init()
sounda= pygame.mixer.Sound("desert_rustle.wav")
sounda.play()
I also tried using channels but the result is the same
For me (on Windows 7, Python 2.7, PyGame 1.9) I actually have to remove the pygame.init() call to make it work or if the pygame.init() stays to create at least a screen in pygame.
My example:
import time, sys
from pygame import mixer
# pygame.init()
mixer.init()
sound = mixer.Sound(sys.argv[1])
sound.play()
time.sleep(5)
sounda.play() returns an object which is necessary for playing the sound. With it you can also find out if the sound is still playing:
channela = sounda.play()
while channela.get_busy():
pygame.time.delay(100)
I had no sound from playing mixer.Sound, but it started to work after i created the window, this is a minimal example, just change your filename, run and press UP key to play:
WAVFILE = 'tom14.wav'
import pygame
from pygame import *
import sys
mixer.pre_init(frequency=44100, size=-16, channels=2, buffer=4096)
pygame.init()
print pygame.mixer.get_init()
screen=pygame.display.set_mode((400,400),0,32)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key==K_ESCAPE:
pygame.quit()
sys.exit()
elif event.key==K_UP:
s = pygame.mixer.Sound(WAVFILE)
ch = s.play()
while ch.get_busy():
pygame.time.delay(100)
pygame.display.update()
What you need to do is something like this:
import pygame
import time
pygame.init()
pygame.mixer.init()
sounda= pygame.mixer.Sound("desert_rustle.wav")
sounda.play()
time.sleep (20)
The reason I told the program to sleep is because I wanted a way to keep it running without typing lots of code. I had the same problem and the sound didn't play because the program closed immediately after trying to play the music.
In case you want the program to actually do something just type all the necessary code but make sure it will last long enough for the sound to fully play.
import pygame, time
pygame.mixer.init()
pygame.init()
sounda= pygame.mixer.Sound("beep.wav")
sounda.play()
pygame.init() goes after mixer.init(). It worked for me.
I had the same problem under windows 7. In my case I wasn't running the code as Administrator. Don't ask me why, but opening a command line as administrator fixed it for me.
I think what you need is pygame.mixer.music:
import pygame.mixer
from time import sleep
pygame.mixer.init()
pygame.mixer.music.load(open("\windows\media\chimes.wav","rb"))
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
sleep(1)
print "done"
You missed to wait for the sound to finish. Your application will start playing the sound but will exit immediately.
If you want to play a single wav file, you have to initialize the module and create a pygame.mixer.Sound() object from the file. Invoke play() to start playing the file. Finally, you have to wait for the file to play.
Use get_length() to get the length of the sound in seconds and wait for the sound to finish:
(The argument to pygame.time.wait() is in milliseconds)
import pygame
pygame.mixer.init()
sounda = pygame.mixer.Sound('desert_rustle.wav')
sounda.play()
pygame.time.wait(int(sounda.get_length() * 1000))
Alternatively you can use pygame.mixer.get_busy to test if a sound is being mixed. Query the status of the mixer continuously in a loop.
In the loop, you need to delay the time by either pygame.time.delay or pygame.time.Clock.tick. In addition, you need to handle the events in the application loop. See pygame.event.get() respectively pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
import pygame
pygame.init()
pygame.mixer.init()
sounda = pygame.mixer.Sound('desert_rustle.wav')
sounda.play()
while pygame.mixer.get_busy():
pygame.time.delay(10)
pygame.event.poll()
Your code plays desert_rustle.wav quite fine on my machine (Mac OSX 10.5, Python 2.6.4, pygame 1.9.1). What OS and Python and pygame releases are you using? Can you hear the .wav OK by other means (e.g. open on a Mac's terminal or start on a Windows console followed by the filename/path to the .wav file) to guarante the file is not damaged? It's hard to debug your specific problem (which is not with the code you give) without being able to reproduce it and without having all of these crucial details.
Just try to re-save your wav file to make sure its frequency info. Or you can record a sound to make sure its frequency,bits,size and channels.(I use this method to solve this problem)
I've had something like this happen. Maybe you have the same problem? Try using an absolute path:
import pygame
pygame.init()
pygame.mixer.init()
sounda= pygame.mixer.Sound("/absolute_path/desert_rustle.wav")
sounda.play()
Where abslute_path is obviously replaced with your actual absolute path ;)
good luck.
import pygame
pygame.init()
sound = pygame.mixer.Sound("desert_rustle.wav")
pygame.mixer.Sound.play(sound)
This will work on python 3
5 years late answer but I hope I can help someone.. :-)
Firstly, you dont need the "pygame.init()"-line.
Secondly, make a loop and play the sound inside that, or else pygame.mixer will start, and stop playing again immediately.
I got this code to work fine on my Raspberry pi with Raspbian OS.
Note that I used a while-loop that continues to loop the sound forver.
import pygame.mixer
pygame.mixer.init()
sounda = pygame.mixer.Sound("desert_rustle.wav")
while True:
sounda.play()
Just try:
import pygame.mixer
from time import sleep
pygame.mixer.init()
pygame.mixer.music.load(open("\windows\media\chimes.wav","rb"))
print ""
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
sleep(1)
print "done"
This should work. You just need to add print ""and the sound will have
had time to load its self.
Many of the posts are running all of this at toplevel, which is why the sound may seem to close. The final method will return while the sound is playing, which closes the program/terminal/process (depending on how called).
What you will eventually want is a probably a class that can call either single time playback or a looping function (both will be called, and will play over each other) for background music and single sound effects.
Here is pattern, that uses a different event loop / run context other than Pygame itself, (I am using tkinter root level object and its init method, you will have to look this up for your own use case) once you have either the Pygame.init() runtime or some other, you can call these methods from your own logic, unless you are exiting the entire runtime, each file playback (either single use or looping)
this code covers the init for ONLY mixer (you need to figure our your root context and where the individual calls should be made for playback, at least 1 level inside root context to be able to rely on the main event loop preventing premature exit of sound files, YOU SHOULD NOT NEED TIME.SLEEP() AT ALL (very anti-pattern here).... ALSO whatever context calls the looping forever bg_music, it will probably be some 'level' or 'scene' or similar in your game/app context, when passing from one 'scene' to the next you will probably want to immediately replace the bg_music with the file for next 'scene', and if you need the fine-grained control stopping the sound_effect objects that are set to play once (or N times)....
from pygame import mixer
bg_music = mixer.Channel(0)
sound_effects = mixer.Channel(1)
call either of these from WITHIN your inner logic loops
effect1 = mixer.Sound('Sound_Effects/'+visid+'.ogg')
sound_effects.play(effect1, 0)
sound1 = mixer.sound('path to ogg or wav file')
bg_music.play(sound1, -1) # play object on this channel, looping forever (-1)
do this:
import pygame
pygame.mixer.init()
pygame.mixer.music.load("desert_rustle.wav")
pygame.mixer.music.play(0)
I think that your problem is that the file is a WAV file.
It worked for me with an MP3. This will probably work on python 3.6.

Pygame mixer causes tkinter to freeze

to-speech module to say a list and the audio module I am using is mixer form pygame. When i start playing the audio the tkinter program can not be interacted with and if I move the window the audio stops. I think some sort of threading may fix this but I am not sure how that works.
Problem
Tkinter window freezes.
Goal
To be able to interact with the program when audio is playing.
Code
import tkinter as tk
from gtts import gTTS
from io import BytesIO
import pygame
window = tk.Tk()
window.geometry("800x600")
window.resizable(0, 0)
def speak(text,language="en",accent="com"):
mp3_fp = BytesIO()
phrase = gTTS(text=text,lang=language,tld=accent)
phrase.write_to_fp(mp3_fp)
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(mp3_fp,"mp3")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.delay(10)
pygame.event.poll()
def play():
data = list([boy,girl,pink,blue])
for i in data:
speak(i)
window.mainloop()
Code Explanation
The play()passes each value in the dictionary E.g. Boy, Girl to speak() separately where they are played one by one consecutively
In the speak() the audio from the text-to-speech module (gTTS) gets passed to pygame.mixer where it is played when the last word has been said.
First of all you need to post the minimum code necessary to demonstrate your problem. You haven't done that. Fortunately, this is a well-known "problem" and easy to answer without importing four libraries and building an application around what you've provided in order to answer it.
Secondly -- in the code that you have provided, data = list[boy,girl,pink,blue] isn't even proper syntax. It should be data = list(["boy", "girl", "pink", "blue"]). You have to post running code to get the best answers.
Lecture over.
The issue is that conventional unmodified Python runs in a single thread. If you want to know why that is then I invite you to research the GIL (Global Interpreter Lock) for more background.
There's only one thread, and when PyGame is doing something then the thread is busy and TkInter stops responding to input, and vice versa -- when TkInter is in the middle of something you'll find that PyGame stops responding.
You can demonstrate this phenomenon with this:
import tkinter as tk
import time
def delay():
time.sleep(10)
def main():
root = tk.Tk()
tk.Button(root, text="Test Me", command=delay).pack(expand=True, fill=tk.BOTH)
root.mainloop()
if __name__ == "__main__":
main()
When you run this you'll see that the button is depressed, the button stays depressed while the application goes to sleep, and the button doesn't go back to its unclicked status until after it wakes up.
The only way I know of to get around your particular problem is by running TkInter and/or PyGame on separate threads.
You are going to have to read up on Python's Threading() module. You might start here. I've browsed it and it seems to be pretty complete.
Just to demonstrate the difference:
import tkinter as tk
import time
import threading
def delay():
print("Delay started...")
time.sleep(10)
print("... and finished.")
def dispatchDelayToThread():
t = threading.Thread(target=delay)
t.start()
def main():
root = tk.Tk()
tk.Button(root, text="Test Me", command=dispatchDelayToThread).pack(expand=True, fill=tk.BOTH)
root.mainloop()
if __name__ == "__main__":
main()
I didn't even really change the code any! I added a function to dispatch the code I'd already written then changed the button to call the dispatcher instead of the code. Very easy to implement.
Run this and you'll see that the button returns to ready right away. If you run this from the command line you'll see that it prints a line when you enter the thread, and another line when the thread completes. And an even cooler thing is that if you click the button three times in a row you'll get three "starting" message, followed by three "finished" messages shortly afterwards.
To demonstrate this threading using your own code:
import pygame
import tkinter as tk
import io
import gtts
import threading
def speak(text,language="en",accent="com"):
mp3_fp = io.BytesIO()
phrase = gtts.gTTS(text=text,lang=language,tld=accent)
phrase.write_to_fp(mp3_fp)
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(mp3_fp,"mp3")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.delay(10)
pygame.event.poll()
def dispatchPlay():
t = threading.Thread(target=play)
t.start()
def play():
data = list(["boy", "girl", "pink", "blue"])
for i in data:
speak(i)
def main():
root = tk.Tk()
root.geometry('300x200')
tk.Button(root, text="This is a clicky button", command=dispatchPlay).pack(expand=True, fill=tk.BOTH)
root.mainloop()
if __name__ == "__main__":
main()
Generally you would have your user interface on one thread, frame updates on another thread if it's a game, sound stuff on yet another thread, networking connection on a thread, etc., with all of them tied together through some kind of messaging system.
Note well, however, that in conventional unmodified Python there is only ever one thread running at any one time! You can have a hundred threads spawned, but only one of them will run at a time. Python sucks at multithreading computationally-intensive tasks, but shines in threading out I/O stuff that you usually just spend your time waiting on.

How to get out of the while loop in Pygame when after playing the music?

I have the code down below actually what I want that when one the music get finished it should get out of the while loop automatically. But it isn't getting out of that and if I remove that while loop the song is not getting played.
from pygame import mixer
def mplayer(name):
''' for playing music '''
mixer.init()
mixer.music.load(name)
mixer.music.set_volume(0.7)
mixer.music.play()
mplayer('welcome.mp3')
while True:
continue
Is there any way that once the music is finished than it should get out the loop?
Use mixer.music.get_busy() to test test if any sound is being mixed.
from pygame import mixer
def mplayer(name):
''' for playing music '''
mixer.init()
mixer.music.load(name)
mixer.music.set_volume(0.7)
mixer.music.play()
mplayer('welcome.mp3')
while mixer.music.get_busy():
# you may have to handle the events here
# pygame.event.pump()
# [...]
pass
Note, you may need to handle the events in the loop that is waiting for the music to finish. See pygame.event.get() respectively pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
PyGame has 2 different modules for playing sound and music, the pygame.mixer module and the pygame.mixer.music module. This module contains classes for loading Sound objects and controlling playback. The difference is explained in the documentation:
The difference between the music playback and regular Sound playback is that the music is streamed, and never actually loaded all at once. The mixer system only supports a single music stream at once.
If pygame.mixer.music doesn't work for you try pygame.mixer.music:
from pygame import mixer
def mplayer(name):
''' for playing music '''
mixer.init()
my_sound = mixer.Sound(name)
my_sound.set_volume(0.7)
my_sound.play(0)
mplayer('welcome.mp3')
while mixer.get_busy():
# you may have to handle the events here
# pygame.event.pump()
# [...]
pass
Here is the solution of my own problem:↓
from pygame import mixer
def mplayer(name):
''' for playing music '''
mixer.init()
mixer.music.load(name)
mixer.music.set_volume(0.7)
mixer.music.play()
mplayer('welcome.mp3')
while mixer.music.get_busy(): #just changed this line
pass

How to show the current time and the duration of an audio using Pyglet?

I am new in Python - Pyglet and Stackoverflow. I would like know how to show the current playing time and the total duration of an audio in Pyglet. It is clearly given in Pyglet Docs but I don't excactly understood how to use it properly. So kindly I would like to request for help.
It would be lot easier by showing an example. Thanks!
Here is my code..
from tkinter import*
import pyglet
root = Tk()
player = pyglet.media.Player()
song = "er.mp3"
src = pyglet.media.load(song)
player.queue(src)
def play():
player.play()
def pause():
player.pause()
button_1 = Button(root,text = "Play", command = play)
button_1.pack()
button_2 = Button(root,text = "Pause", command = pause)
button_2.pack()
root.mainloop()
(Sorry For Bad English)
Short answer is:
current_time = player.time
That will give/store the time of the currently playing audio.
What you do with this information is up to you, I assume you want to add it to a label or something.
v = StringVar()
Label(master, textvariable=v).pack()
# Probably in a event driven loop or something.
v.set(player.time)
However the long answer is, don't mix your libraries.
Pyglet is great for 2D/3D rendering since you can hook into the GL libraries pretty well.
What Pyglet does not do great is Audio (even tho it has support for it).
Tkinter on the other hand does nothing of these things but instead gives you buttons and other "widgets".
I would recommend using any other library under how to play music through python with mpg321 for playing audio with tkinter.
Either The Snack Sound Toolkit or winsound if you're on windows.

How can I play audio (playsound) in the background of a Python script?

I am just writing a small Python game for fun and I have a function that does the beginning narrative.
I am trying to get the audio to play in the background, but unfortunately the MP3 file plays first before the function continues.
How do I get it to run in the background?
import playsound
def displayIntro():
playsound.playsound('storm.mp3',True)
print('')
print('')
print_slow('The year is 1845, you have just arrived home...')
Also, is there a way of controlling the volume of the playsound module?
I am using a Mac, and I am not wedded to using playsound. It just seems to be the only module that I can get working.
Just change True to False (I use Python 3.7.1)
import playsound
playsound.playsound('storm.mp3', False)
print ('...')
In Windows:
Use winsound.SND_ASYNC to play them asynchronously:
import winsound
winsound.PlaySound("filename", winsound.SND_ASYNC | winsound.SND_ALIAS )
To stop playing
winsound.PlaySound(None, winsound.SND_ASYNC)
On Mac or other platforms:
You can try this Pygame/SDL
pygame.mixer.init()
pygame.mixer.music.load("file.mp3")
pygame.mixer.music.play()
There is a library in Pygame called mixer and you can add an MP3 file to the folder with the Python script inside and put code like this inside:
from pygame import mixer
mixer.init()
mixer.music.load("mysong.mp3")
mixer.music.play()
Well, you could just use pygame.mixer.music.play(x):
#!/usr/bin/env python3
# Any problems contact me on Instagram, #vulnerabilties
import pygame
pygame.mixer.init()
pygame.mixer.music.load('filename.extention')
pygame.mixer.music.play(999)
# Your code here
A nice way to do it is to use vlc. Very common and supported library.
pip install python-vlc
Then writing your code
import vlc
#Then instantiate a MediaPlayer object
player = vlc.MediaPlayer("/path/to/song.mp3")
#And then use the play method
player.play()
#You can also use the methods pause() and stop if you need.
player.pause()
player.stop
#And for the super fancy thing you can even use a playlist :)
playlist = ['/path/to/song1.mp3', '/path/to/song2.mp3', '/path/to/song3.mp3']
for song in playlist:
player = vlc.MediaPlayer(song)
player.play()
You could try just_playback. It's a wrapper I wrote around miniaudio that plays audio in the background while providing playback control functionality like pausing, resuming, seeking and setting the playback volume.
from pygame import mixer
mixer.music.init()
mixer.music.load("audio.mp3") # Paste The audio file location
mixer.play()
Use vlc
import vlc
player = None
def play(sound_file):
global player
if player is not None:
player.stop()
player = vlc.MediaPlayer("file://" + sound_file)
player.play()
This is a solution to reproduce similar length sounds. This solution worked to me to solve the error:
The specified device is not open or recognized by MCI.
Here follow the syntax as a class method, but you can change it to work even without a class.
Import these packages:
import multiprocessing
import threading
import time
Define this function:
def _reproduce_sound_nb(self, str_, time_s):
def reproduce_and_kill(str_, time_sec=time_s):
p = multiprocessing.Process(target=playsound, args=(str_,))
p.start()
time.sleep(time_sec)
p.terminate()
threading.Thread(target=reproduce_and_kill, args=(str_, time_s), daemon=True).start()
In the main program/method:
self._reproduce_sound_nb(sound_path, 3) # path to the sound, seconds after sound stop -> to manage the process end
I used a separate thread to play the sound without blocking the main thread. It works on Ubuntu as well.
from threading import Thread
from playsound import playsound
def play(path):
"""
Play sound file in a separate thread
(don't block current thread)
"""
def play_thread_function:
playsound(path)
play_thread = Thread(target=play_thread_function)
play_thread.start()

Categories

Resources