Length of a mp3 file in python

( Sorry for double post, please remove the same thread in game WIP and demos)

# Music script by RockyMadio #

import wave
import contextlib
import GameLogic
import aud


cont = GameLogic.getCurrentController() 
own = cont.owner 
fname = own['songpath'] # will be changed in future
own['timer'] = 0




with contextlib.closing(wave.open(fname,'r')) as f:
    frames = f.getnframes()
    rate = f.getframerate()
    duration = frames / float(rate)    
    cont.owner["length"] = duration
                 
# Total duration #
minutes = int(own['length']/60)
seconds = int(own['length']%60)


own['reallength'] = str(minutes) + ":" + str("%02i"% seconds)


# Play the song #
song = aud.Factory.file(fname)
aud.device().play(song)   

Hi everyone,

i made this script to calculate the length of any audio (wave type) for my music game. The script works well, but anyway the wave file is kinda too large for the game, even that i just resized all of them. I think there should be a way to turn this script into another version for mp3 file.

(I really need the length to limit the beat map i made for the note patterns and to end the track)
I thank you in advance.

Hi, I’m not that great with audio stuff, but I can tell you what I’d do…

It’s amazing that you managed to get the length of the track by python, but are you sure that you need to?
Looking at your dance project it seems like you’re using songs that already exist, rather than generating your own dynamically. In that case you can get the song length in windows explorer, you don’t need to get it in game.

I’d just write a short dictionary with some data about the songs I was using:

song_data = {"voodoo_people":{"artist":"The Prodigy","title":"Voodoo People (Original)","length":18540},
    "dont_stop_me_now":{"artist":"Queen","title":"Don't Stop Me Now","length":13020}}

I’m sorry I don’t have any more useful advice about decoding data from mp3s, it’s not something I’ve tried before.

BTW: If you’re using MP3s, you should be careful, because when i used them in the past through blender’s normal sound system they sometimes took a bit of time to initiate. I don’t know why, or if it is something that has been fixed, but it could cause problems with your timing…

thanks smoking_mirror

i kinda need the total duration of a track to limit the beat map, because the longer the music is being played, the longer the beatmap file still runs. I want to stop everything and let a result board appear, when the music timer touches the total duration of the song.


Your suggestion is to apply the duration directly into the game without calculating it right?

a mp3 still runs well with true timing (Blender 2.69),i tried it, but sadly i cant get its duration

To get the duration of these variably-compressed sound files you basically have to parse over the whole thing. I’d suggest picking up a 3rd-party library to handle the work for you, like this: https://github.com/devsnd/tinytag

Unless you plan on supporting custom songs, it may be easier to include per-song configuration with duration, name, etc.

thanks pdftgs

I think it should be useful. how can i attach this library into blender?

It’s pure-python, so you need only copy the tinytag folder alongside the .blend and use like so:

from tinytag import TinyTag
tag = TinyTag.get('/some/music.mp3')
print('This track is by %s.' % tag.artist)
print('It is %f seconds long.' % tag.duration)

I should note the MP3 patent holder wants royalties after distributing 5000 copies of the game.
Might consider ogg vorbis instead, it’s the non-evil open source alternative.

Yeah, unless you want players to be able to upload their own songs or some thing like that (actually that might be a good way to get around copyright issues, just allow people to put MP3s or wavs in a folder and play them from there…)

Basically there’s two types of content you have to deal with in a game, pre-generated or dynamic.
Randomly generated levels or songs would be dynamic content, as would user uploaded songs.
Songs packed in and shipped with a game would be pregenerated content.

You only have to do on the fly calculations for dynamic content. everything else it’s better to do the calculations before packing the game. Otherwise it just increases loading time, and it’s the same data every time right?

song duration in seconds * 60 frames per second = song duration in blender logic frames

Though a slow frame rate could cause the game to go out of sinc… which wouldn’t be good.

thanks smoking mirror, i will pay more attention for that

Anyway , i need to input a float value of the duration into a dictionary. Do you know how to turn … for example 3:20 into float?

You can split by the colon, and then multiply the minutes by sixty.

thanks agoose77
can you please explain to me more clearly ? i am not sure i can do it flawlessly

min,sec = duration.split(":")
fps=60 # your fps setting
dtime=(min*60+sec)*fps

edit:
And as said before:
If you use mp3 or ogg files, their decoding get time in the beginning.
Decoder must decode at least one frame, before it goes to audiobuffer.

I strongly advice to use aud library, with buffered option.
This is when timing needs accurate.

I play many time without buffer without problems, must sometimes it delays too much.
http://www.blender.org/api/blender_python_api_2_73a_release/aud.html

Careful, it’s a string type!


timings = duration.split(":")

try:
    minutes, seconds = timings
except ValueError:
    seconds = timings

else:
    seconds += minutes * 60


I’d recommend using audaspace for this, it’s already included in Blender.

string…Eh Oh yeah indeed

thank you ! i will try these options

String :frowning: This time I tested it (correction)


duration="3:20"
fps=60

timings = duration.split(":")

try:
    minutes, seconds = timings
except ValueError:
    seconds = int(timings) * fps

else:
    seconds = (int(seconds) + int(minutes) * 60 ) * fps
    
print (seconds)

edit: One bracked more

you are right. the mp3 file really delays like about 0.3 secibd and all the beat maps i created for the music game also follow behind it, and give offbeat effects too. With regular wave file it really loads very fast.

I reduced the wave files with its sample rate from 44100 to 11025 Hz. The quality is kinda abit unclear compared to the original file. but its still acceptable and causes no noise in the game.

All the tracks i use for the game is just about 2 minutes. so each file just contains about 3 - 4 mb

Playing music with aud (audaspace) not so hard.
11 khz is very low, Sound quality goes below poorest c-cassette, just my thoughts.


device = aud.device()
factory = aud.Factory(bge.logic.expandPath('//Music/Musica.ogg'))
factory_buffered = aud.Factory.buffer(factory)
#We get whole audio file to memory, it eats memory, timing is so important

#
#Here is place when you set reset counter
#

handle = device.play(factory_buffered) #Just lauch preloaded song

thanks pohjan

ya it really takes like 2 second to load the file, i will try to adjust this

No need to any adjust, just rest your musical counter AFTER when soundfile is fully loaded.
I shown reset place in the script

Loading speed depends your cpu, diskspeed and who knows things.