title: Decoding DTMF from a WAV file
body: <p>Following on from my <a href="https://stackoverflow.com/questions/33858663/how-to-use-the-dtmfrecognitionengine-class-in-microsoft-speech">earlier question</a>, my goal is to detect DTMF tones in a WAV file from C#. However, I'm really struggling to understand how this can be done.</p>
  
  <p>I understand the DTMF uses a combination of frequencies, and a Goertzel algorithm can be used ... somehow. I've grabbed a Goertzel code snippet and I've tried shoving a .WAV file into it (using NAudio to read the file, which is a 8KHz mono 16-bit PCM WAV):</p>
  
  <pre><code> using (WaveFileReader reader = new WaveFileReader(@"dtmftest_w.wav"))
    {
        byte[] buffer = new byte[reader.Length];
  
        int read = reader.Read(buffer, 0, buffer.Length);
        short[] sampleBuffer = new short[read/2];
        Buffer.BlockCopy(buffer, 0, sampleBuffer, 0, read/2);
        Console.WriteLine(CalculateGoertzel(sampleBuffer,8000,16));                 
     }
  
   public static double CalculateGoertzel(short[] sample, double frequency, int samplerate)
     {
        double Skn, Skn1, Skn2;
        Skn = Skn1 = Skn2 = 0;
        for (int i = 0; i &lt; sample.Length; i++)
           {
              Skn2 = Skn1;
              Skn1 = Skn;
              Skn = 2 * Math.Cos(2 * Math.PI * frequency / samplerate) * Skn1 - Skn2 + sample[i];
           }
        double WNk = Math.Exp(-2 * Math.PI * frequency / samplerate);
        return 20 * Math.Log10(Math.Abs((Skn - WNk * Skn1)));
      }
  </code></pre>
  
  <p>I know what I'm doing is wrong: I assume that I should iterate through the buffer, and only calculate the Goertzel value for a small chunk at a time - is this correct?</p>
  
  <p>Secondly, I don't really understand what the output of the Goertzel method is telling me: I get a double (example: <code>210.985812</code>) returned, but I don't know to equate that to the presence and value of a DTMF tone in the audio file.</p>
  
  <p>I've searched everywhere for an answer, including the libraries referenced in <a href="https://stackoverflow.com/questions/4808893/detect-a-specific-frequency-tone-from-raw-wave-data">this</a> answer; unfortunately, the code <a href="http://sourceforge.net/projects/dtmf-cs/" rel="nofollow noreferrer">here</a> doesn't appear to work (as noted in the comments on the site). There is a commercial library offered by <a href="http://www.tapiex.com/" rel="nofollow noreferrer">TAPIEx</a>; I've tried their evaluation library and it does exactly what I need - but they're not responding to emails, which makes me wary about actually purchasing their product.</p>
  
  <p>I'm very conscious that I'm looking for an answer when perhaps I don't know the exact question, but ultimately all I need is a way to find DTMF tones in a .WAV file. Am I on the right lines, and if not, can anyone point me in the right direction?</p>
  
  <p>EDIT: Using @Abbondanza 's code as a basis, and on the (probably fundamentally wrong) assumption that I need to drip-feed small sections of the audio file in, I now have this (very rough, proof-of-concept only) code:</p>
  
  <pre><code>const short sampleSize = 160;
  
  using (WaveFileReader reader = new WaveFileReader(@"\\mac\home\dtmftest.wav"))
          {           
              byte[] buffer = new byte[reader.Length];
  
              reader.Read(buffer, 0, buffer.Length);
  
              int bufferPos = 0;
  
              while (bufferPos &lt; buffer.Length-(sampleSize*2))
              {
                  short[] sampleBuffer = new short[sampleSize];
                  Buffer.BlockCopy(buffer, bufferPos, sampleBuffer, 0, sampleSize*2);
  
  
                  var frequencies = new[] {697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0};
  
                  var powers = frequencies.Select(f =&gt; new
                  {
                      Frequency = f,
                     Power = CalculateGoertzel(sampleBuffer, f, 8000)              
                  });
  
                  const double AdjustmentFactor = 1.05;
                  var adjustedMeanPower = AdjustmentFactor*powers.Average(result =&gt; result.Power);
  
                  var sortedPowers = powers.OrderByDescending(result =&gt; result.Power);
                  var highestPowers = sortedPowers.Take(2).ToList();
  
                  float seconds = bufferPos / (float)16000;
  
                  if (highestPowers.All(result =&gt; result.Power &gt; adjustedMeanPower))
                  {
                      // Use highestPowers[0].Frequency and highestPowers[1].Frequency to 
                      // classify the detected DTMF tone.
  
                      switch (Convert.ToInt32(highestPowers[0].Frequency))
                      {
                          case 1209:
                              switch (Convert.ToInt32(highestPowers[1].Frequency))
                              {
                                  case 697:
                                      Console.WriteLine("1 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 770:
                                      Console.WriteLine("4 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 852:
                                      Console.WriteLine("7 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 941:
                                      Console.WriteLine("* pressed at " + bufferPos);
                                      break;
                              }
                              break;
                          case 1336:
                              switch (Convert.ToInt32(highestPowers[1].Frequency))
                              {
                                  case 697:
                                      Console.WriteLine("2 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 770:
                                      Console.WriteLine("5 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 852:
                                      Console.WriteLine("8 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 941:
                                      Console.WriteLine("0 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                              }
                              break;
                          case 1477:
                              switch (Convert.ToInt32(highestPowers[1].Frequency))
                              {
                                  case 697:
                                      Console.WriteLine("3 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 770:
                                      Console.WriteLine("6 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 852:
                                      Console.WriteLine("9 pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                                  case 941:
                                      Console.WriteLine("# pressed at " + bufferPos + " (" + seconds + "s)");
                                      break;
                              }
                              break;
                      }
                  }
                  else
                  {
                      Console.WriteLine("No DTMF at " + bufferPos + " (" + seconds + "s)");
                  }
                  bufferPos = bufferPos + (sampleSize*2);
              }
  </code></pre>
  
  <p>This is the sample file as viewed in Audacity; I've added in the DTMF keypresses that were pressed-</p>
  
  <p><a href="https://i.stack.imgur.com/xGPEn.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/xGPEn.png" alt="enter image description here"></a></p>
  
  <p>and ... it <em>almost</em> works. From the file above, I shouldn't see any DTMF until almost exactly 3 seconds in, however, my code reports:</p>
  
  <pre><code>9 pressed at 1920 (0.12s)
  1 pressed at 2880 (0.18s)
  * pressed at 3200
  1 pressed at 5120 (0.32s)
  1 pressed at 5440 (0.34s)
  7 pressed at 5760 (0.36s)
  7 pressed at 6080 (0.38s)
  7 pressed at 6720 (0.42s)
  5 pressed at 7040 (0.44s)
  7 pressed at 7360 (0.46s)
  7 pressed at 7680 (0.48s)
  1 pressed at 8000 (0.5s)
  7 pressed at 8320 (0.52s)
  </code></pre>
  
  <p>... until it gets to 3 seconds, and THEN it starts to settle down to the correct answer: that <code>1</code> was pressed:</p>
  
  <pre><code>7 pressed at 40000 (2.5s)
  # pressed at 43840 (2.74s)
  No DTMF at 44800 (2.8s)
  1 pressed at 45120 (2.82s)
  1 pressed at 45440 (2.84s)
  1 pressed at 46080 (2.88s)
  1 pressed at 46720 (2.92s)
  4 pressed at 47040 (2.94s)
  1 pressed at 47360 (2.96s)
  1 pressed at 47680 (2.98s)
  1 pressed at 48000 (3s)
  1 pressed at 48960 (3.06s)
  4 pressed at 49600 (3.1s)
  1 pressed at 49920 (3.12s)
  1 pressed at 50560 (3.16s)
  1 pressed at 51520 (3.22s)
  1 pressed at 52160 (3.26s)
  4 pressed at 52480 (3.28s)
  </code></pre>
  
  <p>If I bump up the <code>AdjustmentFactor</code> beyond 1.2, I get very little detection at all.</p>
  
  <p>I sense that I'm almost there, but can anyone see what it is I'm missing?</p>
  
  <p>EDIT2: The test file above is available <a href="https://dl.dropboxusercontent.com/u/86925436/dtmftest.wav" rel="nofollow noreferrer">here</a>. The <code>adjustedMeanPower</code> in the example above is <code>47.6660450354638</code>, and the powers are:</p>
  
  <p><a href="https://i.stack.imgur.com/Ocds5.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Ocds5.png" alt="enter image description here"></a></p>