how to play audio file in python? - python

I am just starting using python with a GUI interface. I've been experimenting with TKinter on a simple timer program. I am stuck, because I want to have a song play with the alert,but have not been able to find a solution. I am working on Linux mint. I have a message window that appears when the time is up, and i would like to start the audio along with the window, and when you exit the window, the audio stops. my code looks like this.
from Tkinter import *
import tkMessageBox
def messageWindow():
win = Toplevel()
b = Button(win, text='Times Up!',
bg="yellow", fg="green",
activebackground="purple", activeforeground="white",
command=quit)
b.pack(ipadx=root.winfo_screenwidth()/2,
ipady=root.winfo_screenheight()/2)
root.mainloop()
def alert():
#this is were i would a call the function to play mp3
messageWindow()
quit()
def start():
root.after(scale.get() * 1000, alert)
root = Tk()
minutes = Label(root, text ="Minutes: ")
minutes.grid(row=0, column=0)
scale = Scale(root, from_=1, to=60, orient=HORIZONTAL, length=450)
scale.grid(row=0, column=1)
button = Button(root,text= "Start Timing", command=start)
button.grid(row=1, column=1, pady=5, sticky=E)
root.mainloop()

pygame includes the functionality to do this. I don't know if it is the best way but it is certainly a way.
import pygame
pygame.init()
pygame.mixer.init()
sounda= pygame.mixer.Sound("desert_rustle.wav")
sounda.play()
sounda.stop()
example taken from here

Seems like you're already happy with an answer (which you accepted 3 years ago, admittedly!) but here's an alternative you could consider for future projects:
use import winsound. Pygame didn't work for me (possibly down to the fact that I'm using python 3), but more importantly, winsound probably makes audio simpler to play in the first place and it would be effective for your purposes as far as I know. You need to use a '.wav' file (as opposed to something like mp3), but it's easy to convert to that format if you look up 'online converter'.
import winsound
winsound.PlaySound('The Countdown.wav', winsound.SND_ASYNC)
Just in case you do need to stop the audio early, you can't use stop(). Instead, use
winsound.PlaySound(None, 0)
Perhaps pyglet does this anyway, but what's great about winsound.SYND_ASYNC is that it will run in conjunction with tkinter instead of halting the program/ waiting until the program finishes, to execute.

Related

Tkinter unbinding key event issue

In the code below, pressing the space bar twice results in two successive beeps. I want to avoid this and instead disable the key while the first beep is happening. I thought unbinding the space key might work, but it doesn't. It's strange that only two beeps seem to stack up rather than more. I'm guessing maybe the cause of the issue is that winsound.Beep is non-blocking so the rebinding occurs almost instantly.
Any suggestion on how to get this to work please?
import winsound
from tkinter import *
def beep(e):
frame.unbind("<space>")
winsound.Beep(440, 1000)
frame.bind("<space>", beep)
root = Tk()
frame = Frame(root, width=100, height=100)
frame.bind("<space>", beep)
frame.pack()
frame.focus_set()
root.mainloop()
Here is a solution that takes the focus away from the widget, so the binding wont get triggered:
import winsound
from tkinter import *
def beep(event):
dummy.focus_set() #setting focus to dummy
winsound.Beep(440, 1000) #playing it
root.after(1000,frame.focus_set) #setting focus back after playing for 1000 ms
root = Tk()
dummy = Label() #making a dummy widget
dummy.pack()
frame = Frame(root, width=100, height=100)
frame.bind("<space>",beep)
frame.pack()
frame.focus_set()
root.mainloop()
I've commented it to understand better, but this is just a way around and its not that complicated to understand either.
Also keep in mind, in all cases of using winsound, as long as that beep has started and finished playing, the GUI will be unresponsive, that is, GUI will be unresponsive for 1 sec(in your case).
This should fix it however you have to download keyboard module with pip install keyboard :
import winsound
from tkinter import *
import keyboard
from _thread import start_new_thread
def beep():
while True:
if keyboard.is_pressed('space'):
winsound.Beep(440, 1000)
root = Tk()
frame = Frame(root, width=100, height=100)
start_new_thread(beep, ())
frame.pack()
frame.focus_set()
root.mainloop()
First start_new_thread()(syntax is important) makes beep() threaded (runs in background?) and its a while loop so it runs continuously and whenever you press space it will beep and even if you spam space it will still just run one beep. However there is a downside. It will run while script is not terminated so if you focus out it will still beep if you press spacebar
You can use the elapsed time since the last successful keypress to decide if the beep should be produced or not.
maybe like this: I do not have access to winsound, so I am using an os feature to mimic a beep. You can comment this out, and uncomment the calls to winsound
# import winsound
import os
import tkinter as tk
import time
def beep(e, time_limit=1, timer=[0]):
t0 = timer[0]
t1 = time.time()
delta_t = t1 - t0
if delta_t < time_limit:
return
# winsound.Beep(440, 1000)
os.system('say "Beep"')
timer[0] = t1
root = tk.Tk()
frame = tk.Frame(root, width=100, height=100)
frame.bind("<space>", beep)
frame.pack()
frame.focus_set()
root.mainloop()
You can bind back the event via after_idle():
def beep(e):
e.widget.unbind('<space>')
winsound.Beep(440, 1000)
e.widget.after_idle(e.widget.bind, '<space>', beep)
explanation:
The callback passed to after_idle() will be executed when tkinter mainloop is idle, i.e. no pending works/events to be handled. So if the spacebar is pressed many times, the first press triggers beep() in which tkinter unbinds the event, beep and then schedules the rebind. After beep() returns, tkinter keeps handling the pending tasks, i.e. handle the rest spacebar events (but at that moment, no bind is active) and then do the after_idle schedule task which is the rebind.

