How to play an audio when a button is pressed - python

I am having problems with setting up a button that will play audio when pressed.
I am using tkinter for the button and simpleaudio for the audio.
x = ""
def audio(file):
x = file
return x
window = tk.Tk()
window.title("Audio")
audio_button = tk.Button(window,text="Audio Test",command=audio("imposter")).pack(side="left")
audio_button1 = tk.Button(window,text="Audio Test Two",command=audio("crewmate")).pack(side="left")
stop_all = tk.Button(window,text="Stop All",command=audio("stop"),fg="red").pack(side="left")
tk.mainloop()
while True:
if not x == "stop":
if x == "crewmate":
wave_obj = sa.WaveObject.from_wave_file("File here")
play_obj = wave_obj.play()
while play_obj.is_playing():
if x == "stop":
break
else:
pass
elif x == "imposter":
wave_obj = sa.WaveObject.from_wave_file("File here")
play_obj = wave_obj.play()
while play_obj.is_playing():
if x == "stop":
break
else:
pass
else:
pass
And it would be nice to help shorten the code a bit too

pack returns None, therefore you should chain pack to the creation of a widget when you need to keep a reference to the widget.
Here is a possible approach to your problem; I do not have simple audio, so I cannot guarantee the code is fully correct; the code lines with simpleaudio calls are commented out, and print statements allow you to verify the logic. You will have to un-comment them, and try.
The logic stops all audio before launching a new file; you may want to make it a bit more user friendly, like letting the song finish if the command requests the same song, instead of restarting it.
# import simpleaudio as sa
import tkinter as tk
def stop_audio():
print('stopping all')
# sa.stopall()
def test_audio(state):
if state == 'imposter':
path = "path/to/imposter.wav"
elif state == 'crewmate':
path = "path/to/crewmate.wav"
else:
return
# wave_obj = sa.WaveObject.from_wave_file(path)
stop_audio()
# play_obj = wave_obj.play()
print(f'playing {state}')
window = tk.Tk()
window.title("Audio")
audio_button = tk.Button(window,text="Audio Test", command=lambda state='imposter': test_audio(state))
audio_button.pack(side="left")
audio_button1 = tk.Button(window,text="Audio Test Two", command=lambda state='crewmate': test_audio(state))
audio_button1.pack(side="left")
stop_all = tk.Button(window,text="Stop All",fg="red", command=stop_audio)
stop_all.pack(side="left")
tk.mainloop()

Related

How can I get GPIO buttons to both trigger on press and provide input to an if-else loop?

Firstly, I am new to python and gpiozero, so please forgive me. I am trying to modify the GPIO music box tutorial. The project has 7 buttons that I want to both trigger a sound and run a script to check the sequence of button presses. Once the correct sequence is achieved I then want it to play another sound file. Here is the code I have so far:
from gpiozero import Button
import time
song = ""
pygame.mixer.init()
pygame.init()
amaj = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/amaj.mp3")
bmaj = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/bmaj.mp3")
cshrpmin = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/cshrpmin.mp3")
dshrpdim = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/dshrpdim.mp3")
emaj = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/emaj.mp3")
fshrpmin = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/fshrpmin.mp3")
gshrpmin = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/gshrpmin.mp3")
successfulsong = pygame.mixer.Sound("/home/pi/gpio-music-box/samples/4chordsongshort.mp3")
a_button = Button(2)
b_button = Button(3)
c_button = Button(4)
d_button = Button(17)
e_button = Button(27)
f_button = Button(22)
g_button = Button(10)
## Assuming these would call a function to play the sound and check sequence.
a_button.when_pressed = amaj.play
b_button.when_pressed = bmaj.play
c_button.when_pressed = cshrpmin.play
d_button.when_pressed = dshrpdim.play
e_button.when_pressed = emaj.play
f_button.when_pressed = fshrpmin.play
g_button.when_pressed = gshrpmin.play
while True:
while len(song) < 4:
chord = input() # Used as placeholder for button press
chord = str(chord)
if (not song) and chord == "E":
song = (chord)
elif song == "E" and chord == "B":
song = (song+chord)
elif song == "EB" and chord == "C":
song = (song+chord)
elif song == "EBC" and chord == "A":
song = (song+chord)
else:
song = ""
print("You created the 4 chord song!")
successfulsong.play
time.sleep(5)
song = ""
I know that is clunky and there should be better ways to do much of this, but I can't for the life of me figure out how to accomplish the "while len(song)..." of the "if/elif/else" portions using a function and trying to add the button press as the variable. I'm really sorry if this is super vague. Any help is appreciated!

