def get_tts_audio(self, message, language, options=None): """Load TTS from ResponsiveVoice.""" if language is None: language = self._lang from responsive_voice import ResponsiveVoice # websession = async_get_clientsession(self.hass) try: engine = ResponsiveVoice() _LOGGER.error(message) data = engine.say(sentence=message, gender=self._gender, lang=self._lang) except HTTPException as ex: _LOGGER.error("Timeout for ResponsiveVoice speech") return None, None # try: # with async_timeout.timeout(10, loop=self.hass.loop): # engine = ResponsiveVoice() # _LOGGER.error(message) # data = engine.say( # sentence=message, gender="self._gender", lang="self._lang" # ) # if request.status != 200: # _LOGGER.error("Error %d on load URL %s", # request.status, request.url) # return None, None # data += await request.read() # except (asyncio.TimeoutError, aiohttp.ClientError): # _LOGGER.error("Timeout for ResponsiveVoice speech") # return None, None return ('mp3', data)
def get_tts_audio(self, message, language, options=None): """Load TTS from ResponsiveVoiceTTS.""" engine = ResponsiveVoice() # from responsive_voice.voices import Ellen # ellen = Ellen() # result = ellen.say(message) print("message", message, "language", language, "pitch", self._pitch, "rate", self._speed, "vol", self._volume) result = engine.say(message, language, pitch=self._pitch, rate=self._speed, vol=self._volume) # result = engine.get_mp3(message, language, pitch=self._pitch, # rate=self._speed, vol=self._volume) # if options is None: # result = engine.say(message, lang=language, pitch=self._pitch, # rate=self._speed, vol=self._volume) # else: # speech_data = self._speech_conf_data.copy() # for key, value in options.items(): # speech_data[_OPTIONS[key]] = value # result = engine.say(message, language, 1, speech_data) print(result) if isinstance(result, dict): _LOGGER.error( "ResponsiveVoice TTS error-- err_no:%d; err_msg:%s; err_detail:%s", result["err_no"], result["err_msg"], result["err_detail"], ) return None, None return self._codec, result
class BareSIP(Thread): def __init__(self, user, pwd, gateway, tts=None, debug=False, block=True, audiodriver="alsa,default", port=5060): self.debug = debug self.user = user self.pwd = pwd self.gateway = gateway #Learn the second one when it has to reasign a unique name #There is an in and an out client name self.jackNames = ['baresip'] if tts: self.tts = tts else: try: from responsive_voice import ResponsiveVoice self.tts = ResponsiveVoice(gender=ResponsiveVoice.MALE) except ImportError: logging.exception( "No responsive_voice module, some features will not work") self._login = "******".format(u=self.user, p=self.pwd, g=self.gateway) self._prev_output = "" self.running = False #Ready immediately, this is a localhost thing self.ready = True self.mic_muted = False self.abort = False self.current_call = None self._call_status = None self.audio = None self._ts = None self.jack_port = "baresip" cnfdir = os.path.join(tmpdir, "ScullerySIP" + str(port)) self.cnfdir = cnfdir try: #Remove any existing shutil.rmtree(self.cnfdir) except: pass os.mkdir(os.path.join(tmpdir, "ScullerySIP" + str(port))) self.cnfdir = cnfdir #Using the template, create a configuration dir for #the baresip instance we are about to make. f = os.path.join(os.path.dirname(__file__), "baresip_template") drivers = "module\t\talsa.so" if 'jack' in audiodriver: drivers = "module\t\tjack.so" for i in os.listdir(f): with open(os.path.join(f, i)) as fd: x = fd.read() x = x.replace("USERNAME", user) x = x.replace("AUDIODRIVER", audiodriver) x = x.replace("PORT", str(port)) x = x.replace("DRIVERS", str(drivers)) with open(os.path.join(cnfdir, i), "w") as fd: fd.write(x) self.baresip = pexpect.spawn('baresip', ["-f", cnfdir]) super().__init__() self.daemon = True self.start() if block: self.wait_until_ready() # properties @property def call_established(self): return self.call_status == "ESTABLISHED" @property def call_status(self): return self._call_status or "DISCONNECTED" # actions def do_command(self, action): if self.ready: action = str(action) self.baresip.sendline(action) else: LOG.warning(action + " not executed!") LOG.exception("NOT READY! please wait") def login(self): LOG.info("Adding account: " + self.user) self.baresip.sendline("/uanew " + self._login) def call(self, number): LOG.info("Dialling: " + number) self.do_command("/dial " + number) def hang(self): if self.current_call: LOG.info("Hanging: " + self.current_call) self.do_command("/hangup") self.current_call = None self._call_status = None else: LOG.error("No active call to hang") def hold(self): if self.current_call: LOG.info("Holding: " + self.current_call) self.do_command("/hold") else: LOG.error("No active call to hold") def resume(self): if self.current_call: LOG.info("Resuming " + self.current_call) self.do_command("/resume") else: LOG.error("No active call to resume") def mute_mic(self): if not self.call_established: LOG.error("Can not mute microphone while not in a call") return if not self.mic_muted: LOG.info("Muting mic") self.do_command("/mute") else: LOG.info("Mic already muted") def unmute_mic(self): if not self.call_established: LOG.error("Can not unmute microphone while not in a call") return if self.mic_muted: LOG.info("Unmuting mic") self.do_command("/mute") else: LOG.info("Mic already unmuted") def accept_call(self): self.do_command("/accept") status = "ESTABLISHED" self.handle_call_status(status) self._call_status = status def list_calls(self): self.do_command("/listcalls") def check_call_status(self): self.do_command("/callstat") sleep(0.1) return self.call_status def quit(self): LOG.info("Exiting") if self.running: if self.current_call: self.hang() self.baresip.sendline("/quit") self.running = False self.current_call = None self._call_status = None self.abort = True def send_dtmf(self, number): from opentone import ToneGenerator number = str(number) for n in number: if n not in "0123456789": LOG.error("invalid dtmf tone") return LOG.info("Sending dtmf tones for " + number) dtmf = join(tempfile.gettempdir(), "DTMF" + number + ".wav") ToneGenerator().dtmf_to_wave(number, dtmf) self.send_audio(dtmf) def speak(self, speech): if not self.call_established: LOG.error("Speaking without an active call!") else: LOG.info("Sending TTS for " + speech) self.send_audio(self.tts.get_mp3(speech)) sleep(0.5) def send_audio(self, wav_file): if not self.call_established: LOG.error("Can't send audio without an active call!") return wav_file, duration = self.convert_audio(wav_file) # send audio stream LOG.info("transmitting audio") self.do_command("/ausrc aufile," + wav_file) # wait till playback ends sleep(duration - 0.5) # avoid baresip exiting self.do_command("/ausrc alsa,default") @staticmethod def convert_audio(input_file, outfile=None): from pydub import AudioSegment input_file = expanduser(input_file) sound = AudioSegment.from_file(input_file) sound += AudioSegment.silent(duration=500) # ensure minimum time # workaround baresip bug while sound.duration_seconds < 3: sound += AudioSegment.silent(duration=500) outfile = outfile or join(tempfile.gettempdir(), "pybaresip.wav") sound = sound.set_frame_rate(48000) sound = sound.set_channels(2) sound.export(outfile, format="wav") return outfile, sound.duration_seconds # this is played out loud over speakers def say(self, speech): if not self.call_established: LOG.warning("Speaking without an active call!") self.tts.say(speech, blocking=True) def play(self, audio_file, blocking=True): if not audio_file.endswith(".wav"): audio_file, duration = self.convert_audio(audio_file) self.audio = self._play_wav(audio_file, blocking=blocking) def stop_playing(self): if self.audio is not None: self.audio.kill() @staticmethod def _play_wav(wav_file, play_cmd="aplay %1", blocking=False): play_mp3_cmd = str(play_cmd).split(" ") for index, cmd in enumerate(play_mp3_cmd): if cmd == "%1": play_mp3_cmd[index] = wav_file if blocking: return subprocess.call(play_mp3_cmd) else: return subprocess.Popen(play_mp3_cmd) # events def handle_incoming_call(self, number): LOG.info("Incoming call: " + number) if self.call_established: LOG.info("already in a call, rejecting") sleep(0.1) self.do_command("b") else: LOG.info("default behaviour, rejecting call") sleep(0.1) self.do_command("b") def handle_call_rejected(self, number): LOG.info("Rejected incoming call: " + number) def handle_call_timestamp(self, timestr): LOG.info("Call time: " + timestr) def handle_call_status(self, status): if status != self._call_status: LOG.debug("Call Status: " + status) def handle_call_start(self): number = self.current_call LOG.info("Calling: " + number) def handle_call_ringing(self): number = self.current_call LOG.info(number + " is Ringing") def handle_call_established(self): LOG.info("Call established") def handle_call_ended(self, reason): LOG.info("Call ended") LOG.debug("Reason: " + reason) def _handle_no_accounts(self): LOG.debug("No accounts setup") self.login() def handle_login_success(self): LOG.info("Logged in!") def handle_login_failure(self): LOG.error("Log in failed!") self.quit() def handle_ready(self): LOG.info("Ready for instructions") def handle_mic_muted(self): LOG.info("Microphone muted") def handle_mic_unmuted(self): LOG.info("Microphone unmuted") def handle_audio_stream_failure(self): LOG.debug("Aborting call, maybe we reached voicemail?") self.hang() def handle_error(self, error): LOG.error(error) if error == "failed to set audio-source (No such device)": self.handle_audio_stream_failure() def onJackAssigned(self, cname): if not cname in self.jackNames: self.jackNames.append(cname) #We only have 2, if there are more, #it means the default has been reassigned if len(self.jackNames) > 2: self.jackNames.pop(0) def on_audio_rtp(self, rtp): pass # event loop def run(self): self.running = True while self.running: try: out = self.baresip.readline().decode("utf-8") if out != self._prev_output: out = out.strip() if self.debug: LOG.debug(out) if "baresip is ready." in out: self.handle_ready() elif "account: No SIP accounts found" in out: self._handle_no_accounts() elif "All 1 useragent registered successfully!" in out: self.ready = True self.handle_login_success() elif "ua: SIP register failed:" in out or\ "401 Unauthorized" in out or \ "Register: Destination address required" in out or\ "Register: Connection timed out" in out: self.handle_error(out) self.handle_login_failure() elif "Incoming call from: " in out: num = out.split("Incoming call from: ")[1].split( " - (press 'a' to accept)")[0].strip() self.current_call = num self._call_status = "INCOMING" self.handle_incoming_call(num) elif "call: rejecting incoming call from " in out: num = out.split("rejecting incoming call from " )[1].split(" ")[0].strip() self.handle_call_rejected(num) elif "call: SIP Progress: 180 Ringing" in out: self.handle_call_ringing() status = "RINGING" self.handle_call_status(status) self._call_status = status elif "call: connecting to " in out: n = out.split("call: connecting to '")[1].split("'")[0] self.current_call = n self.handle_call_start() status = "OUTGOING" self.handle_call_status(status) self._call_status = status elif "Call established:" in out: status = "ESTABLISHED" self.handle_call_status(status) self._call_status = status sleep(0.5) self.handle_call_established() elif "call: hold " in out: n = out.split("call: hold ")[1] status = "ON HOLD" self.handle_call_status(status) self._call_status = status elif "Call with " in out and \ "terminated (duration: " in out: status = "DISCONNECTED" duration = out.split("terminated (duration: ")[1][:-1] self.handle_call_status(status) self._call_status = status self.handle_call_timestamp(duration) self.mic_muted = False elif "call muted" in out: self.mic_muted = True self.handle_mic_muted() elif "call un-muted" in out: self.mic_muted = False self.handle_mic_unmuted() elif "session closed:" in out: reason = out.split("session closed:")[1].strip() status = "DISCONNECTED" self.handle_call_status(status) self._call_status = status self.handle_call_ended(reason) self.mic_muted = False elif "(no active calls)" in out: status = "DISCONNECTED" self.handle_call_status(status) self._call_status = status elif "incoming rtp for 'audio' established, receiving from " in out: rtp = out.split( "stream: incoming rtp for 'audio' established, receiving from " ) self.on_audio_rtp(rtp) elif "===== Call debug " in out: status = out.split("(")[1].split(")")[0] self.handle_call_status(status) self._call_status = status elif 'jack' in out: match = re.search( r"jack: unique name \`(.*?)\' assigned", out) if match: self.onJackAssigned(match.groups(1)[0]) elif "--- List of active calls (1): ---" in \ self._prev_output: if "ESTABLISHED" in out and self.current_call in out: ts = out.split("ESTABLISHED")[0].split( "[line 1]")[1].strip() if ts != self._ts: self._ts = ts self.handle_call_timestamp(ts) elif "failed to set audio-source (No such device)" in out: error = "failed to set audio-source (No such device)" self.handle_error(error) self._prev_output = out except pexpect.exceptions.EOF: # baresip exited self.quit() except pexpect.exceptions.TIMEOUT: # nothing happened for a while pass except Exception: print(traceback.format_exc()) def wait_until_ready(self): while not self.ready: sleep(0.1) if self.abort: return
from responsive_voice import ResponsiveVoice engine = ResponsiveVoice() engine.say("hello world") engine.say("hello world", gender="male", lang="en-GB", rate=0.4) file_path = engine.get_mp3(u"ola mundo", lang="pt-pt") engine.play_mp3(file_path)
from responsive_voice import ResponsiveVoice engine = ResponsiveVoice() engine.say("hello world") engine.say("hello world", gender=ResponsiveVoice.MALE, rate=0.45) engine = ResponsiveVoice(lang=ResponsiveVoice.PORTUGUESE_BR) file_path = engine.get_mp3(u"olá mundo") engine.play_mp3(file_path) from responsive_voice.voices import EnglishIndia, UKEnglishMale, \ FallbackUKEnglishMale india = EnglishIndia() uk = UKEnglishMale() uk2 = FallbackUKEnglishMale() india.say("hello world") uk.say("hello world") uk2.say("hello world")
class BareSIP(Thread): def __init__(self, user, pwd, gateway, tts=None, debug=False, block=True): self.debug = debug self.user = user self.pwd = pwd self.gateway = gateway if tts: self.tts = tts else: self.tts = ResponsiveVoice(gender=ResponsiveVoice.MALE) self._login = "******".format(u=self.user, p=self.pwd, g=self.gateway) self._prev_output = "" self.running = False self.ready = False self.mic_muted = False self.abort = False self.current_call = None self._call_status = None self.audio = None self._ts = None self.baresip = pexpect.spawn('baresip') super().__init__() self.start() if block: self.wait_until_ready() # properties @property def call_established(self): return self.call_status == "ESTABLISHED" @property def call_status(self): return self._call_status or "DISCONNECTED" # actions def do_command(self, action): if self.ready: action = str(action) self.baresip.sendline(action) else: LOG.warning(action + " not executed!") LOG.error("NOT READY! please wait") def login(self): LOG.info("Adding account: " + self.user) self.baresip.sendline("/uanew " + self._login) def call(self, number): LOG.info("Dialling: " + number) self.do_command("/dial " + number) def hang(self): if self.current_call: LOG.info("Hanging: " + self.current_call) self.do_command("/hangup") self.current_call = None self._call_status = None else: LOG.error("No active call to hang") def hold(self): if self.current_call: LOG.info("Holding: " + self.current_call) self.do_command("/hold") else: LOG.error("No active call to hold") def resume(self): if self.current_call: LOG.info("Resuming " + self.current_call) self.do_command("/resume") else: LOG.error("No active call to resume") def mute_mic(self): if not self.call_established: LOG.error("Can not mute microphone while not in a call") return if not self.mic_muted: LOG.info("Muting mic") self.do_command("/mute") else: LOG.info("Mic already muted") def unmute_mic(self): if not self.call_established: LOG.error("Can not unmute microphone while not in a call") return if self.mic_muted: LOG.info("Unmuting mic") self.do_command("/mute") else: LOG.info("Mic already unmuted") def accept_call(self): self.do_command("/accept") status = "ESTABLISHED" self.handle_call_status(status) self._call_status = status def list_calls(self): self.do_command("/listcalls") def check_call_status(self): self.do_command("/callstat") sleep(0.1) return self.call_status def quit(self): LOG.info("Exiting") if self.running: if self.current_call: self.hang() self.baresip.sendline("/quit") self.running = False self.current_call = None self._call_status = None self.abort = True def send_dtmf(self, number): number = str(number) for n in number: if n not in "0123456789": LOG.error("invalid dtmf tone") return LOG.info("Sending dtmf tones for " + number) dtmf = join(tempfile.gettempdir(), number + ".wav") ToneGenerator().dtmf_to_wave(number, dtmf) self.send_audio(dtmf) def speak(self, speech): if not self.call_established: LOG.error("Speaking without an active call!") else: LOG.info("Sending TTS for " + speech) self.send_audio(self.tts.get_mp3(speech)) sleep(0.5) def send_audio(self, wav_file): if not self.call_established: LOG.error("Can't send audio without an active call!") return wav_file, duration = self.convert_audio(wav_file) # send audio stream LOG.info("transmitting audio") self.do_command("/ausrc aufile," + wav_file) # wait till playback ends sleep(duration - 0.5) # avoid baresip exiting self.do_command("/ausrc alsa,default") @staticmethod def convert_audio(input_file, outfile=None): input_file = expanduser(input_file) sound = AudioSegment.from_file(input_file) sound += AudioSegment.silent(duration=500) # ensure minimum time # workaround baresip bug while sound.duration_seconds < 3: sound += AudioSegment.silent(duration=500) outfile = outfile or join(tempfile.gettempdir(), "pybaresip.wav") sound = sound.set_frame_rate(48000) sound = sound.set_channels(2) sound.export(outfile, format="wav") return outfile, sound.duration_seconds # this is played out loud over speakers def say(self, speech): if not self.call_established: LOG.warning("Speaking without an active call!") self.tts.say(speech, blocking=True) def play(self, audio_file, blocking=True): if not audio_file.endswith(".wav"): audio_file, duration = self.convert_audio(audio_file) self.audio = self._play_wav(audio_file, blocking=blocking) def stop_playing(self): if self.audio is not None: self.audio.kill() @staticmethod def _play_wav(wav_file, play_cmd="aplay %1", blocking=False): play_mp3_cmd = str(play_cmd).split(" ") for index, cmd in enumerate(play_mp3_cmd): if cmd == "%1": play_mp3_cmd[index] = wav_file if blocking: return subprocess.call(play_mp3_cmd) else: return subprocess.Popen(play_mp3_cmd) # events def handle_incoming_call(self, number): LOG.info("Incoming call: " + number) if self.call_established: LOG.info("already in a call, rejecting") sleep(0.1) self.do_command("b") else: LOG.info("default behaviour, rejecting call") sleep(0.1) self.do_command("b") def handle_call_rejected(self, number): LOG.info("Rejected incoming call: " + number) def handle_call_timestamp(self, timestr): LOG.info("Call time: " + timestr) def handle_call_status(self, status): if status != self._call_status: LOG.debug("Call Status: " + status) def handle_call_start(self): number = self.current_call LOG.info("Calling: " + number) def handle_call_ringing(self): number = self.current_call LOG.info(number + " is Ringing") def handle_call_established(self): LOG.info("Call established") def handle_call_ended(self, reason): LOG.info("Call ended") LOG.debug("Reason: " + reason) def _handle_no_accounts(self): LOG.debug("No accounts setup") self.login() def handle_login_success(self): LOG.info("Logged in!") def handle_login_failure(self): LOG.error("Log in failed!") self.quit() def handle_ready(self): LOG.info("Ready for instructions") def handle_mic_muted(self): LOG.info("Microphone muted") def handle_mic_unmuted(self): LOG.info("Microphone unmuted") def handle_audio_stream_failure(self): LOG.debug("Aborting call, maybe we reached voicemail?") self.hang() def handle_error(self, error): LOG.error(error) if error == "failed to set audio-source (No such device)": self.handle_audio_stream_failure() # event loop def run(self): self.running = True while self.running: try: out = self.baresip.readline().decode("utf-8") if out != self._prev_output: out = out.strip() if self.debug: LOG.debug(out) if "baresip is ready." in out: self.handle_ready() elif "account: No SIP accounts found" in out: self._handle_no_accounts() elif "All 1 useragent registered successfully!" in out: self.ready = True self.handle_login_success() elif "ua: SIP register failed:" in out or\ "401 Unauthorized" in out or \ "Register: Destination address required" in out or\ "Register: Connection timed out" in out: self.handle_error(out) self.handle_login_failure() elif "Incoming call from: " in out: num = out.split("Incoming call from: ")[1].split( " - (press 'a' to accept)")[0].strip() self.current_call = num self._call_status = "INCOMING" self.handle_incoming_call(num) elif "call: rejecting incoming call from " in out: num = out.split("rejecting incoming call from " )[1].split(" ")[0].strip() self.handle_call_rejected(num) elif "call: SIP Progress: 180 Ringing" in out: self.handle_call_ringing() status = "RINGING" self.handle_call_status(status) self._call_status = status elif "call: connecting to " in out: n = out.split("call: connecting to '")[1].split("'")[0] self.current_call = n self.handle_call_start() status = "OUTGOING" self.handle_call_status(status) self._call_status = status elif "Call established:" in out: status = "ESTABLISHED" self.handle_call_status(status) self._call_status = status sleep(0.5) self.handle_call_established() elif "call: hold " in out: n = out.split("call: hold ")[1] status = "ON HOLD" self.handle_call_status(status) self._call_status = status elif "Call with " in out and \ "terminated (duration: " in out: status = "DISCONNECTED" duration = out.split("terminated (duration: ")[1][:-1] self.handle_call_status(status) self._call_status = status self.handle_call_timestamp(duration) self.mic_muted = False elif "call muted" in out: self.mic_muted = True self.handle_mic_muted() elif "call un-muted" in out: self.mic_muted = False self.handle_mic_unmuted() elif "session closed:" in out: reason = out.split("session closed:")[1].strip() status = "DISCONNECTED" self.handle_call_status(status) self._call_status = status self.handle_call_ended(reason) self.mic_muted = False elif "(no active calls)" in out: status = "DISCONNECTED" self.handle_call_status(status) self._call_status = status elif "===== Call debug " in out: status = out.split("(")[1].split(")")[0] self.handle_call_status(status) self._call_status = status elif "--- List of active calls (1): ---" in \ self._prev_output: if "ESTABLISHED" in out and self.current_call in out: ts = out.split("ESTABLISHED")[0].split( "[line 1]")[1].strip() if ts != self._ts: self._ts = ts self.handle_call_timestamp(ts) elif "failed to set audio-source (No such device)" in out: error = "failed to set audio-source (No such device)" self.handle_error(error) self._prev_output = out except pexpect.exceptions.EOF: # baresip exited self.quit() except pexpect.exceptions.TIMEOUT: # nothing happened for a while pass def wait_until_ready(self): while not self.ready: sleep(0.1) if self.abort: return
class BareSIP(Thread): def __init__(self, user, pwd, gateway, tts=None, debug=False, block=True, config_path=None, sounds_path=None): config_path = config_path or join("~", ".baresipy") self.config_path = expanduser(config_path) if not isdir(self.config_path): makedirs(self.config_path) if isfile(join(self.config_path, "config")): with open(join(self.config_path, "config"), "r") as f: self.config = f.read() LOG.info("config loaded from " + self.config_path + "/config") self.updated_config = False else: self.config = baresipy.config.DEFAULT self.updated_config = True self._original_config = str(self.config) if sounds_path is not None and "#audio_path" in self.config: self.updated_config = True if sounds_path is False: # sounds disabled self.config = self.config.replace( "#audio_path /usr/share/baresip", "audio_path /dont/load") elif isdir(sounds_path): self.config = self.config.replace( "#audio_path /usr/share/baresip", "audio_path " + sounds_path) if self.updated_config: with open(join(self.config_path, "config.bak"), "w") as f: f.write(self._original_config) LOG.info("saving config") with open(join(self.config_path, "config"), "w") as f: f.write(self.config) self.debug = debug self.user = user self.pwd = pwd self.gateway = gateway if tts: self.tts = tts else: self.tts = ResponsiveVoice(gender=ResponsiveVoice.MALE) self._login = "******".format(u=self.user, p=self.pwd, g=self.gateway) self._prev_output = "" self.running = False self.ready = False self.mic_muted = False self.abort = False self.current_call = None self._call_status = None self.audio = None self._ts = None self.baresip = pexpect.spawn('baresip -f ' + self.config_path) super().__init__() self.start() if block: self.wait_until_ready() # properties @property def call_established(self): return self.call_status == "ESTABLISHED" @property def call_status(self): return self._call_status or "DISCONNECTED" # actions def do_command(self, action): if self.ready: action = str(action) self.baresip.sendline(action) else: LOG.warning(action + " not executed!") LOG.error("NOT READY! please wait") def login(self): LOG.info("Adding account: " + self.user) self.baresip.sendline("/uanew " + self._login) def call(self, number): LOG.info("Dialling: " + number) self.do_command("/dial " + number) def enable_recording(self): LOG.info("Enabling call recording") self.do_command("/insmod sndfile") def hang(self): if self.current_call: LOG.info("Hanging: " + self.current_call) self.do_command("/hangup") self.current_call = None self._call_status = None else: LOG.error("No active call to hang") def hold(self): if self.current_call: LOG.info("Holding: " + self.current_call) self.do_command("/hold") else: LOG.error("No active call to hold") def resume(self): if self.current_call: LOG.info("Resuming " + self.current_call) self.do_command("/resume") else: LOG.error("No active call to resume") def mute_mic(self): if not self.call_established: LOG.error("Can not mute microphone while not in a call") return if not self.mic_muted: LOG.info("Muting mic") self.do_command("/mute") else: LOG.info("Mic already muted") def unmute_mic(self): if not self.call_established: LOG.error("Can not unmute microphone while not in a call") return if self.mic_muted: LOG.info("Unmuting mic") self.do_command("/mute") else: LOG.info("Mic already unmuted") def accept_call(self): self.do_command("/accept") status = "ESTABLISHED" self.handle_call_status(status) self._call_status = status def list_calls(self): self.do_command("/listcalls") def check_call_status(self): self.do_command("/callstat") sleep(0.1) return self.call_status @staticmethod def get_contacts(config_dir=join(expanduser("~"), ".baresip")): """ Read contacts and active_contact from config dir :param config_dir: baresip configration directory (default ~/.baresip) :return: (list) all_contacts(dict), current_contact(str) """ with open(f"{config_dir}/current_contact") as cur: cur_con = cur.readline() with open(f"{config_dir}/contacts") as cons: raw = cons.readlines() contacts_list = [] for line in raw: if not line.startswith("#") and line != "\n": contacts_list.append(line) contacts = {} for contact in contacts_list: name = contact.split('"', 2)[1] addr = contact.split('<', 1)[1].split('>', 1)[0] contacts[name] = addr return contacts, cur_con def quit(self): if self.updated_config: LOG.info("restoring original config") with open(join(self.config_path, "config"), "w") as f: f.write(self._original_config) LOG.info("Exiting") if self.running: if self.current_call: self.hang() self.baresip.sendline("/quit") self.running = False self.current_call = None self._call_status = None self.abort = True self.baresip.close() self.baresip.kill(signal.SIGKILL) def send_dtmf(self, number): number = str(number) for n in number: if n not in "0123456789": LOG.error("invalid dtmf tone") return LOG.info("Sending dtmf tones for " + number) dtmf = join(tempfile.gettempdir(), number + ".wav") ToneGenerator().dtmf_to_wave(number, dtmf) self.send_audio(dtmf) def speak(self, speech): if not self.call_established: LOG.error("Speaking without an active call!") else: LOG.info("Sending TTS for " + speech) self.send_audio(self.tts.get_mp3(speech)) sleep(0.5) def send_audio(self, wav_file): if not self.call_established: LOG.error("Can't send audio without an active call!") return wav_file, duration = self.convert_audio(wav_file) # send audio stream LOG.info("transmitting audio") self.do_command("/ausrc aufile," + wav_file) # wait till playback ends sleep(duration - 0.5) # avoid baresip exiting self.do_command("/ausrc alsa,default") @staticmethod def convert_audio(input_file, outfile=None): input_file = expanduser(input_file) sound = AudioSegment.from_file(input_file) sound += AudioSegment.silent(duration=500) # ensure minimum time # workaround baresip bug while sound.duration_seconds < 3: sound += AudioSegment.silent(duration=500) outfile = outfile or join(tempfile.gettempdir(), "pybaresip.wav") sound = sound.set_frame_rate(48000) sound = sound.set_channels(2) sound.export(outfile, format="wav") return outfile, sound.duration_seconds # this is played out loud over speakers def say(self, speech): if not self.call_established: LOG.warning("Speaking without an active call!") self.tts.say(speech, blocking=True) def play(self, audio_file, blocking=True): if not audio_file.endswith(".wav"): audio_file, duration = self.convert_audio(audio_file) self.audio = self._play_wav(audio_file, blocking=blocking) def stop_playing(self): if self.audio is not None: self.audio.kill() @staticmethod def _play_wav(wav_file, play_cmd="aplay %1", blocking=False): play_mp3_cmd = str(play_cmd).split(" ") for index, cmd in enumerate(play_mp3_cmd): if cmd == "%1": play_mp3_cmd[index] = wav_file if blocking: return subprocess.call(play_mp3_cmd) else: return subprocess.Popen(play_mp3_cmd) # events def handle_incoming_call(self, number): LOG.info("Incoming call: " + number) if self.call_established: LOG.info("already in a call, rejecting") sleep(0.1) self.do_command("b") else: LOG.info("default behaviour, rejecting call") sleep(0.1) self.do_command("b") def handle_call_rejected(self, number): LOG.info("Rejected incoming call: " + number) def handle_call_timestamp(self, timestr): LOG.info("Call time: " + timestr) def handle_call_status(self, status): if status != self._call_status: LOG.debug("Call Status: " + status) @staticmethod def handle_text_message(sender, text): LOG.info(f"rec'd: {text} | from: {sender}") def handle_call_start(self): number = self.current_call LOG.info("Calling: " + number) def handle_call_ringing(self): number = self.current_call LOG.info(number + " is Ringing") def handle_call_established(self): LOG.info("Call established") def handle_call_ended(self, reason): LOG.info("Call ended") LOG.debug("Reason: " + reason) def _handle_no_accounts(self): LOG.debug("No accounts setup") self.login() def handle_login_success(self): LOG.info("Logged in!") def handle_login_failure(self): LOG.error("Log in failed!") self.quit() def handle_ready(self): LOG.info("Ready for instructions") def handle_mic_muted(self): LOG.info("Microphone muted") def handle_mic_unmuted(self): LOG.info("Microphone unmuted") def handle_audio_stream_failure(self): LOG.debug("Aborting call, maybe we reached voicemail?") self.hang() def handle_error(self, error): LOG.error(error) if error == "failed to set audio-source (No such device)": self.handle_audio_stream_failure() # event loop def run(self): self.running = True while self.running: try: out = self.baresip.readline().decode("utf-8") if out != self._prev_output: out = out.strip() if self.debug: LOG.debug(out) if "baresip is ready." in out: self.handle_ready() elif "account: No SIP accounts found" in out: self._handle_no_accounts() elif "All 1 useragent registered successfully!" in out: self.ready = True self.handle_login_success() elif "ua: SIP register failed:" in out or\ "401 Unauthorized" in out or \ "Register: Destination address required" in out or\ "Register: Connection timed out" in out or\ "403 Wrong Password" in out or\ "400 Bad From URI" in out: self.handle_error(out) self.handle_login_failure() elif "Incoming call from: " in out: num = out.split("Incoming call from: ")[1].split( " - (press 'a' to accept)")[0].strip() self.current_call = num self._call_status = "INCOMING" self.handle_incoming_call(num) elif "call: rejecting incoming call from " in out: num = out.split("rejecting incoming call from " )[1].split(" ")[0].strip() self.handle_call_rejected(num) elif "call: SIP Progress: 180 Ringing" in out: self.handle_call_ringing() status = "RINGING" self.handle_call_status(status) self._call_status = status elif "call: connecting to " in out: n = out.split("call: connecting to '")[1].split("'")[0] self.current_call = n self.handle_call_start() status = "OUTGOING" self.handle_call_status(status) self._call_status = status elif "Call established:" in out: status = "ESTABLISHED" self.handle_call_status(status) self._call_status = status sleep(0.5) self.handle_call_established() elif "call: hold " in out: n = out.split("call: hold ")[1] status = "ON HOLD" self.handle_call_status(status) self._call_status = status elif "Call with " in out and \ "terminated (duration: " in out: status = "DISCONNECTED" duration = out.split("terminated (duration: ")[1][:-1] self.handle_call_status(status) self._call_status = status self.handle_call_timestamp(duration) self.mic_muted = False elif "call muted" in out: self.mic_muted = True self.handle_mic_muted() elif "call un-muted" in out: self.mic_muted = False self.handle_mic_unmuted() elif "session closed:" in out: reason = out.split("session closed:")[1].strip() status = "DISCONNECTED" self.handle_call_status(status) self._call_status = status self.handle_call_ended(reason) self.mic_muted = False elif "(no active calls)" in out: status = "DISCONNECTED" self.handle_call_status(status) self._call_status = status elif "===== Call debug " in out: status = out.split("(")[1].split(")")[0] self.handle_call_status(status) self._call_status = status elif "--- List of active calls (1): ---" in \ self._prev_output: if "ESTABLISHED" in out and self.current_call in out: ts = out.split("ESTABLISHED")[0].split( "[line 1]")[1].strip() if ts != self._ts: self._ts = ts self.handle_call_timestamp(ts) elif "failed to set audio-source (No such device)" in out: error = "failed to set audio-source (No such device)" self.handle_error(error) elif "terminated by signal" in out or "ua: stop all" in out: self.running = False elif str(out).startswith("sip:") and str(out).endswith( '"'): _, sender, message = str(out).split(':', 2) sender = f"sip:{sender}" message = message.strip().strip('"') self.handle_text_message(sender, message) self._prev_output = out except pexpect.exceptions.EOF: # baresip exited self.running = False except pexpect.exceptions.TIMEOUT: # nothing happened for a while pass except KeyboardInterrupt: self.running = False self.quit() def wait_until_ready(self): while not self.ready: sleep(0.1) if self.abort: return
class Ui(QtWidgets.QMainWindow): def __init__(self): super(Ui, self).__init__() uic.loadUi('iwsss.ui', self) self.setDefaultUi() self.show() def setDefaultUi(self): self.version = self.findChild(QtWidgets.QLabel, 'version') self.version.setText("Mary 1.0.04") self.micButton = self.findChild(QtWidgets.QPushButton, 'micButton') self.Kelime_Label = self.findChild(QtWidgets.QLabel, 'Kelime_Label') self.Yanit_Label = self.findChild(QtWidgets.QLabel, 'Yanit_Label') self.Tip_Label = self.findChild(QtWidgets.QLabel, 'Tip_Label') self.Yanit_Layout = self.findChild(QtWidgets.QLayout, 'horizontalLayout_2') self.Image_Label = self.findChild(QtWidgets.QLabel, 'Image_Label') self.sitelink1 = self.findChild(QtWidgets.QLabel, 'sitelink1') self.sitelink2 = self.findChild(QtWidgets.QLabel, 'sitelink2') self.sitelink3 = self.findChild(QtWidgets.QLabel, 'sitelink3') self.sitebaslik1 = self.findChild(QtWidgets.QLabel, 'sitebaslik1') self.sitebaslik2 = self.findChild(QtWidgets.QLabel, 'sitebaslik2') self.sitebaslik3 = self.findChild(QtWidgets.QLabel, 'sitebaslik3') self.sitebaslik1.setOpenExternalLinks(True) self.sitebaslik2.setOpenExternalLinks(True) self.sitebaslik3.setOpenExternalLinks(True) self.siteaciklama1 = self.findChild(QtWidgets.QLabel, 'siteaciklama1') self.siteaciklama2 = self.findChild(QtWidgets.QLabel, 'siteaciklama2') self.siteaciklama3 = self.findChild(QtWidgets.QLabel, 'siteaciklama3') self.web_sonuc1 = self.findChild(QtWidgets.QLayout, 'web_sonuc1') self.web_sonuc2 = self.findChild(QtWidgets.QLayout, 'web_sonuc2') self.web_sonuc3 = self.findChild(QtWidgets.QLayout, 'web_sonuc3') self.web_sonuc1.setSpacing(0) self.web_sonuc2.setSpacing(0) self.web_sonuc3.setSpacing(0) self.web_sonuc1.setContentsMargins(0, 0, 0, 0) self.web_sonuc2.setContentsMargins(0, 0, 0, 0) self.web_sonuc3.setContentsMargins(0, 0, 0, 0) self.micButton.clicked.connect(self.micButtonPressed) self.setWindowIcon(QtGui.QIcon('image/mary.png')) self.animasyon = False self.ttsIptal = False self.listenAktif = False self.micButtonClickable = True self.Image_Label.show() self.aktifThreadler = 0 self.dosyakonumu = getcwd() self.soundPlayer = QtMultimedia.QMediaPlayer() self.soundPlayer.stateChanged.connect(self.soundPlayerState) #Tray icon self.tray_icon = QtWidgets.QSystemTrayIcon(self) self.tray_icon.setIcon(QtGui.QIcon("image/mary.png")) self.tray_icon.activated.connect(self.trayDoubleClick) show_action = QtWidgets.QAction("Göster", self) quit_action = QtWidgets.QAction("Çıkış yap", self) show_action.triggered.connect(self.show) quit_action.triggered.connect(self.closeApp) tray_menu = QtWidgets.QMenu() tray_menu.addAction(show_action) tray_menu.addAction(quit_action) self.tray_icon.setContextMenu(tray_menu) self.tray_icon.show() for i in range(10): self.dosyakonumu = self.dosyakonumu.replace("\\", "/") i = "border-image: url('{}/image/background.png');".format( self.dosyakonumu) stylesheet = "#centralwidget{" + i + "}" self.setStyleSheet(stylesheet) self.micButton.setStyleSheet( "border-image: url('{}/image/mic_1.png');".format( self.dosyakonumu)) self.yapilanislem = "" self.backgroundListen = True self.voiceEngine = ResponsiveVoice(lang=ResponsiveVoice.TURKISH, gender=ResponsiveVoice.FEMALE, pitch=0.52, rate=0.53, key="8FCWWns8", vol=0.97) threading.Thread(target=self.sesanimasyon, daemon=True).start() threading.Thread(target=self.background, daemon=True).start() self.db = Veritabani() if self.db.ad() == "": self.yapilanislem = "ilkacilis" threading.Thread(target=self.ilkCalistirma, daemon=True).start() def closeApp(self): self.hide() sys.exit() def closeEvent(self, event): event.ignore() self.hide() self.tray_icon.showMessage("Mary - Sesli asistan", "Sesli asistan simge durumuna küçültüldü", QtWidgets.QSystemTrayIcon.Information, 2000) def trayDoubleClick(self, reason): if reason == QtWidgets.QSystemTrayIcon.DoubleClick: self.show() def backgroundCallBack(self, audio): if self.backgroundListen: try: text = r.recognize_google(audio, language='tr-tr') liste = [ "Merih", "Melih", "Meri", "Mery", "MARIO", "Mary", "emery", "h&m" ] for i in liste: if i in text: text = i.replace(i, "Mary") if "MARY" in text.upper(): self.notification = QtCore.QUrl.fromLocalFile( "{}/notification.mp3".format(self.dosyakonumu)) self.soundPlayer.setMedia( QtMultimedia.QMediaContent(self.notification)) self.soundPlayer.play() self.listenAktif = True self.ttsIptal = True self.animasyon = True self.backgroundListen = False self.Tip_Label.setText("Dinleniyor") self.micButton.setStyleSheet( "border-image: url('{}/image/mic_2.png');".format( self.dosyakonumu)) self.Tip_Label.setStyleSheet( "background-color:#ff0000;color: rgb(255, 255, 255);") self.show() self.dinle = listenThread(parent=window) self.dinle.start() self.dinle.signal.connect(self.setUi) self.backgroundListen = False else: print(text) except sr.UnknownValueError: pass except sr.RequestError: pass else: print(text) SystemExit def background(self): while True: try: with sr.Microphone() as source: audio = r.listen(source, timeout=2, phrase_time_limit=2) threading.Thread(target=self.backgroundCallBack, args={audio}).start() except sr.WaitTimeoutError: pass def ilkCalistirma(self): self.micButton.setMaximumSize(0, 0) self.micButton.setMinimumSize(0, 0) self.backgroundListen = False self.Tip_Label.setText("") threading.Thread( target=self.setYanitLabel, args={"Hoşgeldin, Adım Mary. Ben senin sesli asistanınım."}, daemon=True).start() self.voiceEngine.say( "Hoşgeldin, Adım Mary.\nBen senin sesli asistanınım.") threading.Thread( target=self.setYanitLabel, args={ "Öncelikle adını öğrenebilir miyim?\nAdını söylemek için lütfen butona tıkla" }, daemon=True).start() self.voiceEngine.say( "Öncelikle adını öğrenebilir miyim? Adını söylemek için lütfen butona tıkla" ) self.micButton.setMaximumSize(80, 80) self.micButton.setMinimumSize(80, 80) self.Tip_Label.setText("Konuşmak için butona tıklayın") while True: time.sleep(0.1) if self.db.ad() != "": time.sleep(5) self.setYanitLabel("") self.Image_Label.show() self.Image_Label.setStyleSheet( "border-image: url('{}/image/neler_yapabilirsin.png');". format(self.dosyakonumu)) self.Image_Label.setMaximumSize(630, 270) self.Image_Label.setMinimumSize(630, 270) self.voiceEngine.say( f"Yapabileceklerimin bazıları şunlar {self.db.ad()}. Şimdi başlayabilirsin" ) break sys.exit() def micButtonPressed(self): print("micButton Basıldı") if self.listenAktif: self.micButtonClickable = False self.micButton.setEnabled(False) self.ttsIptal = True self.listenAktif = False self.animasyon = False self.Tip_Label.setText("Konuşmak için butona tıklayın") self.Tip_Label.setStyleSheet("color: rgb(255, 255, 255);") self.micButton.setStyleSheet( "border-image: url('{}/image/mic_1.png');".format( self.dosyakonumu)) def i(): time.sleep(2) self.micButtonClickable = True self.micButton.setEnabled(True) self.backgroundListen = True SystemExit threading.Thread(target=i, daemon=True).start() else: if self.micButtonClickable: self.listenAktif = True self.ttsIptal = True self.animasyon = True self.backgroundListen = False self.Tip_Label.setText("Dinleniyor") self.micButton.setStyleSheet( "border-image: url('{}/image/mic_2.png');".format( self.dosyakonumu)) self.Tip_Label.setStyleSheet( "background-color:#ff0000;color: rgb(255, 255, 255);") self.dinle = listenThread(parent=window) self.dinle.start() self.dinle.signal.connect(self.setUi) @staticmethod def sesanimasyon(): def sound(indata, outdata, frames, time, status): volume_norm = norm(indata) * 20 if window.animasyon: window.Tip_Label.setMinimumSize(70 + volume_norm, 0) while True: with sd.Stream(callback=sound): sd.sleep(1000000) def setYanitLabel(self, yazi, foto=False): self.Yanit_Label.setWordWrap(False) QtCore.QMetaObject.invokeMethod(self.Yanit_Label, "setText", QtCore.Qt.QueuedConnection, QtCore.Q_ARG(str, yazi)) yaziBoyutu = QtGui.QFont() yaziBoyutu.setPointSize(22) if foto: self.Yanit_Label.setWordWrap(True) self.Yanit_Label.setAlignment(QtCore.Qt.AlignLeft) else: self.Yanit_Label.setAlignment(QtCore.Qt.AlignCenter) if len(yazi) <= 40: print("40'dan küçük") yaziBoyutu.setPointSize(22) self.Yanit_Label.setMaximumSize(1000, 99999) elif len(yazi) >= 40 and len(yazi) <= 50: print("40-50") yaziBoyutu.setPointSize(20) elif len(yazi) >= 50 and len(yazi) <= 60: print("50-60") yaziBoyutu.setPointSize(18) elif len(yazi) >= 60 and len(yazi) <= 70: print("60-70") yaziBoyutu.setPointSize(17) if foto: self.Yanit_Label.setMaximumSize(600, 99999) else: self.Yanit_Label.setMaximumSize(800, 99999) elif len(yazi) >= 70 and len(yazi) <= 130: print("70-130") if foto: self.Yanit_Label.setMaximumSize(800, 99999) self.Yanit_Label.setWordWrap(True) yaziBoyutu.setPointSize(18) else: self.Yanit_Label.setMaximumSize(1000, 99999) self.Yanit_Label.setWordWrap(True) yaziBoyutu.setPointSize(18) else: print(len(yazi)) print("130 üstü") yaziBoyutu.setPointSize(14) if foto: self.Yanit_Label.setMaximumSize(400, 99999) self.Yanit_Label.setWordWrap(True) else: self.Yanit_Label.setMaximumSize(600, 99999) self.Yanit_Label.setWordWrap(True) self.Yanit_Label.setFont(yaziBoyutu) def labelClear(self): self.siteaciklama1.setText("") self.siteaciklama2.setText("") self.siteaciklama3.setText("") self.sitebaslik1.setText("") self.sitebaslik2.setText("") self.sitebaslik3.setText("") self.sitelink1.setText("") self.sitelink2.setText("") self.sitelink3.setText("") self.siteaciklama1.hide() self.siteaciklama2.hide() self.siteaciklama3.hide() self.sitebaslik1.hide() self.sitebaslik2.hide() self.sitebaslik3.hide() self.sitelink1.hide() self.sitelink2.hide() self.sitelink3.hide() self.web_sonuc1.setSpacing(0) self.web_sonuc2.setSpacing(0) self.web_sonuc3.setSpacing(0) self.web_sonuc1.setContentsMargins(0, 0, 0, 0) self.web_sonuc2.setContentsMargins(0, 0, 0, 0) self.web_sonuc3.setContentsMargins(0, 0, 0, 0) self.Image_Label.setAlignment(QtCore.Qt.AlignLeft) def setUi(self, komut): self.labelClear() if komut.yapilanislem == "neyapabilirsin": self.Image_Label.show() self.Image_Label.setStyleSheet( "border-image: url('{}/image/neler_yapabilirsin.png');".format( self.dosyakonumu)) self.Yanit_Label.setText("") self.Image_Label.setMinimumSize(630, 270) self.Image_Label.setMinimumSize(630, 270) self.Yanit_Layout.setSpacing(0) self.yapilanislem = "" elif komut.yapilanislem == "websiteSonuc": self.Image_Label.hide() self.sitebaslik1.setCursor(QtCore.Qt.PointingHandCursor) self.sitebaslik2.setCursor(QtCore.Qt.PointingHandCursor) self.sitebaslik3.setCursor(QtCore.Qt.PointingHandCursor) self.web_sonuc1.setContentsMargins(0, 15, 0, 15) self.web_sonuc2.setContentsMargins(0, 0, 0, 15) self.web_sonuc3.setContentsMargins(0, 0, 0, 15) self.web_sonuc1.setSpacing(3) self.web_sonuc2.setSpacing(3) self.web_sonuc3.setSpacing(3) self.siteaciklama1.show() self.siteaciklama2.show() self.siteaciklama3.show() self.sitebaslik1.show() self.sitebaslik2.show() self.sitebaslik3.show() self.sitelink1.show() self.sitelink2.show() self.sitelink3.show() self.Yanit_Label.setText("") self.sitelink1.setText(komut.linktext1) self.sitelink2.setText(komut.linktext2) self.sitelink3.setText(komut.linktext3) QtCore.QMetaObject.invokeMethod( self.sitebaslik1, "setText", QtCore.Qt.QueuedConnection, QtCore.Q_ARG( str, "<a href='{}'><font color=white>{}</font></a>".format( komut.link1, komut.linktext1))) QtCore.QMetaObject.invokeMethod( self.sitebaslik2, "setText", QtCore.Qt.QueuedConnection, QtCore.Q_ARG( str, "<a href='{}'><font color=white>{}</font></a>".format( komut.link2, komut.linktext2))) QtCore.QMetaObject.invokeMethod( self.sitebaslik3, "setText", QtCore.Qt.QueuedConnection, QtCore.Q_ARG( str, "<a href='{}'><font color=white>{}</font></a>".format( komut.link3, komut.linktext3))) self.siteaciklama1.setText(komut.aciklama1) self.siteaciklama2.setText(komut.aciklama2) self.siteaciklama3.setText(komut.aciklama3) self.yapilanislem = "" elif komut.foto: self.Image_Label.show() self.Image_Label.setStyleSheet( "border-image: url('{}/image/image.jpg');".format( self.dosyakonumu)) self.Image_Label.setMinimumSize(komut.width, komut.height) self.Image_Label.setMaximumSize(komut.width, komut.height) if komut.yapilanislem == "havadurumu": self.Yanit_Label.setAlignment(QtCore.Qt.AlignLeft) QtCore.QMetaObject.invokeMethod( self.Yanit_Label, "setText", QtCore.Qt.QueuedConnection, QtCore.Q_ARG(str, komut.labelText)) self.Yanit_Layout.setSpacing(15) self.Image_Label.setAlignment(QtCore.Qt.AlignBottom) self.sitebaslik1.setText(komut.detay1) self.sitebaslik2.setText(komut.detay2) self.sitebaslik3.setText(komut.detay3) self.sitebaslik1.show() self.sitebaslik2.show() self.sitebaslik3.show() self.sitebaslik1.setCursor(QtCore.Qt.ArrowCursor) self.sitebaslik2.setCursor(QtCore.Qt.ArrowCursor) self.sitebaslik3.setCursor(QtCore.Qt.ArrowCursor) self.yapilanislem = "" else: self.Yanit_Layout.setSpacing(30) self.setYanitLabel(komut.labelText, foto=True) else: self.Image_Label.setStyleSheet("") self.Image_Label.setMinimumSize(0, 0) self.Image_Label.setMaximumSize(0, 0) self.Image_Label.hide() self.Yanit_Layout.setSpacing(6) self.setYanitLabel(komut.labelText) def soundPlayerState(self, state): if state == QtMultimedia.QMediaPlayer.PlayingState: pass elif state == QtMultimedia.QMediaPlayer.StoppedState: try: remove(self.soundPath) print("Text to speech bitti") self.ttsIptal = True except Exception as code: pass