【问题标题】:Audio object duration keeps changing?音频对象持续时间不断变化?
【发布时间】:2018-08-11 06:38:08
【问题描述】:

我正在尝试获取列表中每个对象的持续时间(以秒为单位),但我的结果遇到了一些问题。我认为这是因为我没有完全理解异步行为。

我从 n 个对象的数组 songs[] 开始我的函数。在我的函数结束时,目标是有一个数组songLengths,其中第一个值是songs[] 中第一个对象的持续时间,依此类推。

我试图在这个例子之后为我的函数建模:JavaScript closure inside loops – simple practical example。但是我得到每个songLengths[] 索引的未定义值。

$("#file").change(function(e) {
  var songs = e.currentTarget.files;
  var length = songs.length;
  var songLengths = [];

  function createfunc(i) {
    return function() {
      console.log("my val = " + i);
    };
  }

  for (var i = 0; i < length; i++) {
    var seconds = 0;
    var filename = songs[i].name;
    var objectURL = URL.createObjectURL(songs[i]);
    var mySound = new Audio([objectURL]);

    mySound.addEventListener(
      "canplaythrough",
      function(index) {
        seconds = index.currentTarget.duration;
      },
      false,
    );
    songLengths[i] = createfunc(i);
  }
});

【问题讨论】:

  • 您是否在某处定义了length?你能提供一个songs 包含什么的例子吗?您从不保存长度,只保存歌曲索引。
  • hello @Xufox 感谢您的评论,songs[] 包含用户上传的音频文件列表。例如,当我上传 5 首歌曲并打印出歌曲 [] 时,这就是控制台中的样子:i.imgur.com/xPP2k1L.png 长度已定义(使用 var length = song.length;我将编辑帖子以包含它)和它正确获取上传的文件数。所以我可以正确地遍历列表并以秒为单位获取时间,但我认为我并没有完全异步地这样做,因为有时一首歌曲的持续时间是另一首歌曲的持续时间

标签: javascript audio metadata duration


【解决方案1】:

由于promises,这样的事情应该可以在没有混乱的闭包的情况下工作,并且增加了干净的异步性。

基本上,我们声明了一个辅助函数computeLength,它采用HTML5 File 并执行您已经完成的计算其长度的魔法。 (虽然我确实添加了URL.revokeObjectURL 调用以避免内存泄漏。)

不过,promise 不只是返回持续时间,而是使用包含原始文件对象和计算的持续时间的对象解析。

然后在事件处理程序中,我们映射选择的files 以从中创建computeLength 承诺,并使用Promise.all 等待所有承诺,然后记录[{file, duration}, {file, duration}, ...] 的结果数组。

function computeLength(file) {
  return new Promise((resolve) => {
    var objectURL = URL.createObjectURL(file);
    var mySound = new Audio([objectURL]);
    mySound.addEventListener(
      "canplaythrough",
      () => {
        URL.revokeObjectURL(objectURL);
        resolve({
          file,
          duration: mySound.duration
        });
      },
      false,
    );
  });  
}

$("#file").change(function(e) {
  var files = Array.from(e.currentTarget.files);
  Promise.all(files.map(computeLength)).then((songs) => {
    console.log(songs);
  });
});

【讨论】:

  • 您好,非常感谢您的回复!我尝试了这段代码,但我认为files.map出了点问题,我把代码放在这个jsfiddlejsfiddle.net/Loxd1knm/5中,如果你在查看控制台时运行它,它会显示Uncaught TypeError:files.map不是一个函数,我还添加了 var song = [];到 $("#file') 函数的开头
  • 啊,我的错! var files = ... 必须是 var files = Array.from(...) 才能使 map 工作!
  • 是的,这非常有效!持续正确的持续时间,谢谢!
猜你喜欢
  • 2022-06-29
  • 1970-01-01
  • 2013-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
  • 2015-07-25
相关资源
最近更新 更多