2015-12-04 124 views
6

私の目標は、earlier questionから、C#のWAVファイルのDTMFトーンを検出することです。しかし、私はこれがどうやってできるのか理解するのは本当に苦労しています。WAVファイルからDTMFをデコードする

私はDTMFが周波数の組み合わせを使用し、Goertzelアルゴリズムを使用できると理解しています...何とかして。私はゲルツェルコードスニペットをつかんでいると私は(8kHzのモノラル16ビットPCM WAVのファイルを、読み取るためにNAudioを使用して)それに.WAVファイルを無理に勧めてみた:

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

私が何を知っています私はやっている間違っている:私はバッファを介して反復し、一度に小さなチャンクのGoertzelの値を計算する必要があると仮定 - これは正しいですか?

第2に、私はGoertzelメソッドの出力が私に言っていることを本当に理解していません。ダブル(例:210.985812)が返されますが、私はそれを存在の値とオーディオファイルのDTMFトーン。

私は答えを探すためにどこでも検索しました。thisで参照されているライブラリを含みます。残念ながら、コードhereは(サイトのコメントに記載されているように)動作していないようです。 TAPIExが提供する商用ライブラリがあります。私は評価ライブラリを試してみましたが、必要なものを正確に実行しますが、電子メールには応答しません。実際に製品を購入することには注意が必要です。

おそらく私は正確な質問がわからないときに答えを探していますが、最終的には.WAVファイルでDTMFトーンを見つける方法が必要です。私は正しい行にいますか?もしそうでなければ、誰かが正しい方向に私を向けることができますか?

EDIT:@Abbondanzaのコードをベースにして、オーディオファイルの小さな部分をドリンプフィードする必要がある(おそらく根本的に間違っている)という仮定では、 of-conceptのみ)コード:

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 < 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 => new 
       { 
        Frequency = f, 
        Power = CalculateGoertzel(sampleBuffer, f, 8000)    
       }); 

       const double AdjustmentFactor = 1.05; 
       var adjustedMeanPower = AdjustmentFactor*powers.Average(result => result.Power); 

       var sortedPowers = powers.OrderByDescending(result => result.Power); 
       var highestPowers = sortedPowers.Take(2).ToList(); 

       float seconds = bufferPos/(float)16000; 

       if (highestPowers.All(result => result.Power > 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); 
      } 

これはAudacityで表示されるサンプルファイルです。私は

enter image description here

と...それほとんど作品pressed-たDTMFのキー押下に追加しました。それは3秒になり、その後、それが落ち着くし始めるまで...

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) 

:上記のファイルから、私はしかし、私のコードのレポート、でほぼ正確に3秒まで任意のDTMFを見るべきではありません正解:1が押されたことを:

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) 

を私は1.2を超えてAdjustmentFactorをつり上げる場合、私はすべてではほとんど検出を取得します。

私はほとんどそこにいると感じますが、誰にも分かりませんが、私は行方不明です。

EDIT2:上記のテストファイルはhereです。上記の例では、adjustedMeanPower47.6660450354638であり、電力は次のとおり

enter image description here

+0

DTMFファイルは少なくとも40msの長さで、少なくとも40msのスペースが必要です。 http://www.genave.com/dtmf-mark-space.htm –

+0

を参照してください。また、検出する必要がある周波数は、http://www.genaveに従って697Hz、770Hz、852Hz、941Hz、1209Hz、1336Hzおよび1477Hzです。 com/dtmf.htm –

+0

答えにコードスニペットを追加しました。あなたの問題について進歩を遂げるのに役立ったかどうか教えてください。 –

答えて

6

提供されたサンプル内の選択された周波数の電力を返しCalculateGoertzel()

DTMF周波数(697,770,852,941,1209,1336、および1477 Hz)ごとにこの電力を計算し、結果の電力をソートして最高の2つを選びます。両方が特定のしきい値を超えると、DTMFトーンが検出されています。

しきい値として使用するものは、サンプルの信号対雑音比(SNR)によって異なります。最初に、すべてのGoerzel値の平均を計算し、平均値に係数(2または3など)を掛けて、2つの最高Goerzel値がその値を上回っているかどうかを確認するだけで十分です。 1.0AdjustmentFactor

var frequencies = new[] {697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0}; 

var powers = frequencies.Select(f => new 
{ 
    Frequency = f, 
    Power = CalculateGoerzel(sample, f, samplerate) 
}); 

const double AdjustmentFactor = 1.0; 
var adjustedMeanPower = AdjustmentFactor * powers.Average(result => result.Power); 

var sortedPowers = powers.OrderByDescending(result => result.Power); 
var highestPowers = sortedPowers.Take(2).ToList(); 

