5

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

Community
  • 1
  • 1
user3220901
  • 105
  • 1
  • 7
  • 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 ? – Christophe Roussy Apr 14 '16 at 09:02
  • 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. – Raymond Toy Apr 14 '16 at 16:18

1 Answers1

1

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

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • 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: http://www.fim.uni-passau.de/fileadmin/files/lehrstuhl/sauer/geyer/BA_MA_Arbeiten/BA-HausnerChristoph-201409.pdf and https://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? – user3220901 Apr 15 '16 at 01:25