Reputation: 544
I am trying to get the total duration of audio from an array of audio paths.
Here is what the array would look like:
var sound_paths = ["1.mp3","2.mp3",...]
I have looked at this post, which helped: how to get audio.duration value by a function
However, I do not know how to implement this over an array. The idea is that I want to loop over each audio file, get its duration, and add it to a "sum_duration" variable.
I cannot seem to figure out a way to do this over a for loop. I have tried Promises (which I am admittedly new at): (Note the functions come from a class)
getDuration(src,cb){
// takes a source audio file and a callback function
var audio = new Audio();
audio.addEventListener("loadedmetadata",()=>{
cb(audio.duration);
});
audio.src = src;
}
getAudioArrayDuration(aud_path_arr){
// takes in an array of audio paths, finds the total audio duration
// in seconds
return new Promise((resolve)=>{
var duration = 0;
for(const aud_path in aud_path_arr){
var audio = new Audio();
audio.src = aud_path;
audio.onloadedmetadata = ()=>{
console.log(audio.duration);
duration += audio.duration;
}
resolve(duration);
}
});
}
However, this obviously does not work, and will just return the duration value of 0.
How can I loop over audio files and return the total audio duration of each file summed?
Upvotes: 2
Views: 2094
Reputation: 161
I think, in this case, working with promises is the right approach, time to get used to them ;) Try to remember, a promise will fullfil your wish in the near future. What makes your problem harder is that you have an array of files to check, each will need to separately be covered by it's own Promise, just so that your program can know when all of them have been fullfilled.
I always call my 'promised' getters 'fetch...', that way I know it'll return a promise instead of a direct value.
function fetchDuration(path) {
return new Promise((resolve) => {
const audio = new Audio();
audio.src = path;
audio.addEventListener(
'loadedmetadata',
() => {
// To keep a promise maintainable, only do 1
// asynchronous activity for each promise you make
resolve(audio.duration)
},
);
})
}
function fetchTotalDuration(paths) {
// Create an array of promises and wait until all have completed
return Promise.all(paths.map((path) => fetchDuration(path)))
// Reduce the results back to a single value
.then((durations) => durations.reduce(
(acc, duration) => acc + duration,
0,
))
;
}
At some point, your code is going to have to deal with this asynchronous stuff, I honestly believe that Promises are the easiest way to do this. It takes a little getting used to, but it'll be worth it in the end. The above could be used in your code something along the lines of:
window.addEventListener('DOMContentLoaded', () => {
fetchTotalDuration(["1.mp3","2.mp3",...])
.then((totalDuration) => {
document.querySelector('.player__total-duration').innerHTML = totalDuration;
})
;
});
Upvotes: 2
Reputation: 10879
I hacked this together real quick, so you'll have to adapt it to your function structure, but it's a working code snippet that should send you in the right direction.
Simply keep track of which audio files have been loaded, and if that matches the number of audio files queried, you call the callback with the total duration.
You should also take failing requests into account, so that if the loadedmetadata
event never fires, you can react accordingly (by either falling back to 0 duration for that file, or throwing an Exception, etc.).
const cb = function(duration) {
console.log(`Total duration: ${duration}`);
};
let sound_paths = ["https://rawcdn.githack.com/anars/blank-audio/92f06aaa1f1f4cae365af4a256b04cf9014de564/5-seconds-of-silence.mp3","https://rawcdn.githack.com/anars/blank-audio/92f06aaa1f1f4cae365af4a256b04cf9014de564/2-seconds-of-silence.mp3"];
let totalDuration = 0;
let loadedSounds = [];
sound_paths.map(src => {
const audio = new Audio();
audio.addEventListener("loadedmetadata", ()=>{
totalDuration += audio.duration;
loadedSounds.push(audio);
if ( loadedSounds.length === sound_paths.length ) {
cb( totalDuration );
}
});
audio.src = src;
});
Upvotes: 1