Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

C# implementation of the Goertzel algorithm for DTMF tone (a.k.a. Touch-Tone) detection and localization in audio data. Includes wrappers and extensions for NAudio.

License

NotificationsYou must be signed in to change notification settings

bert2/DtmfDetection

Repository files navigation

buildtestscoveragenuget packagenuget downloadslast commit

Implementation of theGoertzel algorithm for the detection ofDTMF tones (aka touch tones) in audio data.

packageuse case
DtmfDetectionUse this package if you are only working with rawPCM data (i.e. arrays offloats).
DtmfDetection.NAudioIntegrates withNAudio to detect DTMF tones in audio files and audio streams (e.g. mic-in or the current audio output).

Quick start

In case DtmfDetection is not detecting any or only some of the DTMF tones in your audio data, have look at thetroubleshooting section first.

With NAudio

How to detect and print DTMF changes (DTMF tone starting or stopping) in anmp3 file:

usingSystem;usingDtmfDetection.NAudio;usingNAudio.Wave;classProgram{staticvoidMain(){usingvaraudioFile=newAudioFileReader("long_dtmf_tones.mp3");vardtmfs=audioFile.DtmfChanges();foreach(vardtmfindtmfs)Console.WriteLine(dtmf);}}
Output

1 started @ 00:00:02.7675736 (ch: 0)
1 stopped @ 00:00:05.5607029 (ch: 0)
2 started @ 00:00:06.7138321 (ch: 0)
2 stopped @ 00:00:06.8675736 (ch: 0)
3 started @ 00:00:07.3031972 (ch: 0)
3 stopped @ 00:00:07.4313378 (ch: 0)
4 started @ 00:00:08.2000680 (ch: 0)
4 stopped @ 00:00:10.5319501 (ch: 0)
5 started @ 00:00:12.0950793 (ch: 0)
5 stopped @ 00:00:12.2744444 (ch: 0)
6 started @ 00:00:12.7357142 (ch: 0)
6 stopped @ 00:00:12.8125850 (ch: 0)
7 started @ 00:00:14.5038321 (ch: 0)
7 stopped @ 00:00:14.5294557 (ch: 0)
7 started @ 00:00:14.5550793 (ch: 0)
7 stopped @ 00:00:16.8357142 (ch: 0)
8 started @ 00:00:17.6813378 (ch: 0)
8 stopped @ 00:00:17.7582086 (ch: 0)
9 started @ 00:00:18.4500680 (ch: 0)
9 stopped @ 00:00:18.5269614 (ch: 0)
# started @ 00:00:19.1163265 (ch: 0)
# stopped @ 00:00:19.1419501 (ch: 0)
# started @ 00:00:19.1675736 (ch: 0)
# stopped @ 00:00:19.3469614 (ch: 0)
0 started @ 00:00:19.8338321 (ch: 0)
0 stopped @ 00:00:19.8850793 (ch: 0)
* started @ 00:00:20.4744444 (ch: 0)
* stopped @ 00:00:20.6025850 (ch: 0)
1 started @ 00:00:22.0119501 (ch: 0)
1 stopped @ 00:00:23.7544444 (ch: 0)

How to detect and print multi-channel DTMF changes in awav file while also merging the start and stop of each DTMF tone into a single data structure:

usingSystem;usingDtmfDetection;usingDtmfDetection.NAudio;usingNAudio.Wave;classProgram{staticvoidMain(){usingvaraudioFile=newAudioFileReader("stereo_dtmf_tones.wav");vardtmfs=audioFile.DtmfChanges(forceMono:false).ToDtmfTones();foreach(vardtmfindtmfs)Console.WriteLine(dtmf);}}
Output

1 @ 00:00:00 (len: 00:00:00.9994557, ch: 0)
2 @ 00:00:01.9988208 (len: 00:00:00.9993878, ch: 1)
3 @ 00:00:03.9975736 (len: 00:00:01.9987529, ch: 0)
4 @ 00:00:04.9969614 (len: 00:00:01.9987528, ch: 1)
5 @ 00:00:07.9950793 (len: 00:00:00.9993651, ch: 0)
6 @ 00:00:07.9950793 (len: 00:00:00.9993651, ch: 1)
7 @ 00:00:09.9938321 (len: 00:00:02.9981180, ch: 0)
8 @ 00:00:11.0188208 (len: 00:00:00.9737642, ch: 1)
9 @ 00:00:14.0169614 (len: 00:00:00.9737415, ch: 0)
0 @ 00:00:15.0163265 (len: 00:00:00.9737415, ch: 0)

How to detect and print DTMF changes in audio output:

