Im Anschluss an meine earlier question ist mein Ziel, DTMF-Töne in einer WAV-Datei von C# zu erkennen. Ich habe jedoch wirklich Schwierigkeiten zu verstehen, wie das gemacht werden kann.Decodieren von DTMF aus einer WAV-Datei
Ich verstehe die DTMF verwendet eine Kombination von Frequenzen, und ein Goertzel-Algorithmus kann verwendet werden ... irgendwie. Ich habe einen Goertzel Code-Schnipsel gepackt und ich habe versucht, in eine WAV-Datei schiebend (mit NAudio die Datei zu lesen, die ein 8 kHz Mono 16-Bit-PCM WAV ist):
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)));
}
Ich weiß, was Ich mache es falsch: Ich gehe davon aus, dass ich den Puffer durchlaufen und nur den Goertzel-Wert für einen kleinen Block berechnen soll - ist das korrekt?
Zweitens verstehe ich nicht wirklich, was die Ausgabe der Goertzel Methode sagt mir: Ich habe ein Doppel (Beispiel: 210.985812
) zurückgegeben, aber wenn ich weiß nicht, dass von einem auf das Vorhandensein und den Wert zu DTMF-Ton in der Audiodatei.
Ich habe überall nach einer Antwort gesucht, einschließlich der Bibliotheken, auf die in this Antwort verwiesen wird; Leider scheint der Code here nicht zu funktionieren (wie in den Kommentaren auf der Website erwähnt). Es gibt eine kommerzielle Bibliothek, die von TAPIEx angeboten wird; Ich habe ihre Auswertungsbibliothek ausprobiert und sie tut genau das, was ich brauche - aber sie reagieren nicht auf E-Mails, was mich davor skeptisch macht, ihr Produkt tatsächlich zu kaufen.
Ich bin mir sehr bewusst, dass ich nach einer Antwort suche, wenn ich vielleicht nicht die genaue Frage kenne, aber letztendlich brauche ich nur eine Möglichkeit, DTMF-Töne in einer .WAV-Datei zu finden. Bin ich auf der richtigen Linie, und wenn nicht, kann mir jemand in die richtige Richtung zeigen?
EDIT: Mit @Abbondanza 's Code als Grundlage, und auf der (wahrscheinlich grundsätzlich falschen) Annahme, dass ich kleine Teile der Audiodatei eintropfen lassen muss, habe ich jetzt diese (sehr grobe, (nur of-concept) 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 < 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);
}
Dies ist die Beispieldatei wie in Audacity angezeigt; Ich habe in den DTMF-Tastenfolgen hinzugefügt, die gedrückt- wurden
und ... es fast funktioniert. Aus der Datei oben soll ich bis fast genau 3 Sekunden sieht jede DTMF nicht in, aber mein Code Berichte:
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)
... bis sie auf 3 Sekunden bekommen, und dann beginnt es sesshaft die richtige Antwort: dass 1
gedrückt wurde:
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)
Wenn ich die AdjustmentFactor
über 1,2, stoßen erhalte ich sehr wenig Erkennung überhaupt.
Ich fühle, dass ich fast da bin, aber kann jemand sehen, was ich vermisse?
EDIT2: Die obige Testdatei ist verfügbar here.Die adjustedMeanPower
in dem obigen Beispiel ist 47.6660450354638
, und die Kräfte sind:
Der DTMF-Band sollte mindestens 40ms lang sein und mindestens 40ms Platz haben. Siehe http://www.genave.com/dtmf-mark-space.htm –
Auch die Frequenzen, die Sie erkennen müssen, sind 697Hz, 770Hz, 852Hz, 941Hz, 1209Hz, 1336Hz und 1477Hz gemäß http: //www.genave. com/dtmf.htm –
Ich habe ein Code-Snippet zu meiner Antwort hinzugefügt. Lassen Sie mich wissen, ob es Ihnen geholfen hat, bei Ihrem Problem Fortschritte zu erzielen. –