20

Im my audio player I need to get the duration of my audio track. I need a function that gets src of the audio and returns its duration. Here is what I am trying to do but does not work:

function getDuration(src){
    var audio = new Audio();
    audio.src = "./audio/2.mp3";
    var due;
    return getVal(audio);
}
function getVal(audio){
    $(audio).on("loadedmetadata", function(){
        var val = audio.duration;
        console.log(">>>" + val);
        return val;
    });
}

I tried to split into two functions but it does not work. It would be great if it was as on working function.

Any idea?

TheGuy
  • 427
  • 2
  • 4
  • 12

1 Answers1

31

because you're relying on an event to fire, you can't return a value in getDuration or getVal

instead, you want to use a callback function, like this (callbacks)

The example assume you want to put the duration into a span written like this

<span id="duration"></span>

function getDuration(src, cb) {
    var audio = new Audio();
    $(audio).on("loadedmetadata", function(){
        cb(audio.duration);
    });
    audio.src = src;
}
getDuration("./audio/2.mp3", function(length) {
    console.log('I got length ' + length);
    document.getElementById("duration").textContent = length;
});

Any code that needs to "know" the length should be inside the callback function (where console.log is)


using Promises

function getDuration(src) {
    return new Promise(function(resolve) {
        var audio = new Audio();
        $(audio).on("loadedmetadata", function(){
            resolve(audio.duration);
        });
        audio.src = src;
    });
}
getDuration("./audio/2.mp3")
.then(function(length) {
    console.log('I got length ' + length);
    document.getElementById("duration").textContent = length;
});

using Events - note 'myAudioDurationEvent' can obviously be (almost) anything you want

function getDuration(src, obj) {
    return new Promise(function(resolve) {
        var audio = new Audio();
        $(audio).on("loadedmetadata", function(){
            var event = new CustomEvent("myAudioDurationEvent", {
                detail: {
                    duration: audio.duration,

                }
            });
            obj.dispatchEvent(event);
        });
        audio.src = src;
    });
}
var span = document.getElementById('xyz'); // you'll need to provide better logic here
span.addEventListener('myAudioDurationEvent', function(e) {
    span.textContent = e.detail.duration;
});
getDuration("./audio/2.mp3", span);

although, this can be done similarly with callback or promise by passing in a destination to a modified getDuration function in those solutions as well - my point about using event listeners was more appropriate if one span for example was updated with duration multiple times - this solution still only does each span only once, so can be achieved with the other methods just as easily


given the new information in the comments for this answer, I believe this to be the better solution

function getDuration(src, destination) {
    var audio = new Audio();
    $(audio).on("loadedmetadata", function(){
        destination.textContent = audio.duration;
    });
    audio.src = src;
}

and then invoke getDuration as needed like this

var span = createOrGetSomeSpanElement();
getDuration("./audio/2.mp3", span);

createOrGetSomeSpanElement returns the destination element to use in the getDuration function - how this is done is up to you, seeing as you create a playlist in a loop, I'm guessing you have some element created to receive the audio length already created - it's hard to answer a half asked question sometimes

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Thanks for your code. Still im not sure if it match with my requirement. So based on this it is not possible to have a value to do something like: ` getDuration('./music.mp3') ` and get respond like `249.2122`? As far as I understand, I have to modify my span inside the callback function. – TheGuy Jan 07 '16 at 05:09
  • yes, because you can't put javascript code directly inside a span - let me amend the answer with an example – Jaromanda X Jan 07 '16 at 05:11
  • I know that span was on an example. I was just trying to show how I want to get a single final value from the function. – TheGuy Jan 07 '16 at 05:12
  • 1
    as the "answer" will be asynchronous, you don't know when you will have this "single final value" - so any code that requires this value will need to be written in such a way to account for its asynchronous nature – Jaromanda X Jan 07 '16 at 05:15
  • 1
    There is actually a third way this can done, using events - events would be more appropriate for dynamically updating the displayed duration every time you call getDuration with a new src – Jaromanda X Jan 07 '16 at 05:26
  • Can you please give an example of implementing this by events. Since I am using this function inside a loop function, updating the html tag in the callback function updates all previous html tags in each round. Thats way i was trying to return one final value. However I like to see your example for events which might solve my problem. Thanks – TheGuy Jan 07 '16 at 12:26
  • `all previous html tags` - I don't understand - do you have more than one duration on the page at a time? – Jaromanda X Jan 07 '16 at 12:28
  • Yes, it's like a play list that show the duration of each audio track in the list. this is done by a loop that creates `
  • ` tags and assigns values.
  • – TheGuy Jan 07 '16 at 12:33
  • 1
    @TheGuy - I've added an event example - but please read the last paragraph - events are not necessarily a better solution to your use case – Jaromanda X Jan 07 '16 at 12:38
  • Thanks for your solutions. Since you have provided 3 good possible approaches, and my question is kind of beyond the discussion here, so i made a new question with a working example. Once again thanks for you help. [link](http://stackoverflow.com/questions/34656295/how-to-update-the-value-of-a-html-tag-in-a-loop-without-overriding-previous-roun) – TheGuy Jan 07 '16 at 13:48