Page 1 of 1

DTMF detection

Posted: Sat Oct 10, 2009 4:11 am
by idle
hacked together from various sources, it's rough but works
needs to set a timer so it results like 1,2 instead of 111111,222222

set recording to waveout mix

you can test the tones with this java applet
;http://www.dsptutor.freeuk.com/dtmf/ToneGenerator.html

Code: Select all

;you can use this java app to test tones
;http://www.dsptutor.freeuk.com/dtmf/ToneGenerator.html

Global Dim rex.f(512*2+1)
Global Dim imx.f(512*2+1)
Global Dim FFTOUT.f(2,512*2+1)
Global gtone 
Global FFTMUT = CreateMutex()
Global FFTWnd, gdraw, gmaxvalue

Structure CONFIG
 
  hWindow.l            
  size.l               
  buffer.l             
  output.l             
  wave.l               
  format.WAVEFORMATEX  
  lBuf.l               
  nBuf.l               
  nDev.l               
  nBit.l               
  nHertz.l             
  nChannel.l           

EndStructure

Global Config.CONFIG
Global Dim inHdr.WAVEHDR(16)
Global maxvalue,maxvalue2 ,AKnote, gphase, stone.s
Config\format\wFormatTag = #WAVE_FORMAT_PCM

Declare.s gettone(a,b)
 
Procedure Record_Start()

  Config\format\nChannels = 1

  Config\format\wBitsPerSample  = 16
  Config\format\nSamplesPerSec  = 11025
  Config\nDev                   = 0  
  Config\lBuf                   = 1024
  Config\nBuf                   = 8
  Config\nBit                   = 1
 
  Config\format\nBlockAlign     = (Config\format\nChannels*Config\format\wBitsPerSample)/8
  Config\format\nAvgBytesPerSec = Config\format\nSamplesPerSec*Config\format\nBlockAlign
 
  If #MMSYSERR_NOERROR = waveInOpen_(@Config\wave,#WAVE_MAPPER+Config\nDev,@Config\format,Config\hWindow,#Null,#CALLBACK_WINDOW|#WAVE_FORMAT_DIRECT)
   
    For i=0 To Config\nBuf-1
      inHdr(i)\lpData=AllocateMemory(Config\lBuf)
      inHdr(i)\dwBufferLength=Config\lBuf
      waveInPrepareHeader_(Config\wave,inHdr(i),SizeOf(WAVEHDR))
      waveInAddBuffer_(Config\wave,inHdr(i),SizeOf(WAVEHDR))
    Next
   
    If #MMSYSERR_NOERROR = waveInStart_(Config\wave)
      SetTimer_(Config\hWindow,0,1,0)
    EndIf
   
  EndIf
 
EndProcedure

Procedure Record_Read(hWaveIn.l,lpWaveHdr.l)
  *hWave.WAVEHDR=lpWaveHdr
  Config\buffer=*hWave\lpData
  Config\size=*hWave\dwBytesRecorded
  waveInAddBuffer_(hWaveIn,lpWaveHdr,SizeOf(WAVEHDR))
EndProcedure 


