peterkhayes / pitchfinder

A compilation of pitch detection algorithms for Javascript.
433 stars 53 forks source link

Half step down while using AMDF #40

Open evilcudmovement opened 3 years ago

evilcudmovement commented 3 years ago

I am using the code described in the README to input a stream of WebAudio Buffer from a stream like this:

navigator.mediaDevices.getUserMedia({ audio: true, video: false })
      .then(
        function (stream) {
          var audioContext = new (window.AudioContext || window.webkitAudioContext)();

          var audioStream = audioContext.createMediaStreamSource(stream) ;

          const processor = audioContext.createScriptProcessor(1024, 1, 1);

          audioStream.connect(processor);
          processor.connect(audioContext.destination);

          processor.onaudioprocess = function(e) {

              const float32Array = e.inputBuffer.getChannelData(0);

              const pitchFrequency = detectPitch(float32Array);

              console.log('Pitch is: ', getNoteFromPitch(pitchFrequency)) ;
         };
     }
 );

The function to convert pitch to note is something I found online and to be candid I don't understand the logic (I do get the math) and I do get the octave system and the standard guitar turning being in A440 Hz

function getNoteFromPitch(pitchFrequency) {

  const keys = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];

  if (pitchFrequency === null) {
    return null
  }

  // Convert the frequency to a musical pitch.

  // c = 440.0(2^-4.75)
  const c0 = 440.0 * Math.pow(2.0, -4.75)
  //console.log(c0);

  // h = round(12log2(f / c))
  const halfStepsBelowMiddleC = Math.round(12.0 * Math.log2(pitchFrequency  / c0))

  //console.log(halfStepsBelowMiddleC) ;

  // o = floor(h / 12)
  const octave = Math.floor(halfStepsBelowMiddleC / 12.0)
  const key = keys[Math.floor(halfStepsBelowMiddleC % 12)]

  return {pitchFrequency , key, octave}

}

The console shows the note half step down from what it is. I have cross checked by playing a note from virutal, real instruments as well as another app on Android.

I tried using YIN but that was showing all sorts of wild results, even in silence it was coming out to be arbitrary notes in Octave 10.

jodymgustafson commented 3 years ago

I'm getting a frequency of 404Hz for an A4, both on my guitar and even when playing your test note 440_square. Using the same method as the original post.

peterkhayes commented 3 years ago

I have a guess here. Digital audio always has a sampling rate. This refers to the number of times per second that the amplitude of the sound wave is recorded.

The most common sampling rate for digital audio is 44.1khz (44,100 samples per second). However, the second most common rate is 48khz.

If you take audio recorded at 48khz, and read it with a program that is assuming it is 44.1khz, then you'll be playing it back at 91.825% speed. Slowed-down audio has a lower pitch, at the same ratio it was slowed down. Doing the math here, 0.91825 * 440 is 404.25. That would explain your discrepancy perfectly.

peterkhayes commented 3 years ago

As a solution, try initializing your detector with a sampleRate parameter:

const detectPitch = Pitchfinder.AMDF({sampleRate: 48000});
jodymgustafson commented 3 years ago

Genius! You were exactly right. Now it measures frequencies perfectly. Don't know why I have it set to 48k, but checked my settings and there it was. Thank you!

To anyone using Web Audio you can get the sample rate from the context: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/sampleRate

peterkhayes commented 3 years ago

It might be useful to update the examples to use that code to set the sampling rate...