Chord Detection Algorithm with the Web Audio API [closed]
Asked Answered
H

1

6

First off I'm trying to implement this chord detection algorithm: http://www.music.mcgill.ca/~jason/mumt621/papers5/fujishima_1999.pdf

I originally implemented the algorithm to use my microphone, but it didn't work. As a test I created three oscillators to make a c chord, but the algorithm still does not work. I think I should only be seeing a higher number for C,E and G but I see numbers for all the notes. Is there a problem with my implementation of the algorithm? or is it my N, fref, or fs value?

Here is a snippet of code with the important parts:

// Set audio Context
window.AudioContext = window.AudioContext || window.webkitAudioContext;

var mediaStreamSource = null;
var analyser = null;
var N = 4096;//8192;//2048; // Samples of Sound
var bufferLen = null;
var buffer = null;
var PCP = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // Pitch Class Profiles
var fref = 261.63; // Reference frequency middle C (C4)
// fref = 65.4; // Reference frequency C2
// fref = 440.0; // Reference frequency A4
var audioContext = new AudioContext();
var fs = audioContext.sampleRate; // Retrieve sampling rate. Usually 48KHz
var useMicrophone = false;

navigator.mediaDevices.getUserMedia(constraints)
  .then(function(stream) {
    // Create an analyzer node to process the audio
    analyser = audioContext.createAnalyser();
    analyser.fftSize = N;
    bufferLen = N / 2;
    //bufferLen = analyser.frequencyBinCount;
    console.log( 'bufferLen = ' + bufferLen );
    buffer = new Float32Array(bufferLen);

    if ( useMicrophone ) {
      // Create an AudioNode from the stream.
      mediaStreamSource = audioContext.createMediaStreamSource(stream);
      // Connect it to the destination.
      mediaStreamSource.connect(analyser);
    }
    else {
      // As a test, feed a C chord directly into the analyzer
      // C4, E4, G4
      var freqs = [261.63, 329.63, 392.00];
      for( var i=0; i < freqs.length; i++) {
        var o = audioContext.createOscillator();
        var g = audioContext.createGain(); //Create Gain Node
        o.frequency.value = freqs[i];
        o.connect(g);
        g.gain.value = 0.25;
        g.connect( audioContext.destination );
        g.connect( analyser );
        o.start(0);
        //setTimeout(function(s) {s.stop(0)}, 1000, o);
      }
    }

    // Call algorithm every 50 ms
    setInterval(function() {
      pcpAlg();
    }, 50);
  })
  .catch(function(err) {
    console.log(err.name + ": " + err.message);
  });

function pcpAlg() {
  analyser.getFloatTimeDomainData(buffer);
  //analyser.getFloatFrequencyData( buffer );
  // Reset PCP
  PCP = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  // M(0)=-1 so we don't have to start at 0
  for (var l = 1; l < bufferLen; l++) { // l = 0,1,...,[(N/2) - 1]
    // Calculate M(l)
    var ML = Math.round(12 * Math.log2( (fs * (l / N) ) / fref ) ) % 12; //
    //console.log( ML );
    if (ML >= 0 && ML <= 11) {
      PCP[ML] += Math.pow( Math.abs( buffer[l] ), 2 );
    }
  }

  // Display Data on UI and also try to determine if the sound is a C or F chord
  displayAndCategorize();
}

Here is my full codepen if you want to try running it yourself. Warning I have useMicrophone set to false so it will be making a c chord sound: https://codepen.io/mapmaps/pen/ONQPpw

Haffner answered 14/4, 2016 at 4:4 Comment(2)
Sounds like a cool project, but are you sure the input is correct ? Have you tried with fake hard-coded input values to see if the output is correct in that case ?Hilmahilt
Don't you want to get the float frequency data instead of the time domain data in buffer? I also assume you want to extract the appropriate bin from the fft result to accumulate into PCP. You just currently grab the first time value.Placia
B
2

The problem is with the algorithm from a 1999 paper. You seem to be using FFT for magnitude peaks, which is a coarse spectral frequency estimator, not a musical pitch detector/estimator. Polyphonic chord estimation is an even more difficult/complex task. Look here for research papers on the latest algorithms for polyphonic music extraction: http://www.music-ir.org/mirex/wiki/2015:MIREX2015_Results

Bronchitis answered 15/4, 2016 at 0:16 Comment(1)
I'm not sure if I have enough knowledge to mark this as correct. I chose to implement the algorithm in the 1999 paper because it seems like something I would be able to implement and it claims to be a chord detection algorithm. It was also referenced in these two papers: fim.uni-passau.de/fileadmin/files/lehrstuhl/sauer/geyer/… and ccrma.stanford.edu/~kglee/pubs/klee-ismir06.pdf Within the 1999 paper there is output of his algorithm detecting chords. Maybe he left something out of the paper?Haffner

© 2022 - 2024 — McMap. All rights reserved.