Find the Length of a Song with Pygame - python

I'm building an Radio Automation Program, but I can't figure out how to have a timer countdown the number of seconds left in the song. I'm currently using Pygame and don't really want to load another toolkit just for this. So far I can make a timer count up using this:
import pygame
#setup music
track = "Music/Track02.wav"
pygame.mixer.music.load(track)
pygame.mixer.music.play()
print("Playing Music")
while(pygame.mixer.music.get_busy()):
print "\r"+str(pygame.mixer.music.get_pos()),
But I have no idea how to get the total length of the song and countdown without having played the song already.

You could also load the song as a normal sound and then check the length while using mixer.music for playing it.
a = pygame.mixer.Sound("test.wav")
print("length",a.get_length())

The mutagen.mp3 can be used to find the length of music.
Firstly: pip install mutagen
-> İmport:
from pygame import *
from mutagen.mp3 import MP3
-> Use:
mixer.music.load('example.mp3')
song = MP3('example.mp3')
songLength = song.info.length

Check the Documentation. According to the site, this function returns the length of a sound object in seconds. So, time remaining is simply (pygame.mixer.music.get_length() - pygame.mixer.music.get_pos())*-1.
That way, it displays as a negative number like most time remaining counters do in a music player. Note, I don't have pygame on this computer, so I can't test it. So, check it just to make sure.

Related

How do I get inputs from a MIDI device through Pygame?

I'm somewhat new to both Python and Pygame, and trying to simply read MIDI controls live from a Windows computer. I've looked into Pygame and have set up what I think to be most of the right syntaxes, but the shell seems to crash or at least reset at the line pygame.midi.Input(input_id) every time.
import pygame.midi
import time
pygame.init()
pygame.midi.init()
print("The default input device number is " + str(pygame.midi.get_default_input_id()))
input_id = int(pygame.midi.get_default_input_id())
clock = pygame.time.Clock()
crashed = False
while (crashed == False):
print("input:")
pygame.midi.Input(input_id)
print(str(pygame.midi.Input.read(10)))
clock.tick(2)
I am also new to both Python and and Pygame.
I also tried for quite some time to get the midi input reading to work. In another forum I was pointed to an included sample file in the library. You can see the answer here.
Side note:
On my computer there are several Midi devices active. So it maybe would be a good idea to not rely on the default input id. You can get a list of your Midi devices like this:
import pygame
from pygame.locals import *
from pygame import midi
def printMIDIDeviceList():
for i in range(pygame.midi.get_count()):
print(pygame.midi.get_device_info(i), i)

How would I be able to pan a sound in pygame?

Basically, what I want to accomplish here, is a way to pan the sound while the game is running. I'd like to make the volumes (left and right) change depending on the position of the player. For now I've got a simple code that I thought that would be a good test:
pygame.mixer.init()
self.sound = pygame.mixer.Sound("System Of A Down - DDevil #06.mp3")
print("I could get here")
self.music = self.sound.play()
self.music.set_volume(1.0, 0)
First, I tried something similar but with pygame.mixer.music, but I came to realize that there's no way to change the volumes separately this way, or so I think, then I changed to the code presented here.
Now it seems like the file is not able to be loaded, my guess is that the file is too big to be treated as a sound in pygame. Any idea of how I'd be able to make this work?
You can pan on the Channel like this:
from os import split, join
import pygame
import pygame.examples.aliens
pygame.init()
# get path to an example punch.wav file that comes with pygame.
sound_path = join(split(pygame.examples.aliens.__file__)[0], 'data', 'punch.wav')
sound = pygame.mixer.Sound(sound_path)
# mixer can mix several sounds together at once.
# Find a free channel to play the sound on.
channel = pygame.mixer.find_channel()
# pan volume full loudness on the left, and silent on right.
channel.set_volume(1.0, 0.0)
channel.play(sound)
https://www.pygame.org/docs/ref/mixer.html#pygame.mixer.Channel
It may be worth looking into a seperate audio library for this. In general i'd recommend PortAudio (which is C), but using the python bindings for it provided by PyAudio. This will give you much more control over the exact audio stream.
To simplify this even further, there is a library known as PyDub which is built ontop of PyAudio for a high level interface (it even has a specific pan method!).
from pydub import AudioSegment
from pydub.playback import play
backgroundMusic = AudioSegment.from_wav("music.wav")
# pan the audio 15% to the right
panned_right = backgroundMusic.pan(+0.15)
# pan the audio 50% to the left
panned_left = backgroundMusic.pan(-0.50)
#Play audio
while True:
try:
play(panned_left)
#play(panned_right)
If this is too slow or doesn't provide effective real-time implementations then I would definately give PyAudio a go because you will also learn a lot more about audio processing in the process!
PS. if you do use PyAudio make sure to check out the callback techniques so that the game you are running can continue to run in parallel using different threads.
This answer certainly can help you.
Basically, you get the screen width, then you pan the left according to player.pos.x / screen_width, and the right according to 1 - player.pos.y / screen_width like that:
Pseudocode:
channel = music.play() # we will pan the music through the use of a channel
screen_width = screen.get_surface().get_width()
[main loop]:
right = player.pos.x / screen_width
left = player.pos.x / screen_width
channel.set_volume(left, right)
Documentation:
pygame.mixer.Channel
pygame.mixer.Sound
pygame.mixer.Channel.set_volume

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()