How do I stop sounds stacking on top of each other with winsound? (Python and tkinter)

I'm trying to create a simple soundboard in python using tkinter. My aim is to just have one button, in this instance titled "bruh", where every time the button is clicked, it plays the "bruh.wav" sound.
So far it seems to work, however if I was to press the button repeatedly, the sounds would stack on top of each other as if it's a queue. How do I make it so every button press cancels any sound playing and just plays the beginning of the wav file?
I've read into the winsound module, the "PURGE" commands seems of interest but I am unsure as to how to implement it, I'm only a beginner, sorry!
from tkinter import *
root = Tk()
def leftClick(event):
import winsound
winsound.PlaySound("realbruh.wav", winsound.SND_FILENAME)
frame = Frame(root, width=600, height=600)
bruhButton = Button(root, text="bruh")
bruhButton.bind("<Button-1>", leftClick)
bruhButton.pack()
root.mainloop()
ie: If I was to spam the button, the "bruh" sound would play one after the other until it reaches the amount of times I clicked the button. How do I make it so they interrupt each other, and there is no queue thing?
If the sound is all you need and can use pygame module then try my method.
If you don't have pygame module then install it with pip install pygame. I use pygame module for all the sound effects in my tkinter projects and it works fine.
Here is how I did it:
from tkinter import *
import pygame
pygame.mixer.init() # initialise `init()` for mixer of pygame.
sound = pygame.mixer.Sound("bruh.wav") # Load the sound.
root = Tk()
def leftClick(event):
sound.stop() # Stop the ongoing sound effect.
sound.play() # Play it again from start.
frame = Frame(root, width=600, height=600)
bruhButton = Button(root, text="bruh")
bruhButton.bind("<Button-1>", leftClick)
bruhButton.pack()
root.mainloop()

How to break out of an infinite loop with a Tkinter button?

