2015-05-07 33 views
11

Şarkı atımlarını algılamak için JavaScript'i WebAudioAPI kullanmayı ve ardından bunları bir tuvale işlemekle ilgileniyorum.Yendi tespiti için JS WebAudioAPI'yi nasıl kullanabilirim?

Tuval bölümünü halledebilirim, ancak büyük bir ses adamı değilim ve JavaScript'te nasıl bir beat detektörü yapılacağını anlamıyorum.

this article'u denedim, ancak hayatım boyunca, işlevsel bir program oluşturmak için her bir işlev arasındaki noktaları birleştiremedim.

Size bir kod göstermeliyim, ama dürüst olmam gerekirse, tüm girişimlerim sefil bir şekilde başarısız oldu ve ilgili kod daha önce bahsedilen makalede var.

Yine de, bazı kılavuzlukları, veya şarkı atımlarını WebAudioAPI ile gerçekten nasıl saptayacağınızı gösteren bir demoyu gerçekten takdir ediyorum.

Teşekkürler!

+0

sen bağlantılı makale bütün konuyu oldukça lanetlemek kapakları Peki, özellikle zor zamanlar geçiriyor musunuz? – Nit

+0

Evet Biliyorum! Bu yüzden çok sinir bozucu, işe yaramayacağım, yani ilk işlev için eşiği veya verileri nereden alacağım, zirveleri almadan önce veya sonra filtreleri kullanabilir miyim? Aptal hissediyorum. – undefined

+0

Eşik, üzerinde çalıştığınız ses türüne çok bağlıdır. Kısa bir süre sizin seçtiğiniz bir sayıdır. Veri, çalışmak istediğiniz ses dosyasıdır. – Nit

cevap

9