Music player for Python not working

I want to be able to play multiple songs through a playlist using python, but it will only play the last song on the list. Please help.
from pygame import mixer # Load the required library
from os import listdir
k = listdir('C:/LOCAL')
print(k)
mixer.init()
for x in k:
y = "C:/LOCAL/" + x
print y
mixer.music.queue(y)
mixer.music.load(y)
mixer.music.play()
Your problem is that you assume that playing music with pygame will pause the program until the music is finished - which is not the case. As a result, it tries starting a song, and then it's starting another, and another, etc.
There are a few ways of trying to correct this. You could either:
Use pygame events and "tell" pygame to fire an event when the song finishes (though this requires a display surface (window) to be opened within pygame), or
Detect the length of the song, and sleep for that amount of time (which is more compatible with your current code).
I'm going to assume that you would like to do option 2, since your code works better with it.
To get the length of an MP3 file (I've not tried it with any other types), you could use the Mutagen library.
Some example code to get the length of an MP3 file (in seconds):
from mutagen.mp3 import MP3
tracklength = MP3("/path/to/song.mp3").info.length
Then you could substitute the path with y, and time.sleep for the amount of time returned, before continuing to the next iteration of the loop.
Hope that helps.
(also, you don't need to queue a file before loading it - just load and play)

Python gtts vlc plays too quickly to hear the first couple words

In python, using the module gTTS and VLC-python, I've created a Text to speech program, which is quite simple to do.
But the thing which is bugging me is when I get to playing the mp3 file created by gTTS it skips the first word or two.
So if I have the string "The weather today will be cloudy". It'll speak out "today will be cloudy"
Even if I adjust the string, it seems to miss out the first word or two, sometimes it starts mid word.
When I play the audio file outside of the code, it plays normally, sometimes it stutters on the first word but if I rewind and have it wait a second, it plays perfectly.
Is there a way to load the audio clip or buffer it before playing so it starts smoothly?
In your code you are going to have something along the lines of:
self.Media = self.Instance.media_new_path('my.mp3')
self.player.set_media(self.Media)
self.player.set_xwindow(self.panel1.GetHandle())
which defines what is going to be played.
Then you will have something like:
if self.player.play() == -1:
print("Error playing file")
else:
pass
where you tell vlc to start playing the file.
put a time.sleep(3) before that play command or actuate the play function from a separate button or something.
This is the equivalent of the:
vlc --no-playlist-autostart vp.mp3
or
vlc --start-paused vp.mp3 command line options.
i.e. load the file but don't start playing it, until I tell you.
I found another library called playsound which plays the audio files back without any clipping:
from gtts import gTTS
from playsound import playsound
text = "Say something."
speech = gTTS(text = text, lang = 'en', slow = False)
mp3_file = 'text.mp3'
speech.save(mp3_file)
playsound(mp3_file)
I think this is a better solution than VLC, even if there was no clipping of audio in VLC, because using the vlc library requires you to have VLC Media Player installed on your computer, whereas this playsound library doesn't require you to install anything else for it to work.

Categories

Resources