usingSystem;usingDtmfDetection.NAudio;usingNAudio.CoreAudioApi;usingNAudio.Wave;classProgram{staticvoidMain(){usingvaraudioSource=newWasapiLoopbackCapture{ShareMode=AudioClientShareMode.Shared};usingvaranalyzer=newBackgroundAnalyzer(audioSource);analyzer.OnDtmfDetected+= dtmf=>Console.WriteLine(dtmf);_=Console.ReadKey(intercept:true);}}

How to detect and print DTMF changes in microphone input while also lowering the detection threshold:

usingSystem;usingDtmfDetection;usingDtmfDetection.NAudio;usingNAudio.Wave;classProgram{staticvoidMain(){usingvaraudioSource=newWaveInEvent{WaveFormat=newWaveFormat(Config.Default.SampleRate,bits:32,channels:1)};usingvaranalyzer=newBackgroundAnalyzer(audioSource,Config.Default.WithThreshold(10));analyzer.OnDtmfDetected+= dtmf=>Console.WriteLine(dtmf);_=Console.ReadKey(intercept:true);}}

Without NAudio

How to detect and print DTMF tones in an array ofPCM samples:

usingSystem;usingSystem.Linq;usingDtmfDetection;usingstaticDtmfDetection.DtmfGenerator;classProgram{staticvoidMain(){varsamples=GenerateStereoSamples();foreach(vardtmfinsamples.DtmfChanges(channels:2))Console.WriteLine(dtmf);}// `DtmfDetection.DtmfGenerator` has helpers for generating DTMF tones.staticfloat[]GenerateStereoSamples()=>Stereo(left:Generate(PhoneKey.Star),right:Concat(Mark(PhoneKey.One,ms:40),Space(ms:20),Mark(PhoneKey.Two,ms:40))).Take(NumSamples(milliSeconds:40+20+40,channels:2)).ToArray();}
Output

* started @ 00:00:00 (ch: 0)
1 started @ 00:00:00 (ch: 1)
1 stopped @ 00:00:00.0000026 (ch: 1)
2 started @ 00:00:00.0000051 (ch: 1)
* stopped @ 00:00:00.0000100 (ch: 0)
2 stopped @ 00:00:00.0000100 (ch: 1)

Pre-built example tool

TODO: deploy example tool to choco

DTMF tone localization accuracy

Be aware that this library cannot locate DTMF tones with 100% accuracy, because the detector analyzes the data in ~26 ms blocks with the default configuration. This block size determines the resolution of the localization and every DTMF tone starting position will be "rounded off" to the start of the nearest block.

For instance, if a DTMF tone starts at 35 ms into the audio, its calculated starting position will be around 26 ms, i.e. at the beginning of the second block.

A resolution of 26 ms might seem rather inaccurate relative to the typical duration of a DTMF tone (40 ms). However, keep in mind that DTMF analysis typically is about correctlydetecting DTMF tones and not about accuratelylocating them.

Configuring the detector

The library is designed to be very configurable. Of course, each setting of the detector configuration can be changed. Additionally it is possible to replace any part of its logic with a custom implementation.

Adjusting the detection threshold

The detector's threshold value is probably the setting that needs to be tweaked most often. Depending on the audio source and quality, the threshold might have to be increased to reduce false positives or decreased to reduce false negatives.

Typical values are between30 and35 with enabledGoertzel response normalization and100 to115 without it. Its default value is30.

Changing the threshold value is easy, because each of the three main entry points take an optionalConfig argument (defaulting toConfig.Default):

  • List<DtmfChange> float[].DtmfChanges(int, int, Config?)
  • List<DtmfChange> WaveStream.DtmfChanges(bool, Config?)
  • BackgroundAnalyzer(IWaveIn, bool, Action<DtmfChange>?, Config?, IAnalyzer?)

Now, simply create your ownConfig instance and pass it to the entry point you want to use:

varmycfg=newConfig(threshold:20,sampleBlockSize: ..., ...);vardmtfs=waveStream.DtmfChanges(config:mycfg);

Or you start off with the default config and adjust it with one of its builder methods:

varmycfg=Config.Default.WithThreshold(20);

Disabling Goertzel response normalization

As of version 1.0.0 the frequency response calculated with Goertzel algorithm will be normalized with the total energy of the input signal. This effectively makes the detector invariant against changes in the loudness of the signal with very little additional computational costs.

You can test this yourself with a simple example program that detects DTMF tones in your system's current audio output, but with disabled response normalization:

usingSystem;usingDtmfDetection.NAudio;usingNAudio.CoreAudioApi;usingNAudio.Wave;classProgram{staticvoidMain(){usingvaraudioSource=newWasapiLoopbackCapture{ShareMode=AudioClientShareMode.Shared};usingvaranalyzer=newBackgroundAnalyzer(audioSource,config:Config.Default.WithNormalizeResponse(false));analyzer.OnDtmfDetected+= dtmf=>Console.WriteLine(dtmf);_=Console.ReadKey(intercept:true);}}

Now play any of thetest files and observe the program's output. If your playback volume is high enough, DTMF tones should be detected. Try to gradually decrease the volume and see that no more DTMF tones will be detected as soon as the Goertzel responses fall below the detection threshold. With enabled response normalization all DTMF tones should be detected regardless of the volume level.

I generally recommend to leave response normalization enabled, because loudness invariance ensures that DTMF tones are detected correctly in a wider range of scenarios. However, if you are analyzing audio signals that feature strong background noises, you might accomplish better detection results by disabling response normalization.

Just note that without response normalization the detection threshold has to be significantly increased and depends on the loudness of your signal. A good starting point is a value of100.

Providing a custom source of sample data

Different kinds of sample data are fed to the analysis in a unified way using theISamples interface. Currently there are three implementations ofISamples:

implementationpackageusage
AudioDataDtmfDetectioncreated from afloat[]
AudioFileDtmfDetection.NAudiocreated from an NAudioWaveStream
AudioStreamDtmfDetection.NAudiocreated from an NAudioIWaveIn

In case none of the above implementations suit your needs, you can implement the interface yourself and pass it directly to theAnalyzer:

// Untested `ISamples` implementation for `System.IO.Stream`s.publicclassMySamples:ISamples,IDisposable{privatereadonlyBinaryReaderreader;privatelongposition;publicintChannels=>1;publicintSampleRate=>8000;publicTimeSpanPosition=>newTimeSpan((long)Math.Round(position*1000.0/SampleRate));publicMySamples(Streamsamples)=>this.reader=newBinaryReader(samples);publicintRead(float[]buffer,intcount){varsafeCount=Math.Min(count,reader.BaseStream.Length/sizeof(float)-position);for(vari=0;i<safeCount;i++,position++)buffer[i]=reader.ReadSingle();return(int)safeCount;}publicvoidDispose()=>reader.Dispose();}// ...varmySamples=newMySamples(myStream);varanalyzer=Analyzer.Create(mySamples,Config.Default);vardtmfs=newList<DtmfChange>();while(analyzer.MoreSamplesAvailable)dtmfs.AddRange(analyzer.AnalyzeNextBlock());

Refer to theAPI reference of theISamples interface for more details on how to implement it correctly.

Injecting a custom detector implementation

TODO: document how to

Other configuration options

TODO: document other options

Troubleshooting

None or not all expected DTMF tones are detected

You should first try tolower the detection threshold. The default config uses a value of30 which might be too high for your audio data. In case you get false positives afterwards, try increasing the threshold again to find the "sweet spot". If possible try to de-noise the audio data.

You can also try todisable the Goertzel response normalization. Generally this is not recommended, but it might give better results in certain cases. Be aware that without normalization you need to ensure that the threshold is tuned to the loudness of your audio data. I.e. when your audio becomes louder, you should also increase the threshold and when it becomes quieter, you have to decrease it. You should also use a higher starting value for the threshold (around100).

API reference

DtmfDetection:./src/DtmfDetection/README.md

DtmfDetection.NAudio:./src/DtmfDetection.NAudio/README.md

Changelog

1.2.2

  • refactor wait time estimation of theBackgroundAnalyzer to a less handcrafted solution

1.2.1

  • BackgroundAnalyzer ctor now also takes a handler for theOnDtmfDetected event

1.2.0

  • add XML documentation
  • generate API reference

1.1.0

DtmfDetection:

  • add extension method for analyzingfloat arrays
  • add helpers for generating DTMF tones

1.0.1

DtmfDetection:

  • remove unwanted dependencies from nuget package

1.0.0

  • upgrade to netstandard2.1
  • make implementation much more configurable
  • improve runtime performance by ~25%

DtmfDetection:

  • normalize Goertzel response with total signal energy for loudness invariance

DtmfDetection.NAudio:

  • update NAudio reference to 1.10.0
  • correctly calculate wait time until enough samples can be read when analyzing audio provided by aNAudio.Wave.BufferedWaveProvider stream

0.9.2

DtmfDetection.NAudio:

  • update NAudio reference to 1.8.4.0

0.9.1

DtmfDetection:

  • update to .NET framework 4.7
  • reduce memory footprint a little bit

DtmfDetection.NAudio:

  • update to .NET framework 4.7

0.9.0

DtmfDetection:

  • implement multi-channel support
  • fix short DTMF tones not being detected
  • adjust Goertzel algorithm implementation a bit

DtmfDetection.NAudio:

  • fix mono-conversion (average all channels)

TODO

  • finish README
  • continuous deployment of CLI tool to choco
  • add config options to CLI tool

About

C# implementation of the Goertzel algorithm for DTMF tone (a.k.a. Touch-Tone) detection and localization in audio data. Includes wrappers and extensions for NAudio.

Topics

Resources

License

Stars

Watchers

Forks


[8]ページ先頭

©2009-2025 Movatter.jp