Browse Source

Ca se précise :
- plus de cpu hog
- détection des silences correctes.

Reste a fignoler la planifications des répliques, a refactorer le tout
puis a inclure les fonctionalité de détection d'appel de call center du
projet callblocker.

Jean-Francois Burdet 6 years ago
parent
commit
cbb399639c

+ 97 - 54
lenny.py

@@ -2,22 +2,22 @@
 # -*- coding: utf-8 -*-
 
 import audioop
+import contextlib
+import glob
 import io
 import os
+import subprocess
 import syslog
 import threading
 import time
-import glob
-import subprocess
+import wave
+from datetime import datetime
 
 import linphone
 from enum import Enum
-from datetime import datetime
-
-import wave
-import contextlib
+import threading
 
-VOLUME_THRESHOLD = 50
+VOLUME_THRESHOLD = 100
 
 
 class ConversationStatus(Enum):
@@ -26,12 +26,31 @@ class ConversationStatus(Enum):
     WAITFORANSWER = 2
 
 
+class Conversation(object):
+    def __init__(self):
+        self._status = ConversationStatus.READY_TO_TALK
+
+    @property
+    def status(self):
+        return self._status
+
+    @status.setter
+    def status(self, value):
+        if value != self._status:
+            print(" New status : " + str(value))
+            self._status = value
+
+
 current_dir = os.path.dirname(os.path.realpath(__file__))
-replies = glob.glob(current_dir + "/replies/*.wav")
+replies_seq = glob.glob(current_dir + "/replies/sequence/*.wav")
+replies_seq.sort()
+
+replies_generic = glob.glob(current_dir + "/replies/generic/*.wav")
+replies_generic.sort()
 
 incoming_stream_file = "/tmp/lenny"
 
-conversation_status = ConversationStatus.READY_TO_TALK
+conversation = Conversation()
 
 replies_pos = 0
 
@@ -50,30 +69,42 @@ def get_wav_duration(fname):
         return frames / float(rate)
 
 
+def sleep(duration):
+    dummy_event = threading.Event()
+    dummy_event.wait(timeout=duration)
+
+
 def say(core):
-    global conversation_status
+    global conversation
     global replies_pos
-    if conversation_status is not ConversationStatus.IMTALKING:
-        voice_filename = replies[replies_pos]
-        replies_pos = (replies_pos + 1) % len(replies)
+    if conversation.status is not ConversationStatus.IMTALKING:
+        conversation.status = ConversationStatus.IMTALKING
+
+        # On joue les repliques en sequence, puis quand
+        # on arrive au bout, on en joue une au hasard
+        # du groupe 'generic'
+
+        voice_filename = replies_seq[replies_pos]
+        replies_pos = (replies_pos + 1) % len(replies_seq)
         if replies_pos == 0:
             # On ne rejoue jamais la première réplique "allo"
             replies_pos = 1
 
-        print("Saying : " + voice_filename)
-        conversation_status = ConversationStatus.IMTALKING
-
         duration = get_wav_duration(voice_filename)
+        print("Saying : " + voice_filename + "(duration : " + str(duration) + ")")
+
         core.play_file = voice_filename
-        time.sleep(duration)
+
+        sleep(duration)
         core.play_file = ""
 
         # On laisse l'autre l'occassion de reparler
-        conversation_status = ConversationStatus.WAITFORANSWER
+        conversation.status = ConversationStatus.WAITFORANSWER
 
 
 def call_state_changed(core, call, state, message):
-    global conversation_status
+    global conversation
+    global replies_pos
     log("state changed : " + message)
 
     if state == linphone.CallState.Released:
@@ -84,21 +115,29 @@ def call_state_changed(core, call, state, message):
 
     if state == linphone.CallState.IncomingReceived:
         log("Incoming call : {}".format(call.remote_address.username))
-        if call.remote_address.username == "**620":
-            print(" .. from  JF !")
+
+        replies_pos = 0
+
+        if call.remote_address.username == "**620" or call.remote_address.username == "0765757624":
+            print(" ... " + call.remote_address.username)
+
+        #if call.remote_address.username == "0227570500":
+        #    print(" .. from  Bernard !")
+
 
             call_params = core.create_call_params(call)
-            call_params.record_file = current_dir + "/out/call_"+datetime.now().strftime('%Y-%m-%d_%Hh%Mmn%Ss')+".wav"
+            call_params.record_file = current_dir + "/out/call_" + datetime.now().strftime(
+                '%Y-%m-%d_%Hh%Mmn%Ss') + ".wav"
 
             # Let ring some time
-            time.sleep(5)
+            sleep(5)
 
             core.accept_call_with_params(call, call_params)
             call.start_recording()
 
-            time.sleep(2)
+            sleep(2)
 
-            t = threading.Thread(target=incoming_stream_worker, args=[silence_call_bak, core, call])
+            t = threading.Thread(target=incoming_stream_worker, args=[core, call])
             t.start()
 
             say(core)
@@ -112,15 +151,8 @@ def registration_state_changed(core, call, state, message):
     log("registration_state_changed: " + str(state) + ", " + message)
 
 