I am working in a program where I use Tkinter for the UI. I am writing a code to play an audio repeatedly. I am using pygame.mixer.music() for playing audio.
In the UI I created two buttons ("Start" and "Stop"). I attached a method which contains the loop structure to the start button, so that when the Start button is pressed the loop will be executed and starts playing audio repeatedly. Now I don't know how to attach the Stop button. Like, when Stop button is pressed the control should exit the loop. Can I use interrupts or some other thing like that? Iam totaly new to the concept of interrupts. To proceed with that, help me with what kind of interrupt, what is the library for that, etc. If not please help me how to proceed with the stop button.
Here is my code:
from pygame import *
from Tkinter import *
import time
root = Tk()
root.geometry("1000x200")
root.title("sampler")
m=1
n=1
mixer.init()
def play():
while m==1:
print 'playing'
mixer.music.load('audio 1.mp3')
mixer.music.play()
time.sleep(n)
start = Button(root, text="play", command = play)
start.pack()
stop = Button(root, text="Stop")
stop.pack()
mainloop()
n defines how long the audio should be played for each loop.
Python doesn't exactly support interrupts, the closest thing would probably be some sort of signal handler, which are supported via its signal library. However they may not work well with Tkinter (or pygame), so I don't think that would be a good approach—and they're not really necessary anyway because what you want to do can be handled within Tkinter's mainloop().
Although it may seem somewhat complex, the way I would suggest implementing it would be to encapsulate most of the playing control functionality within a single Python class. This will reduce the use of global variables, which will make the program easier to debug and develop further (because of the many advantages of Object-Oriented Programming — aka as OOP).
Below illustrates what I mean. Note, I'm using Python 3, so had to make a few additional changes to your code in order for it would work with that version. I'm not sure, but this version ought to work in Python 2, as well, except you'll need to change the import of the Tkinter module as indicated.
from pygame import *
from tkinter import * # Change to "from Tkinter import *" for Python 2.x.
class PlayController(object):
def __init__(self, mixer, music_filename, polling_delay):
self.mixer = mixer
self.music_filename = music_filename
self.polling_delay = polling_delay # In milliseconds.
self.playing = False
def play(self):
if self.playing:
self.stop()
self.mixer.music.load(self.music_filename)
self.mixer.music.play(-1) # -1 means to loop indefinitely.
self.playing = True
root.after(self.polling_delay, self.check_status) # Start playing check.
def stop(self):
if self.playing:
self.mixer.music.stop()
self.playing = False
def check_status(self):
if self.playing:
print('playing')
root.after(self.polling_delay, self.check_status) # Repeat after delay.
root = Tk()
root.geometry("1000x200")
root.title("Sampler")
mixer.init()
play_control = PlayController(mixer, 'tone.wav', 1000)
Button(root, text="Play", command=play_control.play).pack()
Button(root, text="Stop", command=play_control.stop).pack()
mainloop()
You need to add a command to your button... stop = Button(root, text="Stop", command=stop)
Just adding a stop command probably wont work because the way your infinite loop is structured, you can't interact with the tkinter interface while play is clicked. Try restructuring your program like this:
from Tkinter import *
from pygame import *
import time
import threading
switch = True
root = Tk()
n = 1
mixer.init()
root.geometry("1000x200")
root.title("sampler")
def play():
def run():
while switch:
print 'playing'
mixer.music.load('audio 1.mp3')
mixer.music.play()
time.sleep(n)
if not switch:
break
thread = threading.Thread(target=run)
thread.start()
def switch_on():
global switch
switch = True
play()
def switch_off():
global switch
switch = False
def kill():
root.destroy()
onbutton = Button(root, text="Play", command=switch_on)
onbutton.pack()
offbutton = Button(root, text="Stop", command=switch_off)
offbutton.pack()
killbutton = Button(root, text="Kill", command=kill)
killbutton.pack()
root.mainloop()
This way the tkinter buttons are running on separate threads, so while one is looping you can still interact with the other.

tkinter key-binding won't work

I am trying to make a program which will react to key-presses and play certain mp3 files. Here's a piece of the code:
from pygame import mixer
from Tkinter import *
root = Tk()
def playBDT():
mixer.init()
mixer.music.load("Ba Dum Tss!.mp3")
mixer.music.play()
button2 = Button(root, command = playBDT)
button2.bind("<KeyPress-X>", playBDT)
button2.grid(row=0,column=0)
root.mainloop()
Now when i run this i get a frame with a button. When i click the button the sound plays normally. However, when i press the X key nothing happens. How can i fix it? Also if i were to play a game with this program running in the background, will the sounds play when i press the corresponding keys?
I had the same problem, but i don't know if this works for you.
button2 = Button(root, command = playBDT)
button2.focus_force()
button2.bind("<KeyPress-X>", playBDT)
button2.grid(row=0,column=0)

Periodically update label in Tkinter

I read through many of solutions of this problem already, but I still can't make this simple program work. I know it's probably really simple, but I can't find out what am I missing.
I have this simple program:
from Tkinter import *
import subprocess
def run():
process=subprocess.Popen(some_script, shell=True, stdout=subprocess.PIPE)
while True:
nextline = process.stdout.readline()
if not nextline:
break
output.set(nextline)
root.update_idletasks()
root = Tk()
output = StringVar()
label1 = Label(root, textvariable=output)
label1.pack()
button1 = Button(root, text="Go", command=run)
button1.pack()
root.mainloop()
So when I click the button, some_script is executed. I want the label to periodically update with the output of the script, but it doesn't. What am I doing wrong?
I'm guessing the GUI becomes unresponsive when you run this. If so, then you're blocking Tkinter's main loop. You'll need to run the subprocess bit in a Thread and use whatever Tkinter's thread-safe methods are to update your GUI. I found this old article that sounds very similar to what you're doing: http://effbot.org/zone/tkinter-threads.htm
There's also this handy recipe: http://code.activestate.com/recipes/82965-threads-tkinter-and-asynchronous-io/

Categories