I'm writing an audio player in python using pyglet's Player class. This module is just a test of the Player and Source classes and it produces nothing. No sound, no error, just one warning about vsync that probably has nothing to do with the actual problem.
import pyglet.media as media
def main():
fname='D:\\Music\\JRR Tolkien\\LotR Part I The Fellowship of the Ring\\01-- 0001 Credits.mp3'
src=media.load(fname)
player=media.Player()
player.queue(src)
player.volume=1.0
player.play()
if __name__=="__main__":
main()
Also, src.play() does nothing too. What am I doing wrong?
EDIT: I've also confirmed that the media.driver is the media.drivers.directsound module. I was afraid it was using silent.
You have to somehow start the pyglet loop. Besides drawing the screen, calls the event handlers and plays the sounds.
import pyglet
import pyglet.media as media
def main():
fname='D:\\test.mp3'
src=media.load(fname)
player=media.Player()
player.queue(src)
player.volume=1.0
player.play()
try:
pyglet.app.run()
except KeyboardInterrupt:
player.next()
if __name__=="__main__":
main()
Something like that works, but if you use Pyglet also sure you'll want to draw something.
Related
I am trying to play an audio file in a loop (this example repeats it 3 times). The audio file is very short. It is just someone saying "Hello, world".
import pygame
def wait_for_player():
while pygame.mixer.music.get_busy():
continue
def play_file(filename):
pygame.init()
pygame.mixer.music.load(filename)
for i in range(3):
pygame.mixer.music.play()
wait_for_player()
if __name__ == '__main__':
play_file('hello_world.mp3')
What I hear sounds like this:
"Hello, world"
"o, world"
"o, world"
That is, the audio is clipped briefly at the beginning on subsequent replays. On a different device the same code and the same audio file sounds like this:
"o, world"
"Hello, world"
"Hello, world"
Each of these devices used bluetooth - one on a Raspberry Pi, the other on a Windows laptop. I tried again on a couple other devices without bluetooth and each time the audio played correctly. So maybe it's a bluetooth problem.
I tried padding a second or two of silence before the "hello", but it made no difference (except for spacing out the timing between each play - the audio was still clipped as it was without the padded silence).
My next step is to pad with a (hopefully) imperceptible bit of audio to "prime the pump", but I would rather have code that works than a workaround. Is there anything I can do programatically to ensure the entire sound plays each and every time?
ETA: Here is my workaround. I copied this gist into tone.py. It wraps around mixer.Sound to make it easy to play a note. The modified code below plays a low frequency note at low volume. This is enough to "prime the pump" and it now works on all devices. It's still a workaround, so hopefully someone will have a better answer - but I can go live with this if I have to.
from time import sleep
import pygame
from tone import Note
def wait_for_player():
while pygame.mixer.music.get_busy():
continue
def play_file(filename):
pygame.init()
Note(0.08, volume=0.01).play(-1)
sleep(1)
pygame.mixer.music.load(filename)
pygame.mixer.music.play(loops=3)
wait_for_player()
if __name__ == '__main__':
play_file('hello_world.mp3')
Here is my workaround. I copied this gist into tone.py. It wraps around mixer.Sound to make it easy to play a note. The modified code below plays a low frequency note at low volume. This is enough to "prime the pump" and it now works on all devices. It's still a workaround, so hopefully someone will have a better answer - but I can go live with this if I have to.
from time import sleep
import pygame
from tone import Note
def wait_for_player():
while pygame.mixer.music.get_busy():
continue
def play_file(filename):
pygame.init()
Note(0.08, volume=0.01).play(-1)
sleep(1)
pygame.mixer.music.load(filename)
pygame.mixer.music.play(loops=3)
wait_for_player()
if __name__ == '__main__':
play_file('hello_world.mp3')
To be specific, I need to play sound in a while loop which is fast to execute. And the audio needs to be played separately. I've tried various functions/libraries: playsound, winsound, vlc. But none of them meet my demand.
Either the sounds are overlapped, or I need to wait for the sound to finish to continue the next line of code, which blocks the whole process, making the program running with unbearable lags.
Matters in playsound, winsound, vlc:
playsound: has block option but will block the process (with block=True) or overlap the sound (block=False).
winsound: with SND_ASYNC option, say I have audio A and audio B (which needs to be played after audio A), if audio B is played, audio A will stop immediately.
vlc: [000001ba245794d0] mmdevice audio output error: cannot initialize COM (error 0x80010106), which is kind of weird and nothing useful was found on Google. And this method seems not a good option to me and others. I use this simply for its is_playing() function and I can place the next sound to a queue.
So any advice?
If you're just after playing a sound and having it interrupt whatever was playing before, winsound does just that:
import winsound
from time import sleep
winsound.PlaySound("sound.wav", winsound.SND_ASYNC | winsound.SND_FILENAME)
sleep(1)
winsound.PlaySound("sound.wav", winsound.SND_ASYNC | winsound.SND_FILENAME)
sleep(3)
In this example (assuming sound.wav is longer than a second), the sound will start playing, be interrupted after 1 second and start playing again. The second sleep is there to avoid the script ending before the sound stops (stopping the script stops the sound).
If you want to queue up sounds to play one after the other, while your code keeps running:
import threading
import queue
import winsound
from time import sleep
q = queue.Queue()
def thread_function():
while True:
sound = q.get()
if sound is None:
break
winsound.PlaySound(sound, winsound.SND_FILENAME)
if __name__ == "__main__":
t = threading.Thread(target=thread_function)
t.start()
q.put("sound.wav")
print('One...')
sleep(1)
q.put("sound.wav")
print('Two...')
sleep(1)
q.put("sound.wav")
print('Three...')
q.put(None)
t.join()
This simple example queues up a sound which the thread starts playing, then while it is playing, it queues up the next one and a bit later a third. You'll noticed that the sounds play one after the other and the program only stops when the sounds complete playing (and the thread stops due to the None at the end of the queue).
If you're looking to play one sound over the other and have them mix, using winsound won't work, but you can use libraries like pyglet.
For example:
import pyglet
window = pyglet.window.Window()
effect = pyglet.resource.media('sound.wav', streaming=False)
#window.event
def on_key_press(symbol, modifiers):
effect.play()
#window.event
def on_draw():
window.clear()
if __name__ == "__main__":
pyglet.app.run()
This example opens a window and every time you press a key, it'll play the sound immediately, without interrupting previous sounds. The program ends immediately when you close the window.
This can be easily done with playsound.
playsound.playsound('alert.mp3', False)
Setting the second argument to False makes the function run asynchronously.
I'm trying to shuffle a list of mp3 files and then play through them with pyglet. Once the player runs out of sources I want to re-shuffle the list, and then play through them again. This process would happen infinitely. I have been able to get pyglet to play through the sources once, but the on_player_eos event is never fired when I run out of sources. I'm not sure exactly what I am doing wrong, or if I am approaching it in the wrong way. I've tried a couple ways of doing it, but have had no luck getting it to work. Here is the code
import os, sys, random, pyglet
from random import shuffle
song1 = pyglet.media.load('beats/breathe.mp3', streaming=False)
song2 = pyglet.media.load('beats/everything.mp3', streaming=False)
song3 = pyglet.media.load('beats/jazz.mp3', streaming=False)
player = pyglet.media.Player()
# Play beatz
def beatz():
playlist = [song1, song2, song3]
shuffle(playlist)
player.queue(playlist[0])
player.queue(playlist[1])
player.queue(playlist[2])
player.play()
def on_player_eos():
print('repeating')
beatz()
player.on_player_eos = on_player_eos
pyglet.app.run()
I have also tried replacing the 'player.on_player_eos = on_player_eos' with
player.push_handlers(on_player_eos)
But it doesnt seem to do anything either. Any help appreciated.
UPDATE:
I was able to get this to work by simply adding 'streaming=False' to each song variable. Streaming sources in pyglet can only be queued once on a player object, while static sources can be queued as many times as you want.
With recognizing the event 'on_player_eos', I also wasn't aware of the pyglet event loop. In order for pyglet to pick up the events I wanted it to, the code below had to be run.
pyglet.app.run()
This fixed my problems.
The problem is when i play a sound it stops/cuts before finishing playing. (sound being under 1 second)
import pyglet
#from time import sleep
window = pyglet.window.Window()
# i use this for my code.
#pyglet.resource.path = ['/resources']
#pyglet.resource.reindex()
#bullet_sound = pyglet.resource.media("bullet.wav", streaming=False)
## streaming fix's an error message
bullet_sound = pyglet.media.load("/resources/bullet.wav", streaming=False)
#window.event
def on_key_press(symbol, modifiers):
# sound only plays for a millisecond
bullet_sound.play()
# this lets the sound complete
#sleep(1)
# also tried this with 'update()'
#player.queue(bullet_sound)
#def update(dt):
# player.play()
#pyglet.clock.schedule_interval(update, 1/120.0)
pyglet.app.run()
I can't seem to find anything about this when google searching. Sleeping makes the sound finish, so i suppose it has something to do with it dropping the sound prematurely. But how to i get it not to do so?
I even tried using the player, putting it in an update function and queuing it from the event, but that din't change anything.
Segmentation faults, sound cutting, too large exceptions, sound related issues on linux mint might be caused by Linux/PulseAudio, the fix is using OpanAL, installing(might be pre-installed on mint) then adding a line to use it after importing pyglet:
pyglet.options['audio'] = ('openal', 'silent')
So I'm making a music player with the PyMedia module and the Tkinter module. I've been Googling for an answer on to to "check" if PyMedia.startPlayback is busy playing a song, but so far I've been unsuccessful. My goal is to be able to play the next song in the queue when the current song stops playing.
for example:
def Listen(songs):
global player
while True:
songs+=1
def play(number):
player.startPlayback(number)
def pause():
player.pausePlayback()
#init pymedia player
player= pymedia.Player()
player.start()
play(songs)
#wait till song is done playing...
#After finished continue on to the next one
play(songs)
This isn't the exact code but it is the gist of it.
I have python 2.7 on Windows 7. Thanks for you input.
http://pymedia.org/docs/pymedia.player.html
The docs list an isPlaying function for the player. They even use it in the example for the while loop.