the referenced article by Joe Sullivan hakkında anlaşılması gereken en önemli şey, çok fazla kaynak kodu vermesine rağmen, son ve eksiksiz koddan çok uzak olmasıdır. Çalışan bir çözüme ulaşmak için yine de bazı kodlama ve hata ayıklama becerilerine ihtiyacınız olacaktır. Bu yanıt, kodunun çoğunu başvurulan makaleden alır, özgün lisanslama uygun olduğunda geçerlidir. Aşağıda, yukarıdaki makale tarafından tanımlanan işlevlerin kullanılması için naif bir örnek uygulaması yer almaktadır. Ses veri olarak dosyanın çözümlenmesi the FileReader API

  • üzerinde yerel bir dosya okuma

    ve kullanma:


    kod cevabını yazılı hazırlık koddan oluşan makalede açıklandığı gibi:

    • bir low-pass filter
    • hesaplama Tepeler daha sonra, aralığı sayılarını gruplama bir eşik
    • kullanılarak ve bu örnekteki, ses filtre sayar I .98 keyfi bir değerdir kullanılan eşik için

    tempo maksimum ve minimum değerler arasında değişir; gruplandırırken olası sonsuz döngüleri önlemek ve hata ayıklama örneklemesi yapmak için bazı ek kontroller ve rastgele yuvarlama ekledim.işleme arkasındaki mantık ilgili yöntemlerin

  • API docs başvurulabilir başvurulan makalesinde
  • sözdizimi açıklanmaktadır

    • : yorumlama

      Not çünkü örnek uygulama kısa tutmak için kıt


    audio_file.onchange = function() { 
     
        var file = this.files[0]; 
     
        var reader = new FileReader(); 
     
        var context = new(window.AudioContext || window.webkitAudioContext)(); 
     
        reader.onload = function() { 
     
        context.decodeAudioData(reader.result, function(buffer) { 
     
         prepare(buffer); 
     
        }); 
     
        }; 
     
        reader.readAsArrayBuffer(file); 
     
    }; 
     
    
     
    function prepare(buffer) { 
     
        var offlineContext = new OfflineAudioContext(1, buffer.length, buffer.sampleRate); 
     
        var source = offlineContext.createBufferSource(); 
     
        source.buffer = buffer; 
     
        var filter = offlineContext.createBiquadFilter(); 
     
        filter.type = "lowpass"; 
     
        source.connect(filter); 
     
        filter.connect(offlineContext.destination); 
     
        source.start(0); 
     
        offlineContext.startRendering(); 
     
        offlineContext.oncomplete = function(e) { 
     
        process(e); 
     
        }; 
     
    } 
     
    
     
    function process(e) { 
     
        var filteredBuffer = e.renderedBuffer; 
     
        //If you want to analyze both channels, use the other channel later 
     
        var data = filteredBuffer.getChannelData(0); 
     
        var max = arrayMax(data); 
     
        var min = arrayMin(data); 
     
        var threshold = min + (max - min) * 0.98; 
     
        var peaks = getPeaksAtThreshold(data, threshold); 
     
        var intervalCounts = countIntervalsBetweenNearbyPeaks(peaks); 
     
        var tempoCounts = groupNeighborsByTempo(intervalCounts); 
     
        tempoCounts.sort(function(a, b) { 
     
        return b.count - a.count; 
     
        }); 
     
        if (tempoCounts.length) { 
     
        output.innerHTML = tempoCounts[0].tempo; 
     
        } 
     
    } 
     
    
     
    // http://tech.beatport.com/2014/web-audio/beat-detection-using-web-audio/ 
     
    function getPeaksAtThreshold(data, threshold) { 
     
        var peaksArray = []; 
     
        var length = data.length; 
     
        for (var i = 0; i < length;) { 
     
        if (data[i] > threshold) { 
     
         peaksArray.push(i); 
     
         // Skip forward ~ 1/4s to get past this peak. 
     
         i += 10000; 
     
        } 
     
        i++; 
     
        } 
     
        return peaksArray; 
     
    } 
     
    
     
    function countIntervalsBetweenNearbyPeaks(peaks) { 
     
        var intervalCounts = []; 
     
        peaks.forEach(function(peak, index) { 
     
        for (var i = 0; i < 10; i++) { 
     
         var interval = peaks[index + i] - peak; 
     
         var foundInterval = intervalCounts.some(function(intervalCount) { 
     
         if (intervalCount.interval === interval) return intervalCount.count++; 
     
         }); 
     
         //Additional checks to avoid infinite loops in later processing 
     
         if (!isNaN(interval) && interval !== 0 && !foundInterval) { 
     
         intervalCounts.push({ 
     
          interval: interval, 
     
          count: 1 
     
         }); 
     
         } 
     
        } 
     
        }); 
     
        return intervalCounts; 
     
    } 
     
    
     
    function groupNeighborsByTempo(intervalCounts) { 
     
        var tempoCounts = []; 
     
        intervalCounts.forEach(function(intervalCount) { 
     
        //Convert an interval to tempo 
     
        var theoreticalTempo = 60/(intervalCount.interval/44100); 
     
        theoreticalTempo = Math.round(theoreticalTempo); 
     
        if (theoreticalTempo === 0) { 
     
         return; 
     
        } 
     
        // Adjust the tempo to fit within the 90-180 BPM range 
     
        while (theoreticalTempo < 90) theoreticalTempo *= 2; 
     
        while (theoreticalTempo > 180) theoreticalTempo /= 2; 
     
    
     
        var foundTempo = tempoCounts.some(function(tempoCount) { 
     
         if (tempoCount.tempo === theoreticalTempo) return tempoCount.count += intervalCount.count; 
     
        }); 
     
        if (!foundTempo) { 
     
         tempoCounts.push({ 
     
         tempo: theoreticalTempo, 
     
         count: intervalCount.count 
     
         }); 
     
        } 
     
        }); 
     
        return tempoCounts; 
     
    } 
     
    
     
    // http://stackoverflow.com/questions/1669190/javascript-min-max-array-values 
     
    function arrayMin(arr) { 
     
        var len = arr.length, 
     
        min = Infinity; 
     
        while (len--) { 
     
        if (arr[len] < min) { 
     
         min = arr[len]; 
     
        } 
     
        } 
     
        return min; 
     
    } 
     
    
     
    function arrayMax(arr) { 
     
        var len = arr.length, 
     
        max = -Infinity; 
     
        while (len--) { 
     
        if (arr[len] > max) { 
     
         max = arr[len]; 
     
        } 
     
        } 
     
        return max; 
     
    }
    <input id="audio_file" type="file" accept="audio/*"></input> 
     
    <audio id="audio_player"></audio> 
     
    <p> 
     
        Most likely tempo: <span id="output"></span> 
     
    </p>

  • +0

    Teşekkürler! Bu, tartışmaya getirdiğim azlığı göz önünde bulundurarak gerçekten eksiksiz bir cevaptır :) Bu yöntemin gerçekten ne kadar etkili olduğundan emin değilim, ancak şarkıdan beri, * X-Ambassadors - Renegades *, snippet'inle birlikte 128BPM veriyor ve [** bu web sitesi **] üzerinden (https://songbpm.com/x-ambassadors/renegades), 90BPM üzerinden geçtiğinizde. Her neyse, bu sizin hatanız değil, ama algoritmanınki, çok teşekkürler :) – undefined

    +0

    Bir sorun, bağlantılı kodun ve snippet'in düşük geçiş filtresi frekansını ayarlamamasıdır. Kimin varsayılanını bilir. Sayfanın kaynağına bakarsak, 'Düşük geçiş filtresi ile' düğmesi için bunu 200 Hz olarak ayarlar. 'filter.frequency.value = 200' – jaket

    +0

    @jaket Bu, işlevselliği kullanmanın çok temel bir örneğidir, açıkçası iyileştirilmesi gereken çok yer vardır. Ama yorum için teşekkürler. – Nit

    6

    Burada javascript Web Audio API'siyle nasıl yapılacağını gösteren bir eğitim yazdım. Adımları

    https://askmacgyver.com/blog/tutorial/how-to-implement-tempo-detection-in-your-application

    Anahat

    1. Tampon
    2. Alçak geçiş sayesinde
    3. Çalıştır Dizisi Tampon
    4. Tampon
    5. Array 10 saniye Klip Trim Filtre bir Array içine Ses Dosyası Dönüşümü
    6. Verileri Doldurun Örnek
    7. Normale Gruplamalarınızdan Veri
    8. Sayım Hacmi Gruplandırmaları
    9. Infer Tempo

    Bu kod aşağıda ağır işi yapar sayın. Alçak geçiş sayesinde

    Yük Ses Dizi Tampon içine Dosya ve Çalıştır Filtre

    function createBuffers(url) { 
    
    // Fetch Audio Track via AJAX with URL 
    request = new XMLHttpRequest(); 
    
    request.open('GET', url, true); 
    request.responseType = 'arraybuffer'; 
    
    request.onload = function(ajaxResponseBuffer) { 
    
        // Create and Save Original Buffer Audio Context in 'originalBuffer' 
        var audioCtx = new AudioContext(); 
        var songLength = ajaxResponseBuffer.total; 
    
        // Arguments: Channels, Length, Sample Rate 
        var offlineCtx = new OfflineAudioContext(1, songLength, 44100); 
        source = offlineCtx.createBufferSource(); 
        var audioData = request.response; 
        audioCtx.decodeAudioData(audioData, function(buffer) { 
    
         window.originalBuffer = buffer.getChannelData(0); 
         var source = offlineCtx.createBufferSource(); 
         source.buffer = buffer; 
    
         // Create a Low Pass Filter to Isolate Low End Beat 
         var filter = offlineCtx.createBiquadFilter(); 
         filter.type = "lowpass"; 
         filter.frequency.value = 140; 
         source.connect(filter); 
         filter.connect(offlineCtx.destination); 
    
          // Render this low pass filter data to new Audio Context and Save in 'lowPassBuffer' 
          offlineCtx.startRendering().then(function(lowPassAudioBuffer) { 
    
          var audioCtx = new(window.AudioContext || window.webkitAudioContext)(); 
          var song = audioCtx.createBufferSource(); 
          song.buffer = lowPassAudioBuffer; 
          song.connect(audioCtx.destination); 
    
          // Save lowPassBuffer in Global Array 
          window.lowPassBuffer = song.buffer.getChannelData(0); 
          console.log("Low Pass Buffer Rendered!"); 
          }); 
    
         }, 
         function(e) {}); 
    } 
    request.send(); 
    } 
    
    
    createBuffers('https://askmacgyver.com/test/Maroon5-Moves-Like-Jagger-128bpm.mp3'); 
    
    Şimdi Array Alçak Geçiren Filtreli Song Tampon (Ve Orjinal)

    O comprise var

    Birkaç giriş, sampleRate (44100 şarkının saniye sayısı ile çarpılır).

    window.lowPassBuffer // Low Pass Array Buffer 
    window.originalBuffer // Original Non Filtered Array Buffer 
    

    Verilerinizi Normale Kişisel Klip Numune Şarkı

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

    Aşağı dan

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

    10 İkinci Klip Trim

    Sayım Düz Çizgi Gruplandırmaları

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

    İkinci Gruplama Dakika Başına Beats Türetilemedi 60 Saniyede kadar sayın Ölçek 10

    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 
    
    İlgili konular