I would love my script to play a second sound when the first one is ended. From tkSnack documentation:
play ( )
Plays the sound. All options are ignored if play() is used to resume a paused play options.
If a play() command is issued while another one is in progress,
the latter one is queued up and starts to play as soon as possible.
So, I thought that calling the play method on the sound object while a song was being played would result in the second song to be started when this one's finished.
Unfortunately, this doesn't seem to happen. In fact, as I call the read method on the sound object, in order to load the audio file, the player stops to play.
How should I load the audio?
I've also tried to use two different sound object, but this results in an overlapping playing.
So far, the only solution I've found is to use the blocking option for the play method. And add it in a loop:
for song in songList:
SoundObject.read(song)
SoundObject.play(blocking=True)
This is however far from a good solution, since it blocks the whole application, even if I start it in a daemon thread.
Here is a sample code:
import Tkinter, tkSnack, time
win = Tkinter.Tk()
tkSnack.initializeSnack(win)
snd = tkSnack.Sound()
def play1():
snd.read('/home/francesco/Musica/Andrea Bocelli - Ave Maria.mp3')
snd.play()
def play2():
snd.read('/home/francesco/Musica/Andrea Bocelli - La voce del silenzio.mp3')
snd.play()
b1 = Tkinter.Button(win, text="play1", command = play1)
b2 = Tkinter.Button(win, text="play2", command = play2)
b1.pack()
b2.pack()
win.mainloop()
I would love that clicking play2 it will "append" the soundtrack to the current one and start it as soon as the first one is ended.
Related
I am trying to set up a simple media player with Qt.
I want to choose the start position of the video on startup of the player. I bound this to an isSeekable event to make sure media is loaded and seekable.
Still the player does not work as expected. Either I am gettings errors (0xC00D6D60) or the video just starts from the beginning.
This is my code:
def seekable_changed(self, is_seekable):
if (is_seekable):
self._player.pause()
self._player.setPosition(videostartmsdiff) # milliseconds
self._player.play()
I tried different combinations with or without pause() and play()
How do I set start position in Qt to let it work as expected?
Thanks
You should connect to QMediaPlayer::stateChanged(QMediaPlayer::State state) signal and call setPosition when state becomes QMediaPlayer::PlayingState.
So, I'm not sure the title describes my issue in a comprehensive manner.
I have made a working "loop" code in Python (even with working recursive functions) for some sort of a card game "AI". See the following pseudo-code:
def move():
if Turn == "Computer":
if Computer cannot play:
return False
else:
Do some "AI" stuff... the computer plays
Turn = "Player"
move()
elif Turn == "Player":
if Player cannot play:
return False:
else:
in = input("Select Card: ")
****So here the loop stops and waits for the input from the player
...then the input is evaluated and "played" as a player move
Turn = "Computer"
move()
while continue:
continue = move()
So if the computer can play, it plays the card, the turn changes to Player and the move function is called again, otherwise false is returned and the PC-Player back and forth turn is over. In the Player's turn the loop stops to wait for the user input from terminal and then it continues...
So far everything works, but now I want to implement a GUI for player input. I have some basic knowledge with Tkinter and so far I created buttons on screen rapresenting the player's cards. The question is how can I "stop" the loop waiting for the user to click a button (corresponding to the card - or the PASS TURN). Maybe with some sort of threading but sounds complicated... there must be an easier solution like in the "Terminal input version" that I have already working.
Thanks
You can't use a loop in a GUI, because the GUI has it's own loop that needs to run. The mainloop() in tkinter. You need to shift your planning to "event driven" programming. It sounds like in your case the event would be the user choosing a card.
def player_chooses_card():
# graphic and logic updates for player's choice
# computer turn
if Computer cannot play:
trigger graphic for end of game
else:
Do some "AI" stuff... the computer plays
# function is done; back to idle state waiting for an event
FWIW making a loop with recursion is bad style in python.
I feel your pain on the Tkinter loops. It was a hard lesson for me to learn as well. Loops such as while and for DO NOT WORK in tkinter. The way I typically solve this problem is with "root.after." Take the code in your loop and define it as a command (adding root.after(1, name of your command) like so:
def myloop():
move()
root.after(1, myloop)
Then, call it for the first time right before 'root.mainloop', like so:
<your other code>
root.after(1, myloop)
root.mainloop()
Also keep in mind that Tkinter does not make nice nice with multiprocessing/threading within mainloops, so you have to set up each process/thread as its own canvas and root.
Finally, you could set up a series of if statements and variables to separate your loop into chunks so that it stops and starts based on player input. For example,
def myloop():
if playerhasdonething == True:
move()
else:
print("waiting on player")
root.after(1, my mainloop)
I apologize if I missed the point of your question, this is how I personally would try to solve your problem
I am making a mp3 player using python 3 and Tkinter, using pygame.mixer for the MP3s. When you input a directory and press play it auto finds all the MP3s in the directory (using glob) and adds them to a list. I use a for loop to load then play the music, but when it plays it starts the next song immediately and automatically repeats till the end of the list where it plays only that song. I tried using time.sleep() which works but freezes the program. How can I wait till the song is over to start the next one and not freeze the program.
My current code (just the function to play the music):
def play():
print("playing")
global playing
playing = True
direct = directorye.get()
direct = direct.lower()
if direct.endswith(".mp3"):
mixer.music.load(directorye.get())
mixer.music.play()
else:
dire = r"C:\Users\agweb\Downloads\Music" #for the time being I just hard coded the directory
mp3s = glob.glob(dire + r"\*.MP3") # adds all MP3s in directory to list
#mp3s =+ glob.glob(directorye.get() + "*.MP3")
for i in mp3s:
print(i)
mixer.music.load(i)
mixer.music.play()
audio = MP3(i)
time.sleep(audio.info.length) #works but freezes program
There are several approaches possible.
Since tkinter is thread-safeish(*) on Python 3, probably the easiest way is to start a slightly modified version of your play function as a second thread.
In the global context, create a threading.Event. I would suggest the name stop_playing.
In the play function, only sleep for a little interval at a time, checking if stop_playing is set:
for i in mp3s:
mixer.music.load(i)
mixer.music.play()
audio = MP3(i)
playtime = 0
interval = 100 # ms
while playtime < audio.info.length:
time.sleep(interval)
playtime += interval
if stop_playing.is_set():
# TODO: stop the current playback.
# Then exit the thread.
return
The new callback for the "Play" button should create a thread for play() and start() it.
The callback for the "Stop" button should simply call stop_playing.set(). That will cause the playing thread to stop.
(* Provided it uses a tcl compiled with threading enabled.)
This solution work you can test it
from threading import Thread
import pygame
from itertools import cycle
def put_next_music_inside(loop_on_musics, stop_event):
for cur_music in loop_on_musics:
while pygame.mixer.music.peek(stop_event):
pass
pygame.event.get(stop_event)
pygame.mixer.music.load(cur_music)
pygame.mixer.music.play()
pygame.mixer.init()
pygame.display.init()
END_OF_MUSIC_EVENT = -100
pygame.mixer.music.set_endevent(END_OF_MUSIC_EVENT)
list_of_music = []#fill it with musics'path
first_music = list_of_music[0]
del list_of_music[0]
list_of_music.append(first_music)
loop_on_musics = cycle(list_of_music)
t = Thread(target=put_next_music_inside, args=(loop_on_musics, END_OF_MUSIC_EVENT))
t.start()
pygame.mixer.music.load(first_music)
pygame.mixer.music.play()
Im writing a litle game on python using Tkinter (And by the way, i am not allowed to use any other non built in modules) and I want to play a background song when on the main window, wich is the one that holds the title, and the buttons to go to other windows and stuff...
So the thing is that I need that sound to stop when I enter to another window, but when I hit the button to go, to another window, the song keeps playing...
I'm using the winsound module, and had define a couple of functions, (That by the way, are wicked as hell) to play the song when I first run the program using threads...
So here is the deal, I want something to put in the function "killsound" so that I can add it to every button, and then when I press any button to open any other window, the sound will be killed.
I was hoping something like 'a.kill()' or 'a.stop()' but it didnt worked.
And I really donĀ“t know how to use the SND_PURGE thing on winsound... Although I understand that SND_PURGE is no longer working on new windows OS (Got Win8.1)
Could you please help me?
Thank You! (And Sorry for the creepy english...)
def Play(nombre): #This function Is the core of the winsound function
ruta = os.path.join('Audio',nombre)
Reproducir= winsound.PlaySound(ruta,winsound.SND_FILENAME)
return Reproducir
def PlayThis():
while flag_play:
try:
return Play('prettiest weed.wav')
except:
return "Error"
def PlayThisThread():
global flag_play
flag_play= True
a=Thread(target=PlayThis, args=())
a.daemon = True
a.start()
PlayThisThread()
def killsound(): #This is the function I want, for killing sound.
global flag_play
flag_play = False
There are 2 major problems in your code:
global variable flag_play has to be placed inside the sound playback loop, in your case within the PlayThis() function
the Winsound module is aiming simple non-threaded usage. while the sound is playbacked, there is no chance to "softly" interrupt. It does not support any playback status reporting e.g. .isPlaying() nor any kind of .stop() that you need in order to kill it.
Solution:
try PyMedia package. Pymedia allows lower-level audio manipulation therefore more details have to be provided at the initialisation:
import time, wave, pymedia.audio.sound as sound
# little to do on the proper audio setup
f= wave.open( 'prettiest weed.wav', 'rb' )
sampleRate= f.getframerate() # reads framerate from the file
channels= f.getnchannels()
format= sound.AFMT_S16_LE # this sets the audio format to most common WAV with 16-bit codec PCM Linear Little Endian, use either pymedia or any external utils such as FFMPEG to check / corvert audio into a proper format.
audioBuffer = 300000 # you are able to control how much audio data is read
with the following assigment the "snd" becomes an instance of the class sound.Output and gives you bunch of useful audio methods:
snd= sound.Output( sampleRate, channels, format )
s= f.readframes( audioBuffer )
snd.play( s )
and finally your threaded playback loop might look as follows:
while snd.isPlaying():
global flag_play
if not flag_play: snd.stop() #here is where the playback gets interupted.
time.sleep( 0.05 )
f.close()
Please, let me know if you need more support on this.
I also created a 0.5 sec wavfile in Adobe Audition containing silence and attached it to the stop button, and this basically "stopped" playback of the previously played audio clip.
I found a way to do it, by adding a 0.5sec sound to a button, so that when I press the button it stops the background one to play the button one, and then stops all sounds on program.
I am new to programming with Python. I have been working through a tutorial book that I found, and got the game up and running, but then decided I wanted to have a "Play Again?" option at the end. I can get the game to quit out with a press of the "n" key, but cannot work out how to get the game to restart.
Here is the code I think is giving the trouble:
#player reaches treasure
if player_rectangle.colliderect(treasure_rectangle):
#display text
screen.blit(text,(screen_width/2-195,screen_height/2-25))
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_n:
exit()
elif event.key==pygame.K_y:
pygame.display.update()
I know something needs to go after the elif event, and I have tried all that I can think of. I tried to define the whole program, and call it but that stopped the whole thing running. I have looked around internet sites, but just cannot seem to come up with a answer.
Can some one help out in easy terms how to get the game to restart to the starting position when the y key is pressed? I know it has something to do with a loop, I just cannot place my finger on what.
Many thanks.
It's not entirely clear how your code is organized, so I'll be very general. Usually games are implemented with a "main loop" that handles all of the action. In "pseudo"-python:
def main_loop():
while True:
handle_next_action()
draw_screen()
if game_is_over():
break
Before you start the loop, you usually do some setup to get the game state how you want it:
def main():
setup()
main_loop()
shut_down()
Given those parts, you can reset the game by having the main loop code call setup again (it may need to be specifically designed to be runable more than once):
def main_loop():
while True:
handle_events()
draw_screen()
if game_is_over():
if play_again(): # new code here!
setup()
else:
break
You might want to split the setup code into two parts, one which only needs to be run when the program begins (to read configuration files and set up things like the window system), and a second that gets repeated for each new game (setting up the game's state).
To restart a game you normally need to reset all variables to their initial value (e.g. number of lives, score, initial position, ...).
The best way is to put all initialisations inside a procedure:
def init():
global score,lives # use these global variables
score=0
lives=3
init() # call init() to define and reset all values
while 1: # main loop
...
elif event.key==pygame.K_y:
init() # restart the game