如何从多个音频文件中获取总音频持续时间

Posted

技术标签:

【中文标题】如何从多个音频文件中获取总音频持续时间【英文标题】:How to get total audio duration from multiple audio files 【发布时间】:2021-07-27 14:32:53 【问题描述】:

我正在尝试从一组音频路径中获取音频的总持续时间。

数组如下所示:

var sound_paths = ["1.mp3","2.mp3",...]

我看过这篇文章,它有帮助: how to get audio.duration value by a function

但是,我不知道如何在数组上实现它。这个想法是我想遍历每个音频文件,获取其持续时间,并将其添加到“sum_duration”变量中。

我似乎无法找到通过 for 循环执行此操作的方法。我已经尝试过 Promises(我承认我是新手):(注意函数来自一个类)

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

    );


但是,这显然不起作用,只会返回持续时间值 0。

如何循环播放音频文件并返回每个文件的总音频时长?

【问题讨论】:

【参考方案1】:

我认为,在这种情况下,使用 Promise 是正确的方法,是时候习惯它们了 ;) 试着记住,Promise 将在不久的将来实现你的愿望。让你的问题更难的是你有一系列文件要检查,每个文件都需要分别被它自己的 Promise 覆盖,这样你的程序就可以知道它们什么时候都被填满了。

我总是称我的 'promised' getter 为 'fetch...',这样我就知道它会返回一个 promise 而不是直接值。

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,
    ))
  ;

在某些时候,你的代码将不得不处理这些异步的东西,我真的相信 Promises 是最简单的方法。这需要一点时间来适应,但最终它会是值得的。以上内容可以在您的代码中使用,类似于:

window.addEventListener('DOMContentLoaded', () => 
  fetchTotalDuration(["1.mp3","2.mp3",...])
    .then((totalDuration) => 
      document.querySelector('.player__total-duration').innerhtml = totalDuration;
    )
  ;
);

【讨论】:

太棒了!您是否有理由不执行“const audio = new Audio(path);”而不是在创建后更改音频的 src? 嘿@Steak,我从没想过,只是采用了现有的解决方案并改进了它处理承诺的方式。 (抱歉回复晚了) 谢谢!不用担心!【参考方案2】:

我很快就完成了这个,所以你必须根据你的函数结构来调整它,但它是一个工作代码 sn-p 应该会引导你朝着正确的方向前进。

只需跟踪已加载的音频文件,如果与查询的音频文件数量相匹配,则使用总时长调用回调。

您还应该考虑失败的请求,以便如果 loadedmetadata 事件从未触发,您可以做出相应的反应(通过将该文件的持续时间回退到 0,或抛出异常等)。

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

【讨论】:

以上是关于如何从多个音频文件中获取总音频持续时间的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用相同音频 ID 的情况下播放从数据库中获取的多个音频文件

具有立体声或多个音频流的媒体流的音频持续时间的特定 mediainfo 命令

如何在 iOS 中获取音频文件的持续时间?

如何从颤动的路径中获取音频元数据?

保存多个录制音频文件并获取所有录制音频

从java中的Url获取音频持续时间