[Windows] Simple voice control

Just starting out? Need help? Post your questions and find answers here.
User avatar
Michael Vogel
Addict
Addict
Posts: 2666
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

[Windows] Simple voice control

Post by Michael Vogel »

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: Select all


; 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)
Joris
Addict
Addict
Posts: 885
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: [Windows] Simple voice control

Post by Joris »

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).
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Post Reply