Javascript 如何使用JS WebAudioAPI进行节拍检测?

Javascript 如何使用JS WebAudioAPI进行节拍检测?,javascript,audio,web-audio-api,beat-detection,Javascript,Audio,Web Audio Api,Beat Detection,我对使用JavaScriptWebAudioAPI检测歌曲节拍感兴趣,然后在画布中呈现它们 我可以处理画布部分,但我不是一个喜欢听音频的人,我真的不知道如何用JavaScript制作节拍检测器 我已经尝试了以下步骤,但在我的一生中,无法将每个函数之间的点连接起来,从而生成一个函数程序 我知道我应该给你看一些代码,但老实说,我没有任何代码,我所有的尝试都失败了,相关代码在前面提到的文章中 无论如何,我真的非常感谢一些指导,或者更好的是演示如何使用WebAudioAPI实际检测歌曲节拍 谢谢 需要了

我对使用JavaScript
WebAudioAPI
检测歌曲节拍感兴趣,然后在画布中呈现它们

我可以处理画布部分,但我不是一个喜欢听音频的人,我真的不知道如何用JavaScript制作节拍检测器

我已经尝试了以下步骤,但在我的一生中,无法将每个函数之间的点连接起来,从而生成一个函数程序

我知道我应该给你看一些代码,但老实说,我没有任何代码,我所有的尝试都失败了,相关代码在前面提到的文章中

无论如何,我真的非常感谢一些指导,或者更好的是演示如何使用
WebAudioAPI
实际检测歌曲节拍


谢谢

需要了解的主要问题是,尽管它提供了大量源代码,但它远不是最终的完整代码。要获得有效的解决方案,您仍然需要一些编码和调试技能

此答案的大部分代码来自参考文章,原始许可适用于适当的地方。

下面是一个使用上述文章描述的函数的简单示例实现


代码包括为答案编写的准备代码:

  • 通过网络读取本地文件
  • 使用将文件解码为音频数据
然后,如文章所述:

  • 过滤音频,在本例中使用
  • 使用阈值计算峰值
  • 分组间隔计数,然后是节奏计数
对于阈值,我使用了最大值和最小值之间0.98的任意值;在分组时,我添加了一些额外的检查和任意舍入,以避免可能的无限循环,并使其成为一个易于调试的示例

请注意,注释很少用于保持示例实现的简短,因为:

  • 在参考文章中解释了处理背后的逻辑
  • 语法可以在相关方法的API文档中引用

audio\u file.onchange=function(){
var file=this.files[0];
var reader=new FileReader();
var context=new(window.AudioContext | | window.webkitadiocontext)();
reader.onload=函数(){
上下文.解码音频数据(reader.result,函数(缓冲区){
制备(缓冲液);
});
};
reader.readAsArrayBuffer(文件);
};
功能准备(缓冲区){
var offlineContext=new OfflineAudioContext(1,buffer.length,buffer.sampleRate);
var source=offlineContext.createBufferSource();
source.buffer=缓冲区;
var filter=offlineContext.createBiquadFilter();
filter.type=“低通”;
source.connect(过滤器);
filter.connect(offlineContext.destination);
source.start(0);
offlineContext.startRendering();
offlineContext.oncomplete=函数(e){
过程(e);
};
}
功能流程(e){
var filteredBuffer=e.renderedBuffer;
//如果要分析两个通道,请稍后使用另一个通道
var data=filteredBuffer.getChannelData(0);
var max=阵列最大值(数据);
var min=arrayMin(数据);
var阈值=最小值+(最大值-最小值)*0.98;
var peaks=getPeaksAtThreshold(数据,阈值);
var intervalCounts=任意类型(峰值)之间的计数间隔;
var tempoCounts=groupNeighborsByTempo(间隔计数);
tempoCounts.sort(函数(a,b){
返回b.count-a.count;
});
if(临时计数长度){
output.innerHTML=tempoCounts[0].tempo;
}
}
// http://tech.beatport.com/2014/web-audio/beat-detection-using-web-audio/
函数getPeaksAtThreshold(数据,阈值){
var peaksArray=[];
变量长度=data.length;
对于(变量i=0;i阈值){
peaksArray.push(i);
//向前跳约1/4s以越过此峰值。
i+=10000;
}
i++;
}
返回峰值阵列;
}
任意类型之间的函数计数间隔(峰值){
var区间计数=[];
峰值。forEach(函数(峰值、指数){
对于(变量i=0;i<10;i++){
var间隔=峰值[指数+i]-峰值;
var foundInterval=intervalCounts.some(函数(intervalCount){
if(intervalCount.interval==interval)返回intervalCount.count++;
});
//附加检查以避免后期处理中出现无限循环
如果(!isNaN(间隔)&&interval!==0&&foundInterval){
间歇推({
间隔:间隔,,
计数:1
});
}
}
});
返回间隔计数;
}
函数组邻居按节拍(间隔计数){
var tempoCounts=[];
forEach(函数(intervalCount){
//把一个音程转换成节奏
var理论平均值=60/(区间数/区间数/44100);
theoreticalTempo=数学圆(theoreticalTempo);
if(theoreticalTempo==0){
返回;
}
//调整节奏以适应90-180 BPM的范围
而(theoreticalTempo<90)theoreticalTempo*=2;
而(theoreticalTempo>180)theoreticalTempo/=2;
var foundTempo=tempoCounts.some(函数(tempoCount){
if(tempoCount.tempo==theory-altempo)返回tempoCount.count+=intervalCount.count;
});
如果(!foundTempo){
临时计数({
节奏:理论上的节奏,
计数:intervalCount.count
});
}
});
返回临时计数;
}
// http://stackoverflow.com/questions/1669190/javascript-min-max-array-values
函数arrayMin(arr){
var len=阵列长度,
最小值=无穷大;
而(len--){
如果(arr[len]max){
max=arr[len];
}
}
返回最大值;
}