if (highestPowers.All(result => result.Power > adjustedMeanPower)) 
{ 
    // Use highestPowers[0].Frequency and highestPowers[1].Frequency to 
    // classify the detected DTMF tone. 
} 

スタート:ここ

は、私はより正式な方法で何を意味するかを表現するコードスニペットです。テストデータから偽陽性(DTMFトーンがないはずのサンプルでDTMFトーンを検出する)の場合は、偽陽性が停止するまでその数を増やしてください。

私は(パフォーマンスのための重要な)ゲーツェル計算後に列挙をマテリアライズド:

私はWAVEファイルにあなたのコードを試してみましたが、いくつかのことを調整


更新#1

var powers = frequencies.Select(f => new 
{ 
    Frequency = f, 
    Power = CalculateGoertzel(sampleBuffer, f, 8000) 
// Materialize enumerable to avoid multiple calculations. 
}).ToList(); 

私は閾値処理の調整平均を使用しませんでした。私はちょうど閾値として100.0を使用:

if (highestPowers.All(result => result.Power > 100.0)) 
{ 
    ... 
} 

私はサンプルサイズ(私はあなたが160を使用信じる)倍増:私はあなたのDTMFの分類を固定

int sampleSize = 160 * 2; 

を。

var phoneKeyOf = new Dictionary<int, Dictionary<int, string>> 
{ 
    {1209, new Dictionary<int, string> {{1477, "?"}, {1336, "?"}, {1209, "?"}, {941, "*"}, {852, "7"}, {770, "4"}, {697, "1"}}}, 
    {1336, new Dictionary<int, string> {{1477, "?"}, {1336, "?"}, {1209, "?"}, {941, "0"}, {852, "8"}, {770, "5"}, {697, "2"}}}, 
    {1477, new Dictionary<int, string> {{1477, "?"}, {1336, "?"}, {1209, "?"}, {941, "#"}, {852, "9"}, {770, "6"}, {697, "3"}}}, 
    { 941, new Dictionary<int, string> {{1477, "#"}, {1336, "0"}, {1209, "*"}, {941, "?"}, {852, "?"}, {770, "?"}, {697, "?"}}}, 
    { 852, new Dictionary<int, string> {{1477, "9"}, {1336, "8"}, {1209, "7"}, {941, "?"}, {852, "?"}, {770, "?"}, {697, "?"}}}, 
    { 770, new Dictionary<int, string> {{1477, "6"}, {1336, "5"}, {1209, "4"}, {941, "?"}, {852, "?"}, {770, "?"}, {697, "?"}}}, 
    { 697, new Dictionary<int, string> {{1477, "3"}, {1336, "2"}, {1209, "1"}, {941, "?"}, {852, "?"}, {770, "?"}, {697, "?"}}} 
} 

電話のキーは、その後で取得されています:

var key = phoneKeyOf[(int)highestPowers[0].Frequency][(int)highestPowers[1].Frequency]; 

結果は完璧ではないですが、やや信頼性の高い、私はすべて可能なケースをキャプチャするために、ネストされた辞書を使用していました。


アップデート#2

私はこの問題を考え出したと思うが、今の自分自身をそれを試してみることができません。目標周波数を直接CalculateGoertzel()に渡すことはできません。これはDFTビンの中央に正規化する必要があります。計算力は、このアプローチをしようとすると:

var powers = frequencies.Select(f => new 
{ 
    Frequency = f, 
    // Pass normalized frequenzy 
    Power = CalculateGoertzel(sampleBuffer, Math.Round(f*sampleSize/8000.0), 8000) 
}).ToList(); 

はまた、あなたは順序エラーを最小限にsampleSizeとして205を使用する必要があります。


更新#3

正規化されたサンプル値(範囲内float S [-1.0; 1.0])を返しNAudioのISampleProviderインターフェイスを使用する私は再書いプロトタイプ。また、私はCalculateGoertzel()を最初から書き直しました。それでもパフォーマンスは最適化されていませんが、周波数間ではるかに顕著な電力差があります。私がテストデータを実行すると、にはがあります。私は非常にあなたがそれを見てお勧めします。http://pastebin.com/serxw5nG


更新#4

私はライブ(キャプチャ)音声と録音済みのオーディオファイルにDTMFトーンを検出するGitHub projecttwo NuGet packagesを作成しました。

+1

更新#2は私のために働かなかった、私は恐れている;テストファイルから結果が得られませんでした。しかし、更新#1のコードは(ほぼ)完璧に機能しますが、私の目的には十分です。あなたの助けをありがとう、私はあなたなしでそれを理解していないでしょう! – KenD

+0

アップデート#3は完璧に機能します、もう一度ありがとうございます! – KenD

+0

サンプリング周波数が48000の場合は動作しません。 – moose

関連する問題