From 3dce8528db2c4125443b489224049ce05e57a6fc Mon Sep 17 00:00:00 2001 From: Drew Bednar Date: Fri, 28 Apr 2023 20:34:20 -0400 Subject: [PATCH] saving the virtual assistant --- data/hello.mp3 | Bin 0 -> 7200 bytes dev_requirements.in | 2 + dev_requirements.txt | 41 ++++++++++++ play_audio/simple_play.py | 2 +- say | 1 + socket_send_recieve/sender.py | 2 +- speech_recog/requirement.in | 3 + speech_recog/requirement.txt | 26 ++++++++ speech_recog/virtual_assistant.py | 102 ++++++++++++++++++++++++++++++ 9 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 data/hello.mp3 create mode 100644 dev_requirements.in create mode 100644 dev_requirements.txt create mode 100644 say create mode 100644 speech_recog/requirement.in create mode 100644 speech_recog/requirement.txt create mode 100644 speech_recog/virtual_assistant.py diff --git a/data/hello.mp3 b/data/hello.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..d584cf38384c70d58078208423779729a6a342ba GIT binary patch literal 7200 zcmeI$XH*m4wgB)6y$K1O5Ru*l3>~R~fb=d1h;*f6C?beRFF`=52Bb?Dq)HJ1CG=hl zf`|f&6hQ<;q|PJ%yWYL;uDjM->$PuhKF+N9&7Rr&>^bLvyPDkq0FdfHPyhgG$s-;K zw1W(j6NBJlYse=1l159i?UPs16pt>zL6u!#>y+EWAklA^048#w=(6 zf)Vy+o#6XtaC(jI5!dz9t<8#B zmiM~r9CtG&qcI3mAf)!i31ta5A0DzX-1$vecidIi>4&;T7(gJD1FhD=IPRs940?SU z?NG^_3oDd$fu(a|7#dEb6~^QKfK&B3^H@4259X4^vn;YF|tyab?5 zR_RAr(}+G2F)0xb=tFS>V(5KuL=NEpuG^Yu6887%t+x6wJjF$<=p zLJxx{f_lWvj0+8Y@(}(Bbbi?K6G>}5s!L+}tse-!SS!aopsG|uVrEcppquD3uvjbR z03nQ+p281{-1}p0=R^dJNF0YRJdzi)e$wU9z$NCgZ@3c8?O z2Zl}7s>@bFsyp<&2|c1KvaQ^9y^HoXwI)QLmYGMd3K*qHKe-fKTq5*qr&A=|WvclE?-wGeb|dH!TC(&VgT_v*>RwramYW z@*>3EtV3J|U(^4w6q0|bs2w8YGrO~%xo@$`n4<;RFR1$4_t$-e0|@l#r?q^rTDFe! z@`BU=wJIrJFGs+!XT(Fev=o_f0l%}H=IkU=^j@NB$uVBFvvca3$}nXercO<}>&`7wpBZev0mJop09&Zot(H2me;Tl^DS%(zjt`ZoVG|{#Lkc zNYe+t4vo~0@y{jt)UbicDqw^rJu$l~jr0l@V@N{9OUrgdK3x24T?z^fxn7~QD05r> z;OSCc(oyJZw`Qw8ySOmwPMqDW7EJC|@%M%9b?UdY~-sFcdJ^h0Pq|fICGA!JE2$d3g^1mwsxNoGFoVZ`1j=9!aXDo zw6|tEHQG;(Q<3u0t%1m>TMtKQM*V3-y_|Aot}9}=Ec{8;sTf&_J}>l7Q!UVIV-TRb z3sz|Me^(%Aj(2U6dtt@#t4 ztrL&T-9NTJEnj$5v|_mfifJxyp-IhC3~gVmBCiVcx{pp3OkR`DTQ0jNTLQUXf*-^NN99I|DwC1Yt4-e})3`_D6;A_UFQ|C_}5NY6=diN!mm#s;c$U zuw>Y#nSIIpF-bCW(o4F#!2tJ{&(jWcaVw4mknljUf_h=yH#0HlyoU9?T$&tKA)&(*7uPWdA2RF2LcqFjJjEZ49MRa-OB_hOV3jX&!T7|JsL zDKa&~o{`)I!M9D`_Iu0Varwr@??s}0#GwP8W;5zol1Fmh?b>B~<-!?AJugNgp!Nj6 zz9pRf>yEX)lWe5z8mSH&nWI13!OLgKZ(2@>ejtoTE*zL9!`Rsnj5>yzm9ZzH5t^-n zmt4*BiR?N>6gRgC*)1~L7}q zlXF12WU=@$k9Nxe{@r@YW*#0ZPtYCofYkVX#TAdCLg}C9;_(Q zPlomXUga6fzp?buqJhFlRH09#VSyPgIan z*U@O8X*OD`w3wNkn-7@MMULWgG-ztssi;h&Bk7CO!?74RNdfm?elE12UICbzG!XI< zrW$0Q!(DqFXFwHaCQK6e8iyC_vDuGm8!klC^mZ#xEhvi`W^M^Nm> zY#J913!$YlUQ%7xdckkmeDSN4p?0P*L%u=r5;6X2So!uJKsOfCxK!s}s5vT=opKgd zj=)`px{6m8{Am_G?)S_t3f`=y z2yrzNQH0y_YfjS2mQPmm+sqUFRv515E5P21$vPkRY3u_2*SKrbcg^R6JKlm(N;%&} z$ttKFBV0^E$&%(P)GvD0I#-LdTgHn9iG|XMrw*sc#KL244J6=L1jkk@Dr}hZVuV#_ z%>6D7r@Mc3BKkeNv>YFSdj{09^*H+_jfU7m5>;i(M=w>b(D|rK4-Nt_JGfskF?Us`={#2p;{&H7}Z~Q&i$F zahh{lU9-t$%_VaQDCR}h*3xXzkLsn(PBN+1-8nrLd2&qjZ5SQIze3RLkRoeZ*ZYPf zC=~yjV%n8QQj1yT{#O=fSjSp1x)3)GQin|HAYpX=fv<<006U^E2p-Kdv3cBq89$`M zeMLmo1~TBsr%s=t6SJvI44zoGP8xI&eP`}3^vV#N8^p^STrr$7cN3Z&<&7wef>&Gs z58Gwt(_WotRkxJ1bNB$$zwJ3)9O@}ccT*F>y@BFqA*UXl&N-h=<^H~9Kle*qc>i$K z?mA+iFPSW{y<9j}o9Ig+{OG+P!$Cm9Q28Pvf!)^*>DV}xy!ka~Yks8MAK!+!ytii` z2Ie6|Z&56TPG*+g-VOOOW!-sHR<^s%gFGq<*1mu)hA0rd=VO+?W8t*~$VJwBJIKauw z7p2Xrv7q4;RBV;!&1#ZyA$vJdI2eV%QU&q9_Wk`~lTXz&`CE<*O2oI}uC@FPwCc!+ zOf1%gf~De`+KZg?McOCN<-ozuYQDSK3#(GEXEF0MME@L17S~_%PbGQlSlqzx&6lm`|c!sG-DglWDDWXr!JS%th|8>D|-vcY=FDgv?9iINocF?V~NRJd^ zm3q=bpbNjvv0(7Tv)c zS3*7P9nzD32P6~_eJYqbZwrvgg^|K^ILxRsGm^_%i$wbLu&8LIM#?cT>@$bZkroXV zEXggHjb{)_)O!6v!qz5bz~`QA1Jzd9vZL7KuyxUZIBwc%N>jOW@ZV~WE2!K$YvjMPeG)c@(9$dIc$jwgCevs2<0l$1SO! z9OH4<#o0N-O{|;h(2F!xD!s(`rPrDqT)N^5_ervn+^)ufR$+n zO9;h#$`l#F!szCx)|S>l{yC!W&Fn-U56s!%CT>d~bXFyNNPI)NEL%@WwkM%L&NW3v zxLTleN25doojHp9(MLnxge*vvQ0{$hQ&dkFoPePw(!c*Yn|y)Cpjk*I=UJ)61z$wO z%G>yDXjKz^A9km2XTDJ@hewoliTpWXpl?;?{HjVA_bHOt2rq-R89iQ6oGY%LgIBdwODNEYVxxW)b(yQ>MC4xt$fxS*|1Z zrF3926K5{=XONZ3>zjQnrA|(rS@K#m${5E>n>;3b*SHLcPwGYwwMRmtizLEn>>o;( z892H%-fVK#s=u+g{j{@USb31y`hxV?t9(9W%_ttb=HA6~h3H!_IhGax6T|5RGpQG| zLY?qv^O1V9Bk0N6tD9T?p&F(?W2XNIW|UQGp(~!zanhycC7OwupUYFgcA?+D9GvAc zEuCBKp#>7w42 zeiiah4}$3XzzP}9`hyv!2+d%dDO^8U5l3jwYM%6@_x7)Dj`Fm*;l0JgdD@PtRD0Lp zB*=PCnG0Wg3lQ5sW*0t|loTk{t`Kx*RKZ?vz1%y_z(<2_CMyG>Egm(_=kHmw=Pxok^>A`9&UnPUI~m^-jJ? zZx6?-MZd;jr_HNnKE~ZXDDKn<>QbKfW8KG~ZAtgt*4i&>AkmM4ni73DMwcz@;#r!$ z8@()kXY3B6V&0`d$nvJCWJMac>AM3C9_{tyQr0!J$;U(PY(rO5RO3nFL~OJgCsXdKi~#b~I3 zJ<2K(siISdPv6e=W-!ZzT0J889^2;kIQkArI32!+wZH6e4?TY!#7%E>a5!V;VGE_{C(N@0DGFqw_QKq!mq^!&ua`7@&gF=77gbec> zj$nQ_o!4$BH4i?c%7QN)5t_Bf_6X-Ps|{4-wh2*WiKKU9F7HrKx^2}BGMxmL_p}rJ zGT3&i8!*+Kt^)tz=JktFU7e1fHrE8|ey5T7VZ7}s#~c3;t?3nhyUO1qiof@cldB2l z2=efXLKBNxEwXTwc#cwtdm+Eh@N2K(r^*XX7Mc;yhKKz6iiDOUh<-gZK7kqNGBc?7 z2*0=-Q4?8E{>J^puGv!w%pkXIb~jKr)RLBvL?SEyE63mNBKlphKTTACW1_)-mH1B| z$Il^iY4Re`A7Kcu_zCDH8qg!N;u>YORn zbX&7ZoLIiejKs$W1iOM+9BEIoR8D&k8j8wQ{J!0a%9gwV!B&ZW0cWMT!^ij6{!$fj zfB0;Z7)Qsk_Hn>FEeW7qc zAmwzy_{$r8j*VED@+nL#EJe(fs(9AvQY)!SS(yC>OnJ1P82{gWtB1#R%qc6bR6CUt zMN3sOS%k(W{~OX4f|tAp8q-XO0=4`m*gMlzbqOx~P3>C@TX916UB*<=fU8=D zEM3LzkuTPS4*0^V)RNi+X`cSIe+zPOLQDW;Z>yFrVv9e68B7#@`S+Y!{CAIke|AQH~xc~(9`u-pJfB*jf@BfE1WMiwMaq0h5pFs*x3Ot!B zi{`-3FVr~e$<*|0)-m(8r%z={YUJhBj#$Tb{pHlaA7{UY{abvTi!h^X1V0Nuy*0=&t=IW zC(T14*P^Ra1~NRGQQ;($04)MwWv4L1W{70z*!419CKZsT)rGi|NAj{diQt!;F6eMI z%v}N*GXKdR|2=bXZ|3Mi!)&TSw~3$`k3+P^8vqX()RH2v%F=7vSj)Gr3y6?5mt6Pi zGnE^OS|gz-b^c*{RkcwyOXs@LTTekVY<(NaGY2nw6yw9vNRwYgALN*PX$$Bkv|c~2 z*SDNjHfE^KE67h1ZaTz!XIWL>YuQFf!}zIy7}i6;ZFH1E1Sht}He{f=D-BQUD$r(D zVUFPwJmD`Z|2~rc({A~%7ymK7>JdYbH-KYo%l~SB{@W9Y@u&QI|NPe={>NwjDZl?? I?O%a^19Atnx&QzG literal 0 HcmV?d00001 diff --git a/dev_requirements.in b/dev_requirements.in new file mode 100644 index 0000000..9aa1b14 --- /dev/null +++ b/dev_requirements.in @@ -0,0 +1,2 @@ +black +pip-tools diff --git a/dev_requirements.txt b/dev_requirements.txt new file mode 100644 index 0000000..ce1c082 --- /dev/null +++ b/dev_requirements.txt @@ -0,0 +1,41 @@ +# +# This file is autogenerated by pip-compile with python 3.8 +# To update, run: +# +# pip-compile dev_requirements.in +# +black==22.10.0 + # via -r dev_requirements.in +build==0.9.0 + # via pip-tools +click==8.1.3 + # via + # black + # pip-tools +mypy-extensions==0.4.3 + # via black +packaging==21.3 + # via build +pathspec==0.10.2 + # via black +pep517==0.13.0 + # via build +pip-tools==6.10.0 + # via -r dev_requirements.in +platformdirs==2.5.4 + # via black +pyparsing==3.0.9 + # via packaging +tomli==2.0.1 + # via + # black + # build + # pep517 +typing-extensions==4.4.0 + # via black +wheel==0.38.4 + # via pip-tools + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools diff --git a/play_audio/simple_play.py b/play_audio/simple_play.py index d3c0118..355c47b 100644 --- a/play_audio/simple_play.py +++ b/play_audio/simple_play.py @@ -1,6 +1,6 @@ import simpleaudio as sa -filename = 'myfile.wav' +filename = './data/hello.wav' wave_obj = sa.WaveObject.from_wave_file(filename) play_obj = wave_obj.play() play_obj.wait_done() # Wait until sound has finished playing \ No newline at end of file diff --git a/say b/say new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/say @@ -0,0 +1 @@ + diff --git a/socket_send_recieve/sender.py b/socket_send_recieve/sender.py index 4a44160..3ee8dfa 100644 --- a/socket_send_recieve/sender.py +++ b/socket_send_recieve/sender.py @@ -12,7 +12,7 @@ wave_file_path = sys.argv[1] if not wave_file_path.endswith('.wav'): raise ValueError("File must have .wav file extension.") -(HOST,PORT)=('rospi.runcible.io',9000) +(HOST,PORT)=('localhost',9000) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect((HOST,PORT)) with open(wave_file_path, 'rb') as wave_file: diff --git a/speech_recog/requirement.in b/speech_recog/requirement.in new file mode 100644 index 0000000..3afeebf --- /dev/null +++ b/speech_recog/requirement.in @@ -0,0 +1,3 @@ +pyaudio +SpeechRecognition +gTTs \ No newline at end of file diff --git a/speech_recog/requirement.txt b/speech_recog/requirement.txt new file mode 100644 index 0000000..c5f16db --- /dev/null +++ b/speech_recog/requirement.txt @@ -0,0 +1,26 @@ +# +# This file is autogenerated by pip-compile with python 3.8 +# To update, run: +# +# pip-compile requirement.in +# +certifi==2022.9.24 + # via requests +charset-normalizer==2.1.1 + # via requests +click==8.1.3 + # via gtts +gtts==2.2.4 + # via -r requirement.in +idna==3.4 + # via requests +pyaudio==0.2.12 + # via -r requirement.in +requests==2.28.1 + # via gtts +six==1.16.0 + # via gtts +speechrecognition==3.8.1 + # via -r requirement.in +urllib3==1.26.12 + # via requests diff --git a/speech_recog/virtual_assistant.py b/speech_recog/virtual_assistant.py new file mode 100644 index 0000000..7a07ee0 --- /dev/null +++ b/speech_recog/virtual_assistant.py @@ -0,0 +1,102 @@ +import webbrowser +import typing +import os +import sys +from abc import ABC, abstractmethod + +import speech_recognition as sr + +class Action(ABC): + + action_key: str + + @abstractmethod + def action_callback(self, action_text) -> typing.Any: + pass + + def register_va(self, virtual_assisant): + self._va = virtual_assisant + + def sans_action_key(self, action_text): + return action_text[len(self.action_key)+1:] + +class BrowserAction(Action): + + action_key = "open browser" + + def action_callback(self, action_text): + domain = self.sans_action_key(action_text) + url = f"https://{domain}" + print(f"Opening: {url}") + self._va.("Opening {domain}") + webbrowser.open(url) + +class EchoAction(Action): + + action_key = "echo" + + def action_callback(self, action_text) -> typing.Any: + print(action_text) + self._va.text_to_speech(self.sans_action_key(action_text)) + +class StopAction(Action): + + action_key = "stop listening" + + def action_callback(self, action_text) -> typing.Any: + print("Stopping Virtual Assistant...") + sys.exit(0) + + +class VirtualAssistant: + + def __init__(self, timeout: int = 3, actions: typing.List[Action]=[]): + self.listener = sr.Recognizer() + self.timeout = timeout + self.action_register = [] + for action in actions: + self.register_action(action) + + def register_action(self, action: Action): + """Adds new Action instances to the Virtual Assistant's action register.""" + self.action_register.append(action) + action.register_va(self) + + def parse_action(self, text: str): + """Checks the VA command register and executes callbacks when found.""" + for action in self.action_register: + if text.lower().startswith(action.action_key): + action.action_callback(text) + return + print(f"No actions taken on: {text}") + + def speech_to_text(self) -> str: + """""" + try: + with sr.Microphone() as mic: + print("Arni is listening...") + self.listener.adjust_for_ambient_noise(mic) + utterance = self.listener.listen(mic, timeout=self.timeout) + content = self.listener.recognize_google(utterance) + return content + except (sr.UnknownValueError, sr.RequestError, sr.WaitTimeoutError): + return "" + + def text_to_speech(self, text: str): + print(f"Saying: {text}") + cmd = f'gtts-cli --nocheck "{text}" | mpg123 -q -' + print(cmd) + os.system(cmd) + + + +def main(): + print("Creating virtual assistant...") + va = VirtualAssistant(actions=[BrowserAction(),EchoAction(), StopAction()]) + # breakpoint() + while True: + content = va.speech_to_text() + va.parse_action(content) + +if __name__ == "__main__": + main() \ No newline at end of file