最可能的节奏:

我在这里编写了一个教程,展示了如何使用javascript Web Audio API实现这一点

步骤概述

function countFlatLineGroupings(data) {

 var groupings = 0;
 var newArray = normalizeArray(data);

 function getMax(a) {
    var m = -Infinity,
        i = 0,
        n = a.length;

    for (; i != n; ++i) {
        if (a[i] > m) {
            m = a[i];
        }
    }
    return m;
 }

 function getMin(a) {
    var m = Infinity,
        i = 0,
        n = a.length;

    for (; i != n; ++i) {
        if (a[i] < m) {
            m = a[i];
        }
    }
    return m;
 }

 var max = getMax(newArray);
 var min = getMin(newArray);
 var count = 0;
 var threshold = Math.round((max - min) * 0.2);

 for (var i = 0; i < newArray.length; i++) {

   if (newArray[i] > threshold && newArray[i + 1] < threshold && newArray[i + 2] < threshold && newArray[i + 3] < threshold && newArray[i + 6] < threshold) {
        count++;
    }
 }

 return count;
}

// Count the Groupings
countFlatLineGroupings(lowPassBuffer);
  • 将音频文件转换为
    window.lowPassBuffer  // Low Pass Array Buffer
    window.originalBuffer // Original Non Filtered Array Buffer
    
    function getClip(length, startTime, data) {
    
      var clip_length = length * 44100;
      var section = startTime * 44100;
      var newArr = [];
    
      for (var i = 0; i < clip_length; i++) {
         newArr.push(data[section + i]);
      }
    
      return newArr;
    }
    
    // Overwrite our array buffer to a 10 second clip starting from 00:10s
    window.lowPassFilter = getClip(10, 10, lowPassFilter);
    
    function getSampleClip(data, samples) {
    
      var newArray = [];
      var modulus_coefficient = Math.round(data.length / samples);
    
      for (var i = 0; i < data.length; i++) {
         if (i % modulus_coefficient == 0) {
             newArray.push(data[i]);
         }
      }
      return newArray;
    }
    
    // Overwrite our array to down-sampled array.
    lowPassBuffer = getSampleClip(lowPassFilter, 300);
    
    function normalizeArray(data) {
    
     var newArray = [];
    
     for (var i = 0; i < data.length; i++) {
         newArray.push(Math.abs(Math.round((data[i + 1] - data[i]) * 1000)));
     }
    
     return newArray;
    }
    
    // Overwrite our array to the normalized array
    lowPassBuffer = normalizeArray(lowPassBuffer);
    
    function countFlatLineGroupings(data) {
    
     var groupings = 0;
     var newArray = normalizeArray(data);
    
     function getMax(a) {
        var m = -Infinity,
            i = 0,
            n = a.length;
    
        for (; i != n; ++i) {
            if (a[i] > m) {
                m = a[i];
            }
        }
        return m;
     }
    
     function getMin(a) {
        var m = Infinity,
            i = 0,
            n = a.length;
    
        for (; i != n; ++i) {
            if (a[i] < m) {
                m = a[i];
            }
        }
        return m;
     }
    
     var max = getMax(newArray);
     var min = getMin(newArray);
     var count = 0;
     var threshold = Math.round((max - min) * 0.2);
    
     for (var i = 0; i < newArray.length; i++) {
    
       if (newArray[i] > threshold && newArray[i + 1] < threshold && newArray[i + 2] < threshold && newArray[i + 3] < threshold && newArray[i + 6] < threshold) {
            count++;
        }
     }
    
     return count;
    }
    
    // Count the Groupings
    countFlatLineGroupings(lowPassBuffer);
    
    var final_tempo = countFlatLineGroupings(lowPassBuffer);
    
    // final_tempo will be 21
    final_tempo = final_tempo * 6;
    
    console.log("Tempo: " + final_tempo);
    // final_tempo will be 126