Procedure record_doFFT()

  Protected N.w,M.w,NM1.i,J.i,ND2.i,MM.i
  Static lt,ltone.s 
  If Config\buffer = 0 : ProcedureReturn : EndIf
     
  For pos=0 To 1024:rex(pos)=0:imx(pos)=0:Next
  pos = 0
  For i=0 To Config\size Step 2
    value.w=PeekW(Config\buffer+i)
    rex(pos) = value/32767
    imx(pos) = 0  :
    pos + 1
  Next

  N.w = 1024     
  NM1 = N - 1
  ND2 = N / 2
  MM = Int(Log(N) / 0.69314718055994529)
  J = ND2

  For ii = 1 To N - 2  
      If ii < J
          TR.f = REX(J)
          TI.f = IMX(J)
          REX(J) = REX(ii)
          IMX(J) = IMX(ii)
          REX(ii) = TR
          IMX(ii) = TI
      EndIf
      K = ND2
      While K <= J
          J = J - K
          K = K / 2
      Wend
      J = J + K
  Next 

  For L = 1 To MM  
      LE.l = Int(Pow(2, L))
       
      LE2.l = LE >> 1
      UR.f = 1
      UI.f = 0
      SR.f = Cos(#PI / LE2)  
      SI.f = - Sin(#PI / LE2)
      For J = 1 To LE2  
          JM1.l = J - 1
          For i = JM1 To NM1  
              IP.l = i + LE2
              TR = REX(IP) * UR - IMX(IP) * UI  
              TI = REX(IP) * UI + IMX(IP) * UR
              REX(IP) = REX(i) - TR
              IMX(IP) = IMX(i) - TI
              REX(i) = REX(i) + TR
              IMX(i) = IMX(i) + TI
              i + LE - 1
          Next i
          TR = UR
          UR = TR * SR - UI * SI
          UI = TR * SI + UI * SR
      Next J
  Next L
   
   
   LockMutex(FFTMUT)
   maxvalue = 1
   maxvalue2 = 1
   
   For cnt=1 To 512
     ;calculate the power and phase 
     FFTOUT(0,cnt) = (IMX(cnt) * IMX(cnt)) + (REX(cnt) * REX(cnt)) 
     FFTOUT(1,cnt) = ATan(IMX(cnt)/REX(cnt))
     ;get max frequency  
     If FFTOUT(0,cnt) > maxvalue  
        maxvalue = FFTOUT(0,cnt)
        If maxvalue > gmaxvalue
          gmaxvalue = maxvalue 
        EndIf    
        AkNote=cnt
        fq = (((Config\format\nSamplesPerSec / 512.0) * (cnt)) + (Config\format\nSamplesPerSec / 512.0)) * 0.5 
        gphase = FFTOUT(1,cnt)
     EndIf
     
               
   Next 
   ;loop through again to find the second frequency 
      
   For cnt = 1 To 512 
      ;make sure it's not leakage from the main peak and within a reasonable range   
      If Abs(aknote - cnt) > 10 And  Abs(aknote - cnt) < 90    
        If FFTOUT(0,cnt) > maxvalue2 
          maxvalue2 = FFTOUT(0,cnt)
          fq1 = (((Config\format\nSamplesPerSec / 512.0) * (cnt)) + (Config\format\nSamplesPerSec / 512.0)) * 0.5 
        EndIf
     EndIf  
   Next 
    
   If fq > 0  
       ;apply a theshold check should use DB scale but for quick n dirty we can do it on the scope window height
       sfy.f = (1.0-200.0) / (1.0-gmaxvalue)  
       stone = gettone(fq,fq1) 
       
       If maxvalue * sfy > 100 And maxvalue2 * sfy > 100
         
         If stone <> ltone And stone <> ""
            gtone = #True 
         ElseIf stone = ltone And stone <> "" 
           If ElapsedMilliseconds() > lt 
             gtone = #True 
           EndIf   
         EndIf 
         ltone = stone
         lt= ElapsedMilliseconds()+45
      EndIf  
   EndIf 
   UnlockMutex(FFTMUT)
     
 Delay(0)
   
EndProcedure

Procedure.s gettone(a,b)
   Protected snb.s 
   Static lt, sna.s
   If a > b 
     t=a 
     a=b 
     b=t
   EndIf 
      
   
   If a > 662 And a < 731  ;697
     If b > 1148 And b < 1268 ;1209
       nb=1
       snb="1" 
     ElseIf b > 1269 And b < 1402 ;1336
       nb=2
       snb="2"  
     ElseIf b > 1403 And b < 1550 ;1477
       nb=3
       snb="3" 
     ElseIf b > 1551 And b < 1715 ;1633
       nb=13 
       snb="A"
     EndIf            
   ElseIf a > 731 And a < 808  ;770
     If b > 1148 And b < 1268 
       nb=4
       snb="4" 
     ElseIf b > 1269 And b < 1402
       nb=5
       snb="5"  
     ElseIf b > 1403 And b < 1550 
       nb=6
       snb="6" 
     ElseIf b > 1551 And b < 1715 ;1633
       nb=14 
       snb="B"
     EndIf    
   ElseIf a > 809 And a < 892 ;852
    If b > 1148 And b < 1268 
       nb=7
       snb="7" 
     ElseIf b > 1269 And b < 1402
       nb=8
       snb="8"  
     ElseIf b > 1403 And b < 1550 
       nb=9
       snb="9" 
     ElseIf b > 1551 And b < 1715 ;1633
       nb=15 
       snb="C"
     EndIf     
   ElseIf a > 893 And a < 988  ;941
      If b > 1148 And b < 1268 
       nb=10
       snb="*" 
     ElseIf b > 1269 And b < 1402
       nb=11
       snb="0"  
     ElseIf b > 1403 And b < 1550 
       nb=12
       snb="#" 
     ElseIf b > 1551 And b < 1715 ;1633
       nb=16 
       snb="D"
     EndIf    
   EndIf 
   
    
          
   ProcedureReturn snb 
   
EndProcedure   

Procedure record_CallBack(hWnd.l,Msg.l,wParam.l,lParam.l)
 
  Result.l=#PB_ProcessPureBasicEvents
 
  Select Msg
    Case #WM_TIMER    : record_doFFT()
    Case #MM_WIM_DATA : record_Read(wParam,lParam)
  EndSelect
 
  ProcedureReturn Result
 
EndProcedure

Procedure draw(win)
hwnd = WindowID(win)
gdraw=1 
Static lt 
Static blt 
mytones.s
ltone.s
StartDrawing(WindowOutput(win))
 
SetWindowColor(win,0)
While gdraw 
  
  LockMutex(FFTMUT)
    
  sfy.f = (1.0-200.0) / (1.0-gmaxvalue)  
  
  For a = 1 To 512 
    ht = fftout(0,a) * sfy
    LineXY(a,200,a,200-ht,RGB(255,255,255))
  Next 
  gmaxvalue = 1
  If gtone  
    blt = ElapsedMilliseconds() + 250
    mytones + stone 
    gtone = #False 
  EndIf 
  If ElapsedMilliseconds() > blt 
    mytones = "" 
  EndIf   
  DrawText(20,20,mytones,RGB(0,255,0),0) 
  
  UnlockMutex(fftmut)
  
  Delay(20)
  rc.rect 
  GetWindowRect_(hwnd,@rc)
  InvalidateRect_(hwnd,rc,1)
   
  
Wend 

StopDrawing()

EndProcedure 

;set recording to wave out mix 
Procedure RecWindow(void)
   
   hwnd = OpenWindow(#PB_Any,0,0,200,200,"Tone",#PB_Window_Invisible)
   Config\hWindow=WindowID(hwnd)
   SetWindowCallback(@record_CallBack())
   Record_Start()
    
   Repeat 

   ev = WaitWindowEvent() 

   Until ev=#WM_CLOSE 
      
EndProcedure 

;XIncludeFile "mixer.pbi" 

OpenWindow(0,0,0,512,200,"fft",#PB_Window_SystemMenu)

CreateThread(@RecWindow(),0)
CreateThread(@draw(),0)

Repeat 

  ev = WaitWindowEvent()

Until ev =#WM_CLOSE    

gdraw = #False 

CloseWindow(0)

 

Re: DTMF detection

Posted: Sat Oct 10, 2009 6:57 am
by PB
SetWinBackgroundColor() is not a function, array, macro or linked list.

Re: DTMF detection

Posted: Sat Oct 10, 2009 7:50 am
by Kwai chang caine
Yeaaaahhh !!! That's works fine :D
Like said PB just 2 command SetWinBackgroundColor(), that we must comment
And after ......it's cool 8)

Very good idea to create this code.
And i never see the same nowhere :shock:

Thanks a lot IDLE, with your code always more astonishing 8)

Re: DTMF detection

Posted: Sat Oct 10, 2009 8:01 am
by idle
I guess its a PBOSL function.

replaced it with SetWindowColor() and InvalidateRect()

Re: DTMF detection

Posted: Sat Oct 10, 2009 8:28 pm
by Nico
Not work here, i don't know why!

Re: DTMF detection

Posted: Sat Oct 10, 2009 9:10 pm
by idle
Nico wrote:Not work here, i don't know why!
Did you set recording source to waveout / stereo mix ?
I only tested it on XP, recording on Vista and Windows 7 can be problematic as some sound drivers don't support waveout mix or what ever else it could be named stereo mix, what u hear ...
It's also disabled by default.

Re: DTMF detection

Posted: Mon Oct 12, 2009 2:33 am
by John-G
why do i keep getting "Compiler Warnings" ...

Warning
Depreated function 'CreateGadgetLast()' used

Has 4.40 Dont a way with someting

Re: DTMF detection

Posted: Mon Oct 12, 2009 4:33 am
by idle
probably because you will have a call to CreateGadgetList() following an openwindow() command somewhere in your code, the command is no longer required.

Re: DTMF detection

Posted: Mon Oct 12, 2009 1:16 pm
by luis
This is taking me soooooo back in time :)

Thank you for sharing it.

Re: DTMF detection

Posted: Wed Oct 14, 2009 11:12 pm
by idle
cobbled together a dll with tone generator but you need to compile app as unicode.
only tested on XP

http://www.idlearts.com/dtmf.zip

Re: DTMF detection

Posted: Fri Oct 16, 2009 2:09 am
by John-G
Hello Idle,

ok I love what you have done ...Again cool...very small and fast and gives anyone needing a DTMF a door way in.
BUT.. ( u know there is always a BUT :lol: ) it seems worst in service. It looks like you moved the noise floor up a little to much sorry ...

Maybe so i dont keep bugging you :roll: We need a small Config File or Config
Options that would let us play with timeing /noise floor/ what freq to Listen-ignore
so that we can notch out what we dont need ..it seems to have gotten a little worst than before works good as long and there is no noise

thanks Again John :wink:

Re: DTMF detection

Posted: Fri Oct 16, 2009 6:44 am
by idle
ok added a slider for threshold on the keypad.

Re: DTMF detection

Posted: Sat Jul 31, 2010 3:33 am
by John-G
hello..

I had to take some time off for health problems and now i am programing again .. And was having some problems with the DTMF. Is there anyway you can could add a string$ or something so i can grab the number/leather or let me know the why i can grab the output .. Only need to get it say 1 number per second so that i can sample for a command

If you don't have time that's Ok but though I would ask :roll:
John

Re: DTMF detection

Posted: Sat Jul 31, 2010 9:43 am
by idle
I'll have a look tomorrow, it's been a while.