TheGuy
TheGuy

Reputation: 477

how to get audio.duration value by a function

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?

Upvotes: 24

Views: 61175

Answers (3)

Shubham Soni
Shubham Soni

Reputation: 1

The issue with your code is that you're returning the value from the getVal function inside an event listener (loadedmetadata), but the getDuration function returns before the event is triggered, which is why you don't get the correct value.

Since loadedmetadata is an asynchronous event (i.e., the audio file is still loading when you try to access its duration), you need to handle it using a callback or return a Promise to ensure the duration is accessed only after the metadata is loaded.

Upvotes: 0

kind user
kind user

Reputation: 41903

Had a similar issue recently but with a file input, where I had to check duration of the file uploaded by user. Here's the solution:

const onDrop = ([file]: File[]) => {
  const audio = document.createElement('audio')
  audio.setAttribute('src', URL.createObjectURL(file))
  audio.addEventListener('loadedmetadata', () => {
     const duration = audio.duration;
     // do stuff with the duration
  });
}

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

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

Upvotes: 35

Related Questions