Tkinter button.config() if statements

I have buttons containing images which once clicked will change the image in the button.
If the image is an empty star then the button will update to a full star once clicked and again once clicked this option will revert back to the empty star image in the button.
I tried:
firststar = partial(starsystemfeed, 1)
star_1 = Button(framefeed, image=emptystar, compound="right", command=firststar)
emptystar = PhotoImage(file='icons/emptystar.png')
fullstar = PhotoImage(file='icons/fullstar.png')
def starsystemfeed(num):
global star_1, star_2, star_3, star_4, star_5
global fullstar
global emptystar
if num == 1:
if star_1.config(image = emptystar) is True:
print("1")
star_1.config(image=fullstar)
else:
print("2")
star_1.config(image=emptystar)
but this doesn't seem to work as I keep on getting option "2" on the emptystar image suggesting that I'm doing it wrong
Try this:
firststar = partial(starsystemfeed, 1)
star_1 = Button(framefeed, image=emptystar, compound="right", command=firststar)
emptystar = PhotoImage(file='icons/emptystar.png')
fullstar = PhotoImage(file='icons/fullstar.png')
def starsystemfeed(num):
global star_1, star_2, star_3, star_4, star_5
global fullstar
global emptystar
if num == 1:
if str(star_1.cget("image")) == str(emptystar):
print("1")
star_1.config(image=fullstar)
else:
print("2")
star_1.config(image=emptystar)
You can use <tkinter Widget>.cget("<attribute name>") to get the attribute back from the widget. In this case we want back the image so we use star_1.cget("image") and we check if that is the same as emptystar.

Python VLC - Next track