-def silence_call_bak(core, call):
-    global conversation_status
-    global replies_pos
-    if conversation_status is not ConversationStatus.WAITFORANSWER:
-        say(core)
-
-
-def incoming_stream_worker(sil_cb, core, call):
-    global conversation_status
+def incoming_stream_worker(core, call):
+    global conversation
     print("Worker is starting")
 
     f = open(incoming_stream_file, "rb")
@@ -128,25 +160,37 @@ def incoming_stream_worker(sil_cb, core, call):
     p = f.tell()
     buf = ''
 
+    previous_status = conversation.status
+
     while call.state is not linphone.CallState.End and not THREADS_MUST_QUIT:
-        f.seek(p)
-        buf += f.read(4096)
-        p = f.tell()
+        if conversation.status is ConversationStatus.IMTALKING:
+            f.seek(0, io.SEEK_END)
+            p = f.tell()
+        else:
+
+            if previous_status != conversation.status:
+                f.seek(0, io.SEEK_END)
+                p = f.tell()
+
+            f.seek(p)
+            buf += f.read(4096)
+            p = f.tell()
+
+            if len(buf) >= 20000:
+                volume = audioop.rms(buf, 2)
+                print("Detected volume : " + str(volume))
+                #print("State : " + str(conversation.status))
+                buf = ''
+                if volume < VOLUME_THRESHOLD:
+                    if conversation.status is ConversationStatus.READY_TO_TALK:
+                        threading.Thread(target=say, args=[core]).start()
+                else:
+                    conversation.status = ConversationStatus.READY_TO_TALK
 
         # We must sleep a bit to avoid cpu hog
-        time.sleep(0.01)
-
-        if len(buf) >= 20000:
-            volume = audioop.rms(buf, 2)
-            # print("Detected volume : " + str(volume))
-            # print("State : " + str(conversation_status))
-            buf = ''
-            if volume < VOLUME_THRESHOLD:
-                if conversation_status is not ConversationStatus.IMTALKING:
-                    t = threading.Thread(target=sil_cb, args=[core, call])
-                    t.start()
-            else:
-                conversation_status = ConversationStatus.READY_TO_TALK
+        sleep(0.01)
+        previous_status = conversation.status
+
 
     print("Worker is quitting")
 
@@ -181,7 +225,7 @@ def main():
     core.add_auth_info(auth_info)
 
     while True:
-        time.sleep(0.03)
+        sleep(0.03)
         core.iterate()
 
     log("callblocker quitting.")
@@ -206,16 +250,15 @@ if __name__ == "__main__":
 # qui est allé à l'Université. Et elle l'a terminé avec mention, nous en sommes très fière.
 # et du coup elle m'a dit que je devrais jeter un oeil à ce dont vous me parlez.
 # - Hmm, désolé, j'ai pas tout à fait compris, vous pouvez répéter ?
-# - Could you say that again please ?
 # - Heu, oui, vous pouvez me rappeler de la part de quelle entreprise vous appelez
 # - Oui, en fait y'a une chose ... heuu, car j'avais eu un appel comme le votre, j'ai eu pas mal
 #   de problème avec le personnel ici, qui m'on fait la morale, car je me suis retrouvé avec des affaires
 #   dont je n'avais pas besoin, et ma nièce justement m'a fait la morale également ... vous savez ce que c'est
 #   la famille ....
-#
+# - Could you say that again please ?
 
 # Idées :
 # - En tout cas vous me semblez une personne fort sympathique, c'est toujours agréable de discute
 #   des personnes sympathique, quand on est une personne seule comme moi ... heuu, en fait on parlait
 #   de quoi déjà ?
-
+# - Bon,

BIN
replies/00_allo.wav


BIN
replies/generic/atchoum.wav


BIN
replies/generic/baillement_mal_dormi.wav


BIN
replies/generic/ca_va_me_couter_combien.wav


BIN
replies/generic/toutafait.wav


BIN
replies/sequence/00_allo.wav


+ 0 - 0
replies/01_quoi_j'ai_pas_compris.wav → replies/sequence/01_quoi_jai_pas_compris.wav


BIN
replies/sequence/02_entends_pas_bien.wav


BIN
replies/sequence/03_oui_oui_cest_ca.wav


BIN
replies/sequence/04_oui_tres_bien_oui.wav


BIN
replies/sequence/05_me_semble_deja_appelle.wav


BIN
replies/sequence/06_cest_quoi_votre_nom_deja.wav


BIN
replies/sequence/07_niece_jeter_un_oeil.wav


BIN
replies/sequence/08_vous_pouvez_repeter.wav


BIN
replies/sequence/09_rappeler_nom_entreprise.wav


BIN
replies/sequence/10_on_ma_fait_la_morale.wav


BIN
replies/sequence/11_satane_telephone_entends_rien.wav


BIN
replies/sequence/12_vous_etes_sympathique.wav


BIN
replies/sequence/13_bruit_chien.wav


BIN
replies/sequence/14_ca_va_me_couter_combien.wav