PureBasic Forum
http://forums.purebasic.com/english/

[Windows] Simple voice control
http://forums.purebasic.com/english/viewtopic.php?f=13&t=75972
Page 1 of 1

Author:  Michael Vogel [ Sun Sep 20, 2020 10:21 am ]
Post subject:  [Windows] Simple voice control

I need to control some functions within my purebasic program while staying away some meters from the keyboard. So I thought about voice control, but don't want to install an external program, etc.

Now my idea is recognizing one, two or three hand claps to control three different functions. The first step is done with the code below (mixed some sources from the forum) so I am able to differ between "silence" and "noise" already.

But how to detect, if one or more quick claps have been done? Actually one single "clapping wave" can be seen in one captured buffer but can also appear in two consecutive buffers as well.
So I'd need a simple method to get the "clap count", this is my actual method...
...which is not perfectly reliable (and from time to time, the program even crashs) :|

Code:

; Define Audio

   #AudioMaxBuffer=      16
   #AudioTreshholdLow=   123
   #AudioTreshholdHigh=   133

   Enumeration 1
      #AudioMono
      #AudioStereo
   EndEnumeration

   Enumeration
      #AudioChannelLeft
      #AudioChannelRight
   EndEnumeration

   Structure AudioType
      Device.l
      Window.l
      Channels.l
      Wave.l
      ScopeTimer.l
      Buffers.l
      BufferLen.l
      Format.WAVEFORMATEX
   EndStructure

   Global Audio.AudioType
   Global AudioSemaphore=CreateSemaphore()
   Global AudioCapturing
   Global AudioPw.l,AudioPl.l
   Global *AudioBuffer=AllocateMemory(32768)
   Global Dim AudioBuffer.WAVEHDR(#AudioMaxBuffer)

   Structure ClapType
      RingPos.i
      RingTrend.i
      OnState.i
      ClapCount.i
      Claps.i
      Pause.i
   EndStructure

   Global Clap.ClapType

   #RingBuffer=250
   Global Dim OnOff(#RingBuffer)


   Global Position=0
   Global Trend=0
   Global State
   Global Clap
   Global ClapCount
   Global Pause



   With Audio
      \Device=         0;
      \ScopeTimer=      20;
      \Format\nChannels=   #AudioMono
      \Format\nSamplesPerSec=   12000;   8000 / 11025 / 12000 / 16000 / 22050 / 24000 / 32000 / 44100 / 48000
      \Format\wBitsPerSample= 8;      8 / 16

      \Buffers=         8;      1..MaxBuffers
      \BufferLen=         1024;      1024..8192?

      \Format\wFormatTag=   #WAVE_FORMAT_PCM
      \Format\nBlockAlign=   \Format\nChannels*\Format\wBitsPerSample/8
      \Format\nAvgBytesPerSec=\Format\nSamplesPerSec*\Format\nBlockAlign
      \Format\cbSize=      0

   EndWith

; EndDefine

Procedure AudioGetDevices()

   MMNumDevice.l = waveInGetNumDevs_()
   If MMNumDevice
      For MMDeviceId=0 To MMNumDevice
         MMResult.l = waveInGetDevCaps_(#WAVE_MAPPER+MMDeviceId,@Caps.WAVEINCAPS,SizeOf(WAVEINCAPS))
         If MMResult = #MMSYSERR_NOERROR
            Debug Str(MMDeviceId)+" - "+PeekS(@Caps\szPname,#MAXPNAMELEN)
         EndIf
      Next
   EndIf

EndProcedure
Procedure AudioStartCapture()

   With Audio
      If waveInOpen_(@\Wave,#WAVE_MAPPER+\Device,@\Format,\Window,#Null,#CALLBACK_WINDOW|#WAVE_FORMAT_DIRECT)=#MMSYSERR_NOERROR
         For i = 0 To \Buffers-1
            AudioBuffer(i)\lpData=AllocateMemory(\BufferLen)
            AudioBuffer(i)\dwBufferLength=\BufferLen
            waveInPrepareHeader_(\Wave,AudioBuffer(i),SizeOf(WAVEHDR))
            waveInAddBuffer_(\Wave,AudioBuffer(i),SizeOf(WAVEHDR))
         Next

         If waveInStart_(\Wave)=#MMSYSERR_NOERROR
            Debug ":)"
            SetTimer_(\Window,0,\ScopeTimer,0)
         EndIf
      EndIf
   EndWith

EndProcedure
Procedure AudioStopCapture()

   AudioPw=#Null
   AudioCapturing=#Null
   SignalSemaphore(AudioSemaphore)

   With Audio
      If \Wave
         waveInReset_(\Wave)
         waveInStop_(\Wave)
         For i = 0 To \Buffers-1
            If AudioBuffer(i)
               waveInUnprepareHeader_(\Wave,AudioBuffer(i),SizeOf(WAVEHDR))
               FreeMemory(AudioBuffer(i)\lpData)
            EndIf
         Next
         waveInClose_(\Wave)
      EndIf
   EndWith

   Debug "done."

EndProcedure
Procedure AudioReadCapture(WaveIn.l,*WaveBuffer.WAVEHDR)

   If WaveIn And *WaveBuffer
      If *WaveBuffer\lpData
         CopyMemory(*WaveBuffer\lpData,*AudioBuffer,*WaveBuffer\dwBytesRecorded)
      EndIf


      max=0
      min=999
      StartDrawing(CanvasOutput(0))
      Box(0,0,1024,255,#Black)

      on=0
      off=0
      time=0
      count=0

      With Clap
         For i=0 To 1023
            b=PeekA(*AudioBuffer+i)
            noise=Bool(b<#AudioTreshholdLow Or b>#AudioTreshholdHigh)

            \RingTrend-OnOff(\RingPos)+noise
            OnOff(\RingPos)=noise
            \RingPos=(\RingPos+1)%#RingBuffer

            If \OnState
               If \RingTrend<#RingBuffer/8
                  \OnState=0
                  \Pause=0
               EndIf
            Else
               If \RingTrend>#RingBuffer/4
                  \OnState=1
                  \ClapCount+1
                  Debug "*"
                  \Pause=0
               ElseIf \RingTrend=0
                  \Pause+1
                  If \Pause=5000
                     If \ClapCount
                        \Claps=\ClapCount
                        \ClapCount=0
                        Debug "CLAP "+Str(\Claps)
                     EndIf
                  EndIf
               EndIf
            EndIf

            LineXY(i,0,i,b,#Green)
            Box(i,240-\OnState*222,3,3,#Red)   
         Next i
         StopDrawing()
         If \Claps
            ;Debug Str(\Claps)
            \Claps=#Null
         EndIf

      EndWith

      waveInAddBuffer_(WaveIn,*WaveBuffer,SizeOf(WAVEHDR))
   EndIf

EndProcedure
Procedure AudioLoop(nil)

   AudioCapturing=#True

   While AudioCapturing
      WaitSemaphore(AudioSemaphore)
      If AudioPw
         AudioReadCapture(AudioPw,AudioPl)
      EndIf
   Wend

   Debug "Thread done."

EndProcedure
Procedure Callback(handle.l,msg.l,w.l,l.l)

   If msg=#MM_WIM_DATA
      AudioPw=w
      AudioPl=l
      SignalSemaphore(AudioSemaphore)
   EndIf

   ProcedureReturn #PB_ProcessPureBasicEvents

EndProcedure

Audio\Window=OpenWindow(0,10,10,1024,255,"TEST")
CanvasGadget(0,0,0,1024,255)

CreateThread(@AudioLoop(),0)
SetWindowCallback(@Callback(),0)

AudioGetDevices()
AudioStartCapture()

Repeat
   Select WaitWindowEvent()
   Case #PB_Event_CloseWindow
      quit=1
   EndSelect
Until quit

AudioStopCapture()
Delay(100)

Author:  Joris [ Wed Sep 23, 2020 8:56 am ]
Post subject:  Re: [Windows] Simple voice control

Michael Vogel wrote:
...But how to detect, if one or more quick claps have been done? Actually one single "clapping wave" can be seen in one captured buffer but can also appear in two consecutive buffers as well.
So I'd need a simple method to get the "clap count", this is my actual method...
...which is not perfectly reliable (and from time to time, the program even crashs) :|
Hi Michael ,
I'm not able to test your code at the moment, but (based on midi recording proces) I think you need to have a continu time registration, at least untill the third clap is recorded. If I understand your code without being able to test it, it looks like todo only recording when there is data. So what's the time between two records ?
The recording itself can imo just be a little part, as a absolutle threshold level, that must be in each clap sound. (A clapsound can have strange levels mostly two high parts in one clap).

Page 1 of 1 All times are UTC + 1 hour
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/