I am writing a program in Python to run on a Raspberry Pi in order to control my Wurlitzer jukebox. The program current accepts the codes for "record selection" (A1, B1, C4, etc.), add those codes to a playlist, and executes the list. My issue is that once a song starts, I would like to be able to press a button ("Y" in the current code) to skip the currently playing song. I can't get this to work.
If I use "player.next()" I get an error: 'MediaPlayer' object has no attribute 'next'.
I tried to stop the player and restart it (thinking it would pick up the next song in the Playlist. This doesn't even stop the player.
I do not want to use subprocess if I can avoid it. I'd like to figure out a way within Python to do the skipping. How would one accomplish this?
import os, sys, csv, vlc, time, threading
from pynput.keyboard import Key, Listener
DefaultUSBPath="/media/pi"
PlayHistory="PlayHistory.csv"
#
# Declare variables
#
USBDrive = None
Action = None
Playlist = []
SelectionCount = []
Sel_char = None
#
# Find the USB Drive
#
for item in os.listdir(DefaultUSBPath):
if os.path.isdir(os.path.join(DefaultUSBPath, item)):
if USBDrive is None:
USBDrive = os.path.join(DefaultUSBPath, item)
else:
USBDrive = USBDrive + ";" + os.path.join(DefaultUSBPath, item)
if USBDrive is None:
print ("Error(0) - No USB Drive detected")
sys.exit()
elif ";" in USBDrive:
print ("Error(1) - More than one USB Drive detected.")
sys.exit()
#
# Adding to playlist - Returns directory contents and adds to playlist
#
def addplaylist(track):
list = None
if os.path.isdir(os.path.join(USBDrive, track)):
files = [f for f in os.listdir(os.path.join(USBDrive, track)) if os.path.isfile(os.path.join(USBDrive, track, f))]
for f in files:
if list is None:
list = os.path.join(USBDrive, track, f)
else:
list = list + ";" + os.path.join(USBDrive, track, f)
else:
print ("Error(2) - Selection is invalid")
if list is not None:
if ";" in list:
list = list.split(";")
else:
print ("Error(3) - Selection has no media")
return list
#
# MediaPlayer function
#
def MusicPlayer(P):
global Playlist, player
while len(Playlist) > 0:
song=Playlist.pop(0)
print("Song: ")
print(song)
player=vlc.MediaPlayer(song)
player.play()
#
# Define keyboard actions
#
def on_press(key):
global Action, Playlist, player
try:
Sel_char = int(key.char)
except:
try:
Sel_char = str(key.char)
Sel_char = Sel_char.upper()
except:
Sel_char = None
if Sel_char == "Z":
return False
elif Sel_char == "Y":
print("Skip")
#player.next() This line causes 'MediaPlayer' object has no attribute 'next'
time.sleep(1)
MusicPlayer(Playlist)
elif type(Sel_char) == str:
Action = Sel_char
elif type(Sel_char) == int:
Action = Action + str(Sel_char)
print("Action: " + Action)
Plist = addplaylist(Action)
if Plist is not None:
print("Added to playlist")
Playlist.append(Plist)
print(Plist)
MusicPlayer(Playlist)
else:
pass
#
# Read keyboard input
#
with Listener(on_press=on_press) as listener:
listener.join()
print ("")
print ("Have a nice day!")
print ("")
sys.exit()
The way you have it coded, I expect you'd have to stop it, remove the current media from the list, then re-start it.
However you may be better off running with a media_list_player, see below for a bare bones version. Note, I'm on Linux and had to hack some code to get the key input, rather than using a specific library or spend time on it, but it should give you enough to work with.
Edit
I apologise, there is a much simpler method when using a media_list_player, although if you want finer control you should use the media_list_player where you control the list's index or for full control use a media_player_new() but that's beyond the scope of this question. (I'll leave the original answer below this code, as it may be useful)
import vlc
import time
## pinched from vlc for keyboard input
import termios
import tty
import sys
mymedia = ["vp.mp3","vp1.mp3","happy.mp3","V1.mp4"]
def getch(): # getchar(), getc(stdin) #PYCHOK flake
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return ch
## end pinched code
def jukebox():
player.play_item_at_index(0)
while True:
time.sleep(0.25)
k = getch()
if k == "n": #Next
player.next()
if k == "p": #Previous
player.previous()
if k == " ": #Pause
player.pause()
if k == "q": #Quit
player.stop()
return True
player.next()
vlc_instance = vlc.Instance('--no-xlib --quiet ') # no-xlib for linux and quiet don't complain
player = vlc_instance.media_list_player_new()
Media = vlc_instance.media_list_new(mymedia)
player.set_media_list(Media)
print("Welcome to Jukebox")
print("Options - space = Play/Pause, n = Next, p = Previous, q = Quit")
print("Media",mymedia)
jukebox()
Original code
import vlc
import time
## pinched from vlc for keyboard input
import termios
import tty
import sys
def getch(): # getchar(), getc(stdin) #PYCHOK flake
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return ch
## end pinched code
vlc_instance = vlc.Instance()
player = vlc_instance.media_list_player_new()
mymedia = ["vp.mp3","vp1.mp3","happy.mp3"]
Media = vlc_instance.media_list_new(mymedia)
player.set_media_list(Media)
for index, name in enumerate(mymedia):
print("Playing:",name)
player.play_item_at_index(index)
time.sleep(1)
while player.get_state() != 6:
time.sleep(1)
k = getch()
if k == "y":
player.stop()
break

Pause a for loop to wait for a button press

I am trying to build an image classifier and I need to manually assign classifications for a training dataset, so I build a file browser /media window app in tkinter to display images and assign them classifications via a button click. To iterate over the files, I am using a for loop, but I need it to pause and wait for that input. Here is my code:
def setKnownData(self):
training_sample = self.dataset.sample(frac = .1)
print(training_sample)
for i, row in training_sample.iterrows():
print(row)
global img
file = Image.open(row['ID'])
resized = file.resize((500,600))
img = ImageTk.PhotoImage(resized)
self.media_window.create_image(0,0, anchor = 'nw', image = img)
self.event_var.get()
while True:
if self.event_var.get() == 0:
print(self.event_var.get())
return
if self.event_var.get() == 1:
training_sample.loc[row]['class'] = 'cartoon'
break
elif self.event_var.get() ==2:
training_sample.loc[row]['class'] = 'photo'
break
self.event_var.set(0)
def stateSwitch(self, action):
print('state switching....')
if action == 'toon':
print(self.event_var.get())
self.event_var.set(1)
print('classification: TOON', self.event_var.get())
elif action == 'photo':
self.event_var.set(2)
print('classification: PHOTO')
I've exausted every combination of IntVar, tkinter, and for loop searches and can't find a viable solution, so I apologize if this is a repeat question. How can I pause this for loop, wait for a putton press, and then proceed to the next image in the list?
You need to shift your thinking away from pausing the loop. That's procedural programming, but GUIs are work much better as "event driven programming", where the entire program is just endlessly waiting for an event (like a button press) to happen. The means no loops, besides the tkinter mainloop. And it means making a new function for every event.
def setKnownData(self):
training_sample = self.dataset.sample(frac = .1)
print(training_sample)
self.training_sample = training_sample.iterrows()
def on_button_click(self):
i, row = next(self.training_sample)
print(row)
global img
file = Image.open(row['ID'])
resized = file.resize((500,600))
img = ImageTk.PhotoImage(resized)
self.media_window.create_image(0,0, anchor = 'nw', image = img)
self.event_var.get()
while True:
if self.event_var.get() == 0:
print(self.event_var.get())
return
if self.event_var.get() == 1:
training_sample.loc[row]['class'] = 'cartoon'
break
elif self.event_var.get() ==2:
training_sample.loc[row]['class'] = 'photo'
break
self.event_var.set(0)

raw_input and timeout [duplicate]

This question already has answers here:
Keyboard input with timeout?
(28 answers)
Closed 8 years ago.
I want to do a raw_input('Enter something: .'). I want it to sleep for 3 seconds and if there's no input, then cancel the prompt and run the rest of the code. Then the code loops and implements the raw_input again. I also want it to break if the user inputs something like 'q'.
There's an easy solution that doesn't use threads (at least not explicitly): use select to know when there's something to be read from stdin:
import sys
from select import select
timeout = 10
print "Enter something:",
rlist, _, _ = select([sys.stdin], [], [], timeout)
if rlist:
s = sys.stdin.readline()
print s
else:
print "No input. Moving on..."
Edit[0]: apparently this won't work on Windows, since the underlying implementation of select() requires a socket, and sys.stdin isn't. Thanks for the heads-up, #Fookatchu.
If you're working on Windows you can try the following:
import sys, time, msvcrt
def readInput( caption, default, timeout = 5):
start_time = time.time()
sys.stdout.write('%s(%s):'%(caption, default));
input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input += chr
if len(input) == 0 and (time.time() - start_time) > timeout:
break
print '' # needed to move to next line
if len(input) > 0:
return input
else:
return default
# and some examples of usage
ans = readInput('Please type a name', 'john')
print 'The name is %s' % ans
ans = readInput('Please enter a number', 10 )
print 'The number is %s' % ans
I have some code which makes a countdown app with a tkinter entry box and button so they can enter something and hit the button, if the timer runs out the tkinter window closes and tells them they ran out of time.
I think most other solutions to this problem don't have a window which pops up so thought id add to the list :)
with raw_input() or input(), it isn't possible as it stops at the input section, until it receives input, then it carries on...
I have taken some code from the following link:
Making a countdown timer with Python and Tkinter?
I used Brian Oakley's answer to this problem and added the entrybox etc.
import tkinter as tk
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
def well():
whatis = entrybox.get()
if whatis == "": # Here you can check for what the input should be, e.g. letters only etc.
print ("You didn't enter anything...")
else:
print ("AWESOME WORK DUDE")
app.destroy()
global label2
label2 = tk.Button(text = "quick, enter something and click here (the countdown timer is below)", command = well)
label2.pack()
entrybox = tk.Entry()
entrybox.pack()
self.label = tk.Label(self, text="", width=10)
self.label.pack()
self.remaining = 0
self.countdown(10)
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
app.destroy()
print ("OUT OF TIME")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()
I know what I added was a bit lazy but it works and it is an example only
This code works for Windows with Pyscripter 3.3
For rbp's answer:
To account for input equal to a Carriage Return simply add a nested condition:
if rlist:
s = sys.stdin.readline()
print s
if s == '':
s = pycreatordefaultvalue

Categories