From 18acec1a8144c535b6435aaceca65420da1ba27c Mon Sep 17 00:00:00 2001 From: Minamiyama Date: Sun, 24 Aug 2025 11:31:30 +0800 Subject: [PATCH 01/35] =?UTF-8?q?refactor(audio):=20=E5=B0=86=E6=B5=81?= =?UTF-8?q?=E5=BC=8F=E9=9F=B3=E9=A2=91=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=8F=90=E5=8F=96=E4=B8=BA=E7=8B=AC=E7=AB=8B=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将原本内联在test_page.html中的流式音频处理逻辑重构为独立的StreamingContext类,提高代码可维护性和复用性 --- .../test/js/StreamingContext.js | 149 ++++++++++++++++++ main/xiaozhi-server/test/test_page.html | 131 +-------------- 2 files changed, 152 insertions(+), 128 deletions(-) create mode 100644 main/xiaozhi-server/test/js/StreamingContext.js diff --git a/main/xiaozhi-server/test/js/StreamingContext.js b/main/xiaozhi-server/test/js/StreamingContext.js new file mode 100644 index 00000000..576d9761 --- /dev/null +++ b/main/xiaozhi-server/test/js/StreamingContext.js @@ -0,0 +1,149 @@ +import BlockingQueue from './utils/BlockingQueue.js'; +import { log } from './utils/logger.js'; + +// 音频流播放上下文类 +export class StreamingContext { + constructor(opusDecoder, audioContext, sampleRate, channels, minAudioDuration) { + this.opusDecoder = opusDecoder; + this.audioContext = audioContext; + + // 音频参数 + this.sampleRate = sampleRate; + this.channels = channels; + this.minAudioDuration = minAudioDuration; + + // 初始化队列和状态 + this.queue = []; // 已解码的PCM队列。正在播放 + this.activeQueue = new BlockingQueue(); // 已解码的PCM队列。准备播放 + this.pendingAudioBufferQueue = []; // 待处理的缓存队列 + this.audioBufferQueue = new BlockingQueue(); // 缓存队列 + this.playing = false; // 是否正在播放 + this.endOfStream = false; // 是否收到结束信号 + this.source = null; // 当前音频源 + this.totalSamples = 0; // 累积的总样本数 + this.lastPlayTime = 0; // 上次播放的时间戳 + } + + // 缓存音频数组 + pushAudioBuffer(item) { + this.audioBufferQueue.enqueue(...item); + } + + // 获取需要处理缓存队列,单线程:在audioBufferQueue一直更新的状态下不会出现安全问题 + async getPendingAudioBufferQueue() { + // 原子交换 + 清空 + [this.pendingAudioBufferQueue, this.audioBufferQueue] = [await this.audioBufferQueue.dequeue(), new BlockingQueue()]; + } + + // 获取正在播放已解码的PCM队列,单线程:在activeQueue一直更新的状态下不会出现安全问题 + async getQueue(minSamples) { + let TepArray = []; + const num = minSamples - this.queue.length > 0 ? minSamples - this.queue.length : 1; + // 原子交换 + 清空 + [TepArray, this.activeQueue] = [await this.activeQueue.dequeue(num), new BlockingQueue()]; + this.queue.push(...TepArray); + } + + // 将Int16音频数据转换为Float32音频数据 + convertInt16ToFloat32(int16Data) { + const float32Data = new Float32Array(int16Data.length); + for (let i = 0; i < int16Data.length; i++) { + // 将[-32768,32767]范围转换为[-1,1] + float32Data[i] = int16Data[i] / (int16Data[i] < 0 ? 0x8000 : 0x7FFF); + } + return float32Data; + } + + // 将Opus数据解码为PCM + async decodeOpusFrames() { + if (!this.opusDecoder) { + log('Opus解码器未初始化,无法解码', 'error'); + return; + } else { + log('Opus解码器启动', 'info'); + } + + while (true) { + let decodedSamples = []; + for (const frame of this.pendingAudioBufferQueue) { + try { + // 使用Opus解码器解码 + const frameData = this.opusDecoder.decode(frame); + if (frameData && frameData.length > 0) { + // 转换为Float32 + const floatData = this.convertInt16ToFloat32(frameData); + // 使用循环替代展开运算符 + for (let i = 0; i < floatData.length; i++) { + decodedSamples.push(floatData[i]); + } + } + } catch (error) { + log("Opus解码失败: " + error.message, 'error'); + } + } + + if (decodedSamples.length > 0) { + // 使用循环替代展开运算符 + for (let i = 0; i < decodedSamples.length; i++) { + this.activeQueue.enqueue(decodedSamples[i]); + } + this.totalSamples += decodedSamples.length; + } else { + log('没有成功解码的样本', 'warning'); + } + await this.getPendingAudioBufferQueue(); + } + } + + // 开始播放音频 + async startPlaying() { + while (true) { + // 如果累积了至少0.3秒的音频,开始播放 + const minSamples = this.sampleRate * this.minAudioDuration * 3; + if (!this.playing && this.queue.length < minSamples) { + await this.getQueue(minSamples); + } + this.playing = true; + while (this.playing && this.queue.length) { + // 创建新的音频缓冲区 + const minPlaySamples = Math.min(this.queue.length, this.sampleRate); + const currentSamples = this.queue.splice(0, minPlaySamples); + + const audioBuffer = this.audioContext.createBuffer(this.channels, currentSamples.length, this.sampleRate); + audioBuffer.copyToChannel(new Float32Array(currentSamples), 0); + + // 创建音频源 + this.source = this.audioContext.createBufferSource(); + this.source.buffer = audioBuffer; + + // 创建增益节点用于平滑过渡 + const gainNode = this.audioContext.createGain(); + + // 应用淡入淡出效果避免爆音 + const fadeDuration = 0.02; // 20毫秒 + gainNode.gain.setValueAtTime(0, this.audioContext.currentTime); + gainNode.gain.linearRampToValueAtTime(1, this.audioContext.currentTime + fadeDuration); + + const duration = audioBuffer.duration; + if (duration > fadeDuration * 2) { + gainNode.gain.setValueAtTime(1, this.audioContext.currentTime + duration - fadeDuration); + gainNode.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + duration); + } + + // 连接节点并开始播放 + this.source.connect(gainNode); + gainNode.connect(this.audioContext.destination); + + this.lastPlayTime = this.audioContext.currentTime; + log(`开始播放 ${currentSamples.length} 个样本,约 ${(currentSamples.length / this.sampleRate).toFixed(2)} 秒`, 'info'); + this.source.start(); + } + await this.getQueue(minSamples); + } + } +} + +// 创建streamingContext实例的工厂函数 +export function createStreamingContext(opusDecoder, audioContext, sampleRate, channels, minAudioDuration) { + return new StreamingContext(opusDecoder, audioContext, sampleRate, channels, minAudioDuration); +} \ No newline at end of file diff --git a/main/xiaozhi-server/test/test_page.html b/main/xiaozhi-server/test/test_page.html index 983d3b9a..547f2b31 100644 --- a/main/xiaozhi-server/test/test_page.html +++ b/main/xiaozhi-server/test/test_page.html @@ -181,6 +181,7 @@ import { checkOpusLoaded, initOpusEncoder } from './js/opus.js'; import { addMessage } from './js/document.js' import BlockingQueue from './js/utils/BlockingQueue.js' + import { createStreamingContext } from './js/StreamingContext.js' // 需要加载的脚本列表 - 移除Opus依赖 const scriptFiles = []; @@ -336,125 +337,7 @@ // 创建流式播放上下文 if (!streamingContext) { - streamingContext = { - queue: [], // 已解码的PCM队列。正在播放 - activeQueue: new BlockingQueue(), // 已解码的PCM队列。准备播放 - pendingAudioBufferQueue: [], // 待处理的缓存队列 - audioBufferQueue: new BlockingQueue(), // 缓存队列 - playing: false, // 是否正在播放 - endOfStream: false, // 是否收到结束信号 - source: null, // 当前音频源 - totalSamples: 0, // 累积的总样本数 - lastPlayTime: 0, // 上次播放的时间戳 - - - // 缓存音频数组 - pushAudioBuffer: function (item) { - this.audioBufferQueue.enqueue(...item) - }, - - // 获取需要处理缓存队列,单线程:在audioBufferQueue一直更新的状态下不会出现安全问题 - getPendingAudioBufferQueue: async function () { - // 原子交换 + 清空 - [this.pendingAudioBufferQueue, this.audioBufferQueue] = [await this.audioBufferQueue.dequeue(), new BlockingQueue()]; - - }, - // 获取正在播放已解码的PCM队列,单线程:在activeQueue一直更新的状态下不会出现安全问题 - getQueue: async function (minSamples) { - let TepArray = [] - const num = minSamples - this.queue.length > 0 ? minSamples - this.queue.length : 1; - // 原子交换 + 清空 - [TepArray, this.activeQueue] = [await this.activeQueue.dequeue(num), new BlockingQueue()]; - this.queue.push(...TepArray) - }, - // 将Opus数据解码为PCM - decodeOpusFrames: async function () { - if (!opusDecoder) { - log('Opus解码器未初始化,无法解码', 'error'); - return; - } else { - log('Opus解码器启动', 'info'); - } - - while (true) { - let decodedSamples = []; - for (const frame of this.pendingAudioBufferQueue) { - try { - // 使用Opus解码器解码 - const frameData = opusDecoder.decode(frame); - if (frameData && frameData.length > 0) { - // 转换为Float32 - const floatData = convertInt16ToFloat32(frameData); - // 使用循环替代展开运算符 - for (let i = 0; i < floatData.length; i++) { - decodedSamples.push(floatData[i]); - } - } - } catch (error) { - log("Opus解码失败: " + error.message, 'error'); - } - } - - if (decodedSamples.length > 0) { - // 使用循环替代展开运算符 - for (let i = 0; i < decodedSamples.length; i++) { - this.activeQueue.enqueue(decodedSamples[i]); - } - this.totalSamples += decodedSamples.length; - } else { - log('没有成功解码的样本', 'warning'); - } - await this.getPendingAudioBufferQueue(); - } - }, - - // 开始播放音频 - startPlaying: async function () { - while (true) { - // 如果累积了至少0.3秒的音频,开始播放 - const minSamples = SAMPLE_RATE * MIN_AUDIO_DURATION * 3; - if (!this.playing && this.queue.length < minSamples) { - await this.getQueue(minSamples) - } - this.playing = true; - while (this.playing && this.queue.length) { - // 创建新的音频缓冲区 - const minPlaySamples = Math.min(this.queue.length, SAMPLE_RATE); - const currentSamples = this.queue.splice(0, minPlaySamples); - - const audioBuffer = audioContext.createBuffer(CHANNELS, currentSamples.length, SAMPLE_RATE); - audioBuffer.copyToChannel(new Float32Array(currentSamples), 0); - - // 创建音频源 - this.source = audioContext.createBufferSource(); - this.source.buffer = audioBuffer; - - // 创建增益节点用于平滑过渡 - const gainNode = audioContext.createGain(); - - // 应用淡入淡出效果避免爆音 - const fadeDuration = 0.02; // 20毫秒 - gainNode.gain.setValueAtTime(0, audioContext.currentTime); - gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + fadeDuration); - - const duration = audioBuffer.duration; - if (duration > fadeDuration * 2) { - gainNode.gain.setValueAtTime(1, audioContext.currentTime + duration - fadeDuration); - gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + duration); - } - - // 连接节点并开始播放 - this.source.connect(gainNode); - gainNode.connect(audioContext.destination); - - this.lastPlayTime = audioContext.currentTime; - log(`开始播放 ${currentSamples.length} 个样本,约 ${(currentSamples.length / SAMPLE_RATE).toFixed(2)} 秒`, 'info'); - this.source.start(); - } - await this.getQueue(minSamples) - } - } - }; + streamingContext = createStreamingContext(opusDecoder, audioContext, SAMPLE_RATE, CHANNELS, MIN_AUDIO_DURATION); } streamingContext.decodeOpusFrames(); @@ -467,15 +350,7 @@ } } - // 将Int16音频数据转换为Float32音频数据 - function convertInt16ToFloat32(int16Data) { - const float32Data = new Float32Array(int16Data.length); - for (let i = 0; i < int16Data.length; i++) { - // 将[-32768,32767]范围转换为[-1,1] - float32Data[i] = int16Data[i] / (int16Data[i] < 0 ? 0x8000 : 0x7FFF); - } - return float32Data; - } + // 初始化Opus解码器 - 确保完全初始化完成后才返回 async function initOpusDecoder() { From ae228f3b8616b463c5b7e15a977e29961def1ffe Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 10:33:38 +0800 Subject: [PATCH 02/35] =?UTF-8?q?update=EF=BC=9A=E6=9B=B4=E6=96=B0paddlesp?= =?UTF-8?q?eechtts=E4=BE=9B=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/paddlespeech-deploy.md | 1 - main/xiaozhi-server/config.yaml | 1 - .../core/providers/tts/paddle_speech.py | 29 +++++++------------ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/docs/paddlespeech-deploy.md b/docs/paddlespeech-deploy.md index 6584efff..6639a1b4 100644 --- a/docs/paddlespeech-deploy.md +++ b/docs/paddlespeech-deploy.md @@ -75,7 +75,6 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 - save_path: ./streaming_tts.wav # 服务器生成的语音文件保存路径 ``` ### 3.启动xiaozhi服务 ```py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 51b6a617..01e51c35 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -895,7 +895,6 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 - save_path: ./streaming_tts.wav # 服务器生成的语音文件保存路径 IndexStreamTTS: # 基于Index-TTS-vLLM项目的TTS接口服务 # 参照教程:https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 7f1c6a23..5e5999d9 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -1,3 +1,4 @@ + import asyncio import json import base64 @@ -32,7 +33,7 @@ class TTSProvider(TTSProviderBase): volume = config.get("volume", 1.0) self.volume = float(volume) if volume else 1.0 - self.save_path = config.get("save_path", "./streaming_tts.wav") + self.save_path = config.get("save_path", "None") async def pcm_to_wav(self, pcm_data: bytes, sample_rate: int = 24000, num_channels: int = 1, bits_per_sample: int = 16) -> bytes: @@ -55,22 +56,22 @@ class TTSProvider(TTSProviderBase): return wav_io.getvalue() - async def text_to_speak(self, text, output_file): + async def text_to_speak(self, text, output_file=None): if self.protocol == "websocket": - return await self.text_streaming(text, output_file) + return await self.text_streaming(text) elif self.protocol == "http": - return await self.text(text, output_file) + return await self.text(text) else: raise ValueError("Unsupported protocol. Please use 'websocket' or 'http'.") - async def text(self, text, output_file): + async def text(self, text): request_json = { "text": text, "spk_id": self.spk_id, "speed": self.speed, "volume": self.volume, "sample_rate": self.sample_rate, - "save_path": self.save_path + "save_path": None, } try: @@ -81,11 +82,7 @@ class TTSProvider(TTSProviderBase): if resp_json.get("success"): data = resp_json["result"] audio_bytes = base64.b64decode(data["audio"]) - if output_file: - with open(output_file, "wb") as file_to_save: - file_to_save.write(audio_bytes) - else: - return audio_bytes + return audio_bytes else: raise Exception( f"Error: {resp_json.get('message', 'Unknown error')} while processing text: {text}") @@ -95,7 +92,7 @@ class TTSProvider(TTSProviderBase): except Exception as e: raise Exception(f"Error during TTS HTTP request: {e} while processing text: {text}") - async def text_streaming(self, text, output_file): + async def text_streaming(self, text): try: # 使用 websockets 异步连接到 WebSocket 服务器 async with websockets.connect(self.url) as ws: @@ -151,12 +148,8 @@ class TTSProvider(TTSProviderBase): # 接收结束响应避免服务抛出异常 await ws.recv() - # 返回或保存音频数据 - if output_file: - with open(output_file, "wb") as file_to_save: - file_to_save.write(wav_data) - else: - return wav_data + # 直接返回音频数据 + return wav_data except Exception as e: raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") From 15291bfcf2dd1d71a6f171d562d9563cca18761e Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:02:57 +0800 Subject: [PATCH 03/35] =?UTF-8?q?=E6=9B=B4=E6=96=B0paddlespeechtts?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/tts/paddle_speech.py | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 5e5999d9..c4e2c31f 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -1,4 +1,3 @@ - import asyncio import json import base64 @@ -9,6 +8,7 @@ import wave import websockets from core.providers.tts.base import TTSProviderBase from config.logger import setup_logging +from datetime import datetime TAG = __name__ logger = setup_logging() @@ -19,11 +19,12 @@ class TTSProvider(TTSProviderBase): super().__init__(config, delete_audio_file) self.url = config.get("url", "ws://192.168.1.10:8092/paddlespeech/tts/streaming") self.protocol = config.get("protocol", "websocket") + if config.get("private_voice"): self.spk_id = int(config.get("private_voice")) else: - self.spk_id = int(config.get("spk_id", "0")) - + self.spk_id = int(config.get("spk_id", "0")) + sample_rate = config.get("sample_rate", 24000) self.sample_rate = float(sample_rate) if sample_rate else 24000 @@ -33,7 +34,12 @@ class TTSProvider(TTSProviderBase): volume = config.get("volume", 1.0) self.volume = float(volume) if volume else 1.0 - self.save_path = config.get("save_path", "None") + self.delete_audio_file = config.get("delete_audio_file", True) + if not self.delete_audio_file: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + self.save_path = config.get("save_path", f"./streaming_tts_{timestamp}.wav") + else: + self.save_path = None async def pcm_to_wav(self, pcm_data: bytes, sample_rate: int = 24000, num_channels: int = 1, bits_per_sample: int = 16) -> bytes: @@ -56,22 +62,22 @@ class TTSProvider(TTSProviderBase): return wav_io.getvalue() - async def text_to_speak(self, text, output_file=None): + async def text_to_speak(self, text, output_file): if self.protocol == "websocket": - return await self.text_streaming(text) + return await self.text_streaming(text, output_file) elif self.protocol == "http": - return await self.text(text) + return await self.text(text, output_file) else: raise ValueError("Unsupported protocol. Please use 'websocket' or 'http'.") - async def text(self, text): + async def text(self, text, output_file): request_json = { "text": text, "spk_id": self.spk_id, "speed": self.speed, "volume": self.volume, "sample_rate": self.sample_rate, - "save_path": None, + "save_path": self.save_path } try: @@ -82,7 +88,11 @@ class TTSProvider(TTSProviderBase): if resp_json.get("success"): data = resp_json["result"] audio_bytes = base64.b64decode(data["audio"]) - return audio_bytes + if output_file: + with open(output_file, "wb") as file_to_save: + file_to_save.write(audio_bytes) + else: + return audio_bytes else: raise Exception( f"Error: {resp_json.get('message', 'Unknown error')} while processing text: {text}") @@ -92,7 +102,7 @@ class TTSProvider(TTSProviderBase): except Exception as e: raise Exception(f"Error during TTS HTTP request: {e} while processing text: {text}") - async def text_streaming(self, text): + async def text_streaming(self, text, output_file): try: # 使用 websockets 异步连接到 WebSocket 服务器 async with websockets.connect(self.url) as ws: @@ -148,8 +158,18 @@ class TTSProvider(TTSProviderBase): # 接收结束响应避免服务抛出异常 await ws.recv() - # 直接返回音频数据 - return wav_data + # 根据配置决定是否保存文件 + if not self.delete_audio_file and self.save_path: + with open(self.save_path, "wb") as f: + f.write(wav_data) + logger.bind(tag=TAG).info(f"音频文件已保存到: {self.save_path}") + + # 返回或保存音频数据 + if output_file: + with open(output_file, "wb") as file_to_save: + file_to_save.write(wav_data) + else: + return wav_data except Exception as e: - raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") + raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") \ No newline at end of file From 9af77d0f473d040483ead8d6588dc4529d99db72 Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:24:58 +0800 Subject: [PATCH 04/35] =?UTF-8?q?=E6=9B=B4=E6=96=B0paddlespeechtts?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/providers/tts/paddle_speech.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index c4e2c31f..18677705 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -34,7 +34,7 @@ class TTSProvider(TTSProviderBase): volume = config.get("volume", 1.0) self.volume = float(volume) if volume else 1.0 - self.delete_audio_file = config.get("delete_audio_file", True) + self.delete_audio_file = config.get("delete_audio", True) if not self.delete_audio_file: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") self.save_path = config.get("save_path", f"./streaming_tts_{timestamp}.wav") From bdb6d3c093583a2e908c0a2a2037b19b9f342f33 Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Tue, 26 Aug 2025 11:45:52 +0800 Subject: [PATCH 05/35] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E2=80=9C=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=AB=AFmcp=20=E6=9C=AA=E6=B7=BB=E5=8A=A0=E5=88=B0=20?= =?UTF-8?q?function=20=E5=88=97=E8=A1=A8=E4=B8=AD=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/tools/server_mcp/mcp_executor.py | 2 +- .../core/providers/tools/server_mcp/mcp_manager.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_executor.py b/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_executor.py index 9ae15d4b..ee0bcb2a 100644 --- a/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_executor.py +++ b/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_executor.py @@ -18,8 +18,8 @@ class ServerMCPExecutor(ToolExecutor): """初始化MCP管理器""" if not self._initialized: self.mcp_manager = ServerMCPManager(self.conn) - await self.mcp_manager.initialize_servers() self._initialized = True + await self.mcp_manager.initialize_servers() async def execute( self, conn, tool_name: str, arguments: Dict[str, Any] diff --git a/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py b/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py index 52ab2b79..cf3deb21 100644 --- a/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py +++ b/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py @@ -68,6 +68,9 @@ class ServerMCPManager: # 输出当前支持的服务端MCP工具列表 if hasattr(self.conn, "func_handler") and self.conn.func_handler: + # 刷新工具缓存以确保服务端MCP工具被正确加载 + if hasattr(self.conn.func_handler, "tool_manager"): + self.conn.func_handler.tool_manager.refresh_tools() self.conn.func_handler.current_support_functions() def get_all_tools(self) -> List[Dict[str, Any]]: From e53ba28c652914c76adf10607542de907d8bd3ac Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:47:52 +0800 Subject: [PATCH 06/35] =?UTF-8?q?=E6=9B=B4=E6=96=B0paddlespeechtts?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/tts/paddle_speech.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 18677705..4b987b71 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -37,7 +37,16 @@ class TTSProvider(TTSProviderBase): self.delete_audio_file = config.get("delete_audio", True) if not self.delete_audio_file: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - self.save_path = config.get("save_path", f"./streaming_tts_{timestamp}.wav") + save_path = config.get("save_path") + if save_path: + if not save_path.endswith('.wav'): + save_path = f"{save_path}_{timestamp}.wav" + else: + other_path = save_path[:-4] + save_path = f"{other_path}_{timestamp}.wav" + self.save_path = save_path + else: + self.save_path = f"./streaming_tts_{timestamp}.wav" else: self.save_path = None From 880b6698770c15cacb6ad67d1a5cc980493295d4 Mon Sep 17 00:00:00 2001 From: FAN-yeB <1442100690@qq.com> Date: Tue, 26 Aug 2025 11:49:08 +0800 Subject: [PATCH 07/35] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/paddlespeech-deploy.md | 1 + main/xiaozhi-server/config.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/paddlespeech-deploy.md b/docs/paddlespeech-deploy.md index 6639a1b4..17030fab 100644 --- a/docs/paddlespeech-deploy.md +++ b/docs/paddlespeech-deploy.md @@ -75,6 +75,7 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 + save_path: # 保存路径 ``` ### 3.启动xiaozhi服务 ```py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 01e51c35..a75a607f 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -895,6 +895,7 @@ TTS: sample_rate: 24000 # 采样率 [websocket默认24000,http默认0 自动选择] speed: 1.0 # 语速,1.0 表示正常语速,>1 表示加快,<1 表示减慢 volume: 1.0 # 音量,1.0 表示正常音量,>1 表示增大,<1 表示减小 + save_path: # 保存路径 IndexStreamTTS: # 基于Index-TTS-vLLM项目的TTS接口服务 # 参照教程:https://github.com/Ksuriuri/index-tts-vllm/blob/master/README.md From 91cd843cfeb9d7c0fcf39d314261ea5fdf5e9836 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Wed, 27 Aug 2025 10:04:27 +0800 Subject: [PATCH 08/35] =?UTF-8?q?update:=20=E6=81=A2=E5=A4=8D=E5=94=A4?= =?UTF-8?q?=E9=86=92=E6=92=AD=E6=94=BE=E6=9C=BA=E5=88=B6=EF=BC=8C=E5=BE=85?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=92=8C=E6=94=B9=E9=80=A0=EF=BC=88=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E5=8F=AA=E6=9C=89linkerai=E6=8B=A5=E6=9C=89=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E4=BF=9D=E5=AD=98=E5=94=A4=E9=86=92=E9=9F=B3=E9=A2=91?= =?UTF-8?q?to=5Ftts=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 4 + .../xiaozhi-server/core/handle/helloHandle.py | 118 +++++++++++++++ .../core/handle/intentHandler.py | 5 + .../core/handle/receiveAudioHandle.py | 25 +++- .../core/handle/sendAudioHandle.py | 47 ++++-- main/xiaozhi-server/core/handle/textHandle.py | 27 +++- .../xiaozhi-server/core/providers/tts/base.py | 4 +- .../core/providers/tts/linkerai.py | 74 ++++++++- main/xiaozhi-server/core/utils/util.py | 48 ++++-- main/xiaozhi-server/core/utils/wakeup_word.py | 140 ++++++++++++++++++ 10 files changed, 455 insertions(+), 37 deletions(-) create mode 100644 main/xiaozhi-server/core/utils/wakeup_word.py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index b3c1b1a8..ab662798 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -61,6 +61,10 @@ delete_audio: true close_connection_no_voice_time: 120 # TTS请求超时时间(秒) tts_timeout: 10 +# 开启唤醒词加速 +enable_wakeup_words_response_cache: true +# 开场是否回复唤醒词 +enable_greeting: true # 说完话是否开启提示音 enable_stop_tts_notify: false # 说完话是否开启提示音,音效地址 diff --git a/main/xiaozhi-server/core/handle/helloHandle.py b/main/xiaozhi-server/core/handle/helloHandle.py index de9587b5..9374303d 100644 --- a/main/xiaozhi-server/core/handle/helloHandle.py +++ b/main/xiaozhi-server/core/handle/helloHandle.py @@ -1,5 +1,16 @@ +import time import json +import random import asyncio +from core.utils.dialogue import Message +from core.providers.tts.dto.dto import SentenceType +from core.utils.wakeup_word import WakeupWordsConfig +from core.handle.sendAudioHandle import sendAudioMessage, send_stt_message +from core.utils.util import ( + audio_to_data_stream, + remove_punctuation_and_length, + opus_datas_to_wav_bytes +) from core.providers.tools.device_mcp import ( MCPClient, send_mcp_initialize_message, @@ -8,6 +19,17 @@ from core.providers.tools.device_mcp import ( TAG = __name__ +WAKEUP_CONFIG = { + "refresh_time": 5, + "words": ["你好", "你好啊", "嘿,你好", "嗨"], +} + +# 创建全局的唤醒词配置管理器 +wakeup_words_config = WakeupWordsConfig() + +# 用于防止并发调用wakeupWordsResponse的锁 +_wakeup_response_lock = asyncio.Lock() + async def handleHelloMessage(conn, msg_json): """处理hello消息""" @@ -30,3 +52,99 @@ async def handleHelloMessage(conn, msg_json): asyncio.create_task(send_mcp_tools_list_request(conn)) await conn.websocket.send(json.dumps(conn.welcome_msg)) + + +async def checkWakeupWords(conn, text): + enable_wakeup_words_response_cache = conn.config[ + "enable_wakeup_words_response_cache" + ] + + if not enable_wakeup_words_response_cache or not conn.tts: + return False + + _, filtered_text = remove_punctuation_and_length(text) + if filtered_text not in conn.config.get("wakeup_words"): + return False + + conn.just_woken_up = True + await send_stt_message(conn, text) + + # 获取当前音色 + voice = getattr(conn.tts, "voice", "default") + if not voice: + voice = "default" + + # 获取唤醒词回复配置 + response = wakeup_words_config.get_wakeup_response(voice) + if not response or not response.get("file_path"): + response = { + "voice": "default", + "file_path": "config/assets/wakeup_words.wav", + "time": 0, + "text": "哈啰啊,我是小智啦,声音好听的台湾女孩一枚,超开心认识你耶,最近在忙啥,别忘了给我来点有趣的料哦,我超爱听八卦的啦", + } + + # 获取音频数据 + opus_packets = [] + def handle_audio_frame(frame_data): + opus_packets.append(frame_data) + + audio_to_data_stream(response.get("file_path"), is_opus=True, callback=handle_audio_frame) + + # 播放唤醒词回复 + conn.client_abort = False + + conn.logger.bind(tag=TAG).info(f"播放唤醒词回复: {response.get('text')}") + await sendAudioMessage(conn, SentenceType.FIRST, opus_packets, response.get("text")) + await sendAudioMessage(conn, SentenceType.LAST, [], None) + + # 补充对话 + conn.dialogue.put(Message(role="assistant", content=response.get("text"))) + + # 检查是否需要更新唤醒词回复 + if time.time() - response.get("time", 0) > WAKEUP_CONFIG["refresh_time"]: + if not _wakeup_response_lock.locked(): + asyncio.create_task(wakeupWordsResponse(conn)) + return True + + +async def wakeupWordsResponse(conn): + if not conn.tts or not conn.llm or not conn.llm.response_no_stream: + return + + try: + # 尝试获取锁,如果获取不到就返回 + if not await _wakeup_response_lock.acquire(): + return + + # 生成唤醒词回复 + wakeup_word = random.choice(WAKEUP_CONFIG["words"]) + question = ( + "此刻用户正在和你说```" + + wakeup_word + + "```。\n请你根据以上用户的内容进行20-30字回复。要符合系统设置的角色情感和态度,不要像机器人一样说话。\n" + + "请勿对这条内容本身进行任何解释和回应,请勿返回表情符号,仅返回对用户的内容的回复。" + ) + + result = conn.llm.response_no_stream(conn.config["prompt"], question) + if not result or len(result) == 0: + return + + # 生成TTS音频 + tts_result = await asyncio.to_thread(conn.tts.to_tts, result) + if not tts_result: + return + + # 获取当前音色 + voice = getattr(conn.tts, "voice", "default") + + wav_bytes = opus_datas_to_wav_bytes(tts_result, sample_rate=16000) + file_path = wakeup_words_config.generate_file_path(voice) + with open(file_path, "wb") as f: + f.write(wav_bytes) + # 更新配置 + wakeup_words_config.update_wakeup_response(voice, file_path, result) + finally: + # 确保在任何情况下都释放锁 + if _wakeup_response_lock.locked(): + _wakeup_response_lock.release() \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index 58b2cee7..66102a97 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -2,6 +2,7 @@ import json import asyncio import uuid from core.handle.sendAudioHandle import send_stt_message +from core.handle.helloHandle import checkWakeupWords from core.utils.util import remove_punctuation_and_length from core.providers.tts.dto.dto import ContentType from core.utils.dialogue import Message @@ -26,6 +27,10 @@ async def handle_user_intent(conn, text): filtered_text = remove_punctuation_and_length(text)[1] if await check_direct_exit(conn, filtered_text): return True + + # 检查是否是唤醒词 + if await checkWakeupWords(conn, filtered_text): + return True if conn.intent_type == "function_call": # 使用支持function calling的聊天方法,不再进行意图分析 diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index e5b96be2..b0166fe3 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -1,11 +1,11 @@ import time import json -from core.handle.sendAudioHandle import send_stt_message +import asyncio +from core.utils.util import audio_to_data_stream +from core.handle.abortHandle import handleAbortMessage from core.handle.intentHandler import handle_user_intent from core.utils.output_counter import check_device_output_limit -from core.handle.abortHandle import handleAbortMessage -from core.handle.sendAudioHandle import SentenceType -from core.utils.util import audio_to_data_stream +from core.handle.sendAudioHandle import send_stt_message, SentenceType TAG = __name__ @@ -13,7 +13,14 @@ TAG = __name__ async def handleAudioMessage(conn, audio): # 当前片段是否有人说话 have_voice = conn.vad.is_vad(conn, audio) - + # 如果设备刚刚被唤醒,短暂忽略VAD检测 + if have_voice and hasattr(conn, "just_woken_up") and conn.just_woken_up: + have_voice = False + # 设置一个短暂延迟后恢复VAD检测 + conn.asr_audio.clear() + if not hasattr(conn, "vad_resume_task") or conn.vad_resume_task.done(): + conn.vad_resume_task = asyncio.create_task(resume_vad_detection(conn)) + return if have_voice: if conn.client_is_speaking: await handleAbortMessage(conn) @@ -22,6 +29,13 @@ async def handleAudioMessage(conn, audio): # 接收音频 await conn.asr.receive_audio(conn, audio, have_voice) + +async def resume_vad_detection(conn): + # 等待2秒后恢复VAD检测 + await asyncio.sleep(1) + conn.just_woken_up = False + + async def startToChat(conn, text): # 检查输入是否是JSON格式(包含说话人信息) speaker_name = None @@ -146,7 +160,6 @@ async def check_bind_device(conn): play_audio_frames(conn, music_path) conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) - def play_audio_frames(conn, file_path): """播放音频文件并处理发送帧数据""" def handle_audio_frame(frame_data): diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index 8604279a..fb62bd32 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -1,8 +1,8 @@ import json -import asyncio import time -from core.providers.tts.dto.dto import SentenceType +import asyncio from core.utils import textUtils +from core.providers.tts.dto.dto import SentenceType TAG = __name__ @@ -30,7 +30,7 @@ async def sendAudioMessage(conn, sentenceType, audios, text): # 播放音频 -async def sendAudio(conn, audios, pre_buffer=False): +async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): """ 发送单个opus包,支持流控 Args: @@ -50,12 +50,8 @@ async def sendAudio(conn, audios, pre_buffer=False): await conn.websocket.send(audios) return - # 重置没有声音的状态 conn.last_activity_time = time.time() * 1000 - # 流控逻辑:确保按60ms的帧时长间隔发送 - frame_duration = 60 # 毫秒 - # 获取或初始化流控状态 if not hasattr(conn, "audio_flow_control"): conn.audio_flow_control = { @@ -66,13 +62,10 @@ async def sendAudio(conn, audios, pre_buffer=False): flow_control = conn.audio_flow_control current_time = time.perf_counter() - - # 计算期望的发送时间 + # 计算预期发送时间 expected_time = flow_control["start_time"] + ( flow_control["packet_count"] * frame_duration / 1000 ) - - # 流控延迟 delay = expected_time - current_time if delay > 0: await asyncio.sleep(delay) @@ -83,6 +76,38 @@ async def sendAudio(conn, audios, pre_buffer=False): # 更新流控状态 flow_control["packet_count"] += 1 flow_control["last_send_time"] = time.perf_counter() + else: + if audios is None or len(audios) == 0: + return + # 流控参数优化 + frame_duration = 60 # 帧时长(毫秒),匹配 Opus 编码 + start_time = time.perf_counter() + play_position = 0 + + # 执行预缓冲 + pre_buffer_frames = min(3, len(audios)) + for i in range(pre_buffer_frames): + await conn.websocket.send(audios[i]) + remaining_audios = audios[pre_buffer_frames:] + + # 播放剩余音频帧 + for opus_packet in remaining_audios: + if conn.client_abort: + break + + # 重置没有声音的状态 + conn.last_activity_time = time.time() * 1000 + + # 计算预期发送时间 + expected_time = start_time + (play_position / 1000) + current_time = time.perf_counter() + delay = expected_time - current_time + if delay > 0: + await asyncio.sleep(delay) + + await conn.websocket.send(opus_packet) + + play_position += frame_duration async def send_tts_message(conn, state, text=None): diff --git a/main/xiaozhi-server/core/handle/textHandle.py b/main/xiaozhi-server/core/handle/textHandle.py index 86363217..d5dd1338 100644 --- a/main/xiaozhi-server/core/handle/textHandle.py +++ b/main/xiaozhi-server/core/handle/textHandle.py @@ -1,13 +1,14 @@ import json import time +import asyncio from core.handle.abortHandle import handleAbortMessage from core.handle.helloHandle import handleHelloMessage -from core.providers.tools.device_mcp import handle_mcp_message -from core.utils.util import remove_punctuation_and_length, filter_sensitive_info -from core.handle.receiveAudioHandle import startToChat, handleAudioMessage -from core.providers.tools.device_iot import handleIotDescriptors, handleIotStatus from core.handle.reportHandle import enqueue_asr_report -import asyncio +from core.providers.tools.device_mcp import handle_mcp_message +from core.handle.receiveAudioHandle import startToChat, handleAudioMessage +from core.handle.sendAudioHandle import send_stt_message, send_tts_message +from core.utils.util import remove_punctuation_and_length, filter_sensitive_info +from core.providers.tools.device_iot import handleIotDescriptors, handleIotStatus TAG = __name__ @@ -50,9 +51,23 @@ async def handleTextMessage(conn, message): filtered_len, filtered_text = remove_punctuation_and_length( original_text ) + # 识别是否是唤醒词 is_wakeup_words = filtered_text in conn.config.get("wakeup_words") - if not is_wakeup_words: + # 是否开启唤醒词回复 + enable_greeting = conn.config.get("enable_greeting", True) + + if is_wakeup_words and not enable_greeting: + # 如果是唤醒词,且关闭了唤醒词回复,就不用回答 + await send_stt_message(conn, original_text) + await send_tts_message(conn, "stop", None) + conn.client_is_speaking = False + elif is_wakeup_words: + conn.just_woken_up = True + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, "嘿,你好呀", []) + await startToChat(conn, "嘿,你好呀") + else: # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) enqueue_asr_report(conn, original_text, []) # 否则需要LLM对文字内容进行答复 diff --git a/main/xiaozhi-server/core/providers/tts/base.py b/main/xiaozhi-server/core/providers/tts/base.py index 6a29f1f5..3de3ac27 100644 --- a/main/xiaozhi-server/core/providers/tts/base.py +++ b/main/xiaozhi-server/core/providers/tts/base.py @@ -286,8 +286,8 @@ class TTSProviderBase(ABC): enqueue_audio = [] enqueue_text = text - # 计算音频数据的帧数 - if isinstance(audio_datas, bytes): + # 收集上报音频数据 + if isinstance(audio_datas, bytes) and enqueue_audio is not None: enqueue_audio.append(audio_datas) # 发送音频 diff --git a/main/xiaozhi-server/core/providers/tts/linkerai.py b/main/xiaozhi-server/core/providers/tts/linkerai.py index 875ed0b1..3306942b 100644 --- a/main/xiaozhi-server/core/providers/tts/linkerai.py +++ b/main/xiaozhi-server/core/providers/tts/linkerai.py @@ -1,8 +1,10 @@ import os +import time import queue -import asyncio -import traceback import aiohttp +import asyncio +import requests +import traceback from config.logger import setup_logging from core.utils.tts import MarkdownCleaner from core.providers.tts.base import TTSProviderBase @@ -199,3 +201,71 @@ class TTSProvider(TTSProviderBase): except Exception as e: logger.bind(tag=TAG).error(f"TTS请求异常: {e}") self.tts_audio_queue.put((SentenceType.LAST, [], None)) + + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景 + Args: + text: 要转换的文本 + Returns: + list: 返回opus编码后的音频数据列表 + """ + start_time = time.time() + text = MarkdownCleaner.clean_markdown(text) + + params = { + "tts_text": text, + "spk_id": self.voice, + "frame_duration": 60, + "stream": False, + "target_sr": 16000, + "audio_format": self.audio_format, + "instruct_text": "请生成一段自然流畅的语音", + } + headers = { + "Authorization": f"Bearer {self.access_token}", + "Content-Type": "application/json", + } + + try: + with requests.get( + self.api_url, params=params, headers=headers, timeout=5 + ) as response: + if response.status_code != 200: + logger.bind(tag=TAG).error( + f"TTS请求失败: {response.status_code}, {response.text}" + ) + return [] + + logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒") + + # 使用opus编码器处理PCM数据 + opus_datas = [] + pcm_data = response.content + + # 计算每帧的字节数 + frame_bytes = int( + self.opus_encoder.sample_rate + * self.opus_encoder.channels + * self.opus_encoder.frame_size_ms + / 1000 + * 2 + ) + + # 分帧处理PCM数据 + for i in range(0, len(pcm_data), frame_bytes): + frame = pcm_data[i : i + frame_bytes] + if len(frame) < frame_bytes: + # 最后一帧可能不足,用0填充 + frame = frame + b"\x00" * (frame_bytes - len(frame)) + + self.opus_encoder.encode_pcm_to_opus_stream( + frame, + end_of_stream=(i + frame_bytes >= len(pcm_data)), + callback=lambda opus: opus_datas.append(opus) + ) + + return opus_datas + + except Exception as e: + logger.bind(tag=TAG).error(f"TTS请求异常: {e}") + return [] \ No newline at end of file diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 1eec7327..26484b41 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -1,16 +1,17 @@ -import json -import socket -import subprocess import re import os -from io import BytesIO -from typing import Callable, Any -from core.utils import p3 -import numpy as np -import requests -import opuslib_next -from pydub import AudioSegment +import json +import wave import copy +import socket +import requests +import subprocess +import numpy as np +import opuslib_next +from io import BytesIO +from core.utils import p3 +from pydub import AudioSegment +from typing import Callable, Any TAG = __name__ emoji_map = { @@ -274,6 +275,33 @@ def pcm_to_data_stream(raw_data, is_opus=True, callback: Callable[[Any], Any] = callback(frame_data) +def opus_datas_to_wav_bytes(opus_datas, sample_rate=16000, channels=1): + """ + 将opus帧列表解码为wav字节流 + """ + decoder = opuslib_next.Decoder(sample_rate, channels) + pcm_datas = [] + + frame_duration = 60 # ms + frame_size = int(sample_rate * frame_duration / 1000) # 960 + + for opus_frame in opus_datas: + # 解码为PCM(返回bytes,2字节/采样点) + pcm = decoder.decode(opus_frame, frame_size) + pcm_datas.append(pcm) + + pcm_bytes = b"".join(pcm_datas) + + # 写入wav字节流 + wav_buffer = BytesIO() + with wave.open(wav_buffer, "wb") as wf: + wf.setnchannels(channels) + wf.setsampwidth(2) # 16bit + wf.setframerate(sample_rate) + wf.writeframes(pcm_bytes) + return wav_buffer.getvalue() + + def check_vad_update(before_config, new_config): if ( new_config.get("selected_module") is None diff --git a/main/xiaozhi-server/core/utils/wakeup_word.py b/main/xiaozhi-server/core/utils/wakeup_word.py new file mode 100644 index 00000000..70274dd4 --- /dev/null +++ b/main/xiaozhi-server/core/utils/wakeup_word.py @@ -0,0 +1,140 @@ +import os +import re +import yaml +import time +import hashlib +import portalocker +from typing import Dict + + +class FileLock: + def __init__(self, file, timeout=5): + self.file = file + self.timeout = timeout + self.start_time = None + + def __enter__(self): + self.start_time = time.time() + while True: + try: + portalocker.lock(self.file, portalocker.LOCK_EX | portalocker.LOCK_NB) + return self.file + except portalocker.LockException: + if time.time() - self.start_time > self.timeout: + raise TimeoutError("获取文件锁超时") + time.sleep(0.1) + + def __exit__(self, exc_type, exc_val, exc_tb): + portalocker.unlock(self.file) + + +class WakeupWordsConfig: + def __init__(self): + self.config_file = "data/.wakeup_words.yaml" + self.assets_dir = "config/assets/wakeup_words" + self._ensure_directories() + self._config_cache = None + self._last_load_time = 0 + self._cache_ttl = 1 # 缓存有效期(秒) + self._lock_timeout = 5 # 文件锁超时时间(秒) + + def _ensure_directories(self): + """确保必要的目录存在""" + os.makedirs(os.path.dirname(self.config_file), exist_ok=True) + os.makedirs(self.assets_dir, exist_ok=True) + + def _load_config(self) -> Dict: + """加载配置文件,使用缓存机制""" + current_time = time.time() + + # 如果缓存有效,直接返回缓存 + if ( + self._config_cache is not None + and current_time - self._last_load_time < self._cache_ttl + ): + return self._config_cache + + try: + with open(self.config_file, "a+") as f: + with FileLock(f, timeout=self._lock_timeout): + f.seek(0) + content = f.read() + config = yaml.safe_load(content) if content else {} + self._config_cache = config + self._last_load_time = current_time + return config + except (TimeoutError, IOError) as e: + print(f"加载配置文件失败: {e}") + return {} + except Exception as e: + print(f"加载配置文件时发生未知错误: {e}") + return {} + + def _save_config(self, config: Dict): + """保存配置到文件,使用文件锁保护""" + try: + with open(self.config_file, "w") as f: + with FileLock(f, timeout=self._lock_timeout): + yaml.dump(config, f, allow_unicode=True) + self._config_cache = config + self._last_load_time = time.time() + except (TimeoutError, IOError) as e: + print(f"保存配置文件失败: {e}") + raise + except Exception as e: + print(f"保存配置文件时发生未知错误: {e}") + raise + + def get_wakeup_response(self, voice: str) -> Dict: + voice = hashlib.md5(voice.encode()).hexdigest() + """获取唤醒词回复配置""" + config = self._load_config() + + if not config or voice not in config: + return None + + # 检查文件大小 + file_path = config[voice]["file_path"] + if not os.path.exists(file_path) or os.stat(file_path).st_size < (15 * 1024): + return None + + return config[voice] + + def update_wakeup_response(self, voice: str, file_path: str, text: str): + """更新唤醒词回复配置""" + try: + # 过滤表情符号 + filtered_text = re.sub(r'[\U0001F600-\U0001F64F\U0001F900-\U0001F9FF]', '', text) + + config = self._load_config() + voice_hash = hashlib.md5(voice.encode()).hexdigest() + config[voice_hash] = { + "voice": voice, + "file_path": file_path, + "time": time.time(), + "text": filtered_text, + } + self._save_config(config) + except Exception as e: + print(f"更新唤醒词回复配置失败: {e}") + raise + + def generate_file_path(self, voice: str) -> str: + """生成音频文件路径,使用voice的哈希值作为文件名""" + try: + # 生成voice的哈希值 + voice_hash = hashlib.md5(voice.encode()).hexdigest() + file_path = os.path.join(self.assets_dir, f"{voice_hash}.wav") + + # 如果文件已存在,先删除 + if os.path.exists(file_path): + try: + os.remove(file_path) + except Exception as e: + print(f"删除已存在的音频文件失败: {e}") + raise + + return file_path + except Exception as e: + print(f"生成音频文件路径失败: {e}") + raise From a5e748f7754fa7bf13de51ea6aab965d3695818d Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Thu, 28 Aug 2025 00:39:43 +0800 Subject: [PATCH 09/35] =?UTF-8?q?update:=E4=BC=98=E5=8C=96ha=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E8=B6=85=E6=97=B6=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins_func/functions/hass_get_state.py | 11 +++-------- .../plugins_func/functions/hass_set_state.py | 10 +++------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/main/xiaozhi-server/plugins_func/functions/hass_get_state.py b/main/xiaozhi-server/plugins_func/functions/hass_get_state.py index 94478e35..4efb12b8 100644 --- a/main/xiaozhi-server/plugins_func/functions/hass_get_state.py +++ b/main/xiaozhi-server/plugins_func/functions/hass_get_state.py @@ -29,12 +29,7 @@ hass_get_state_function_desc = { @register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL) def hass_get_state(conn, entity_id=""): try: - - future = asyncio.run_coroutine_threadsafe( - handle_hass_get_state(conn, entity_id), conn.loop - ) - # 添加10秒超时 - ha_response = future.result(timeout=10) + ha_response = handle_hass_get_state(conn, entity_id) return ActionResponse(Action.REQLLM, ha_response, None) except asyncio.TimeoutError: logger.bind(tag=TAG).error("获取Home Assistant状态超时") @@ -45,13 +40,13 @@ def hass_get_state(conn, entity_id=""): return ActionResponse(Action.ERROR, error_msg, None) -async def handle_hass_get_state(conn, entity_id): +def handle_hass_get_state(conn, entity_id): ha_config = initialize_hass_handler(conn) api_key = ha_config.get("api_key") base_url = ha_config.get("base_url") url = f"{base_url}/api/states/{entity_id}" headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} - response = requests.get(url, headers=headers) + response = requests.get(url, headers=headers, timeout=5) if response.status_code == 200: responsetext = "设备状态:" + response.json()["state"] + " " logger.bind(tag=TAG).info(f"api返回内容: {response.json()}") diff --git a/main/xiaozhi-server/plugins_func/functions/hass_set_state.py b/main/xiaozhi-server/plugins_func/functions/hass_set_state.py index c1fcdaa6..3addc823 100644 --- a/main/xiaozhi-server/plugins_func/functions/hass_set_state.py +++ b/main/xiaozhi-server/plugins_func/functions/hass_set_state.py @@ -54,11 +54,7 @@ def hass_set_state(conn, entity_id="", state=None): if state is None: state = {} try: - future = asyncio.run_coroutine_threadsafe( - handle_hass_set_state(conn, entity_id, state), conn.loop - ) - # 添加10秒超时 - ha_response = future.result(timeout=10) + ha_response = handle_hass_set_state(conn, entity_id, state) return ActionResponse(Action.REQLLM, ha_response, None) except asyncio.TimeoutError: logger.bind(tag=TAG).error("设置Home Assistant状态超时") @@ -69,7 +65,7 @@ def hass_set_state(conn, entity_id="", state=None): return ActionResponse(Action.ERROR, error_msg, None) -async def handle_hass_set_state(conn, entity_id, state): +def handle_hass_set_state(conn, entity_id, state): ha_config = initialize_hass_handler(conn) api_key = ha_config.get("api_key") base_url = ha_config.get("base_url") @@ -169,7 +165,7 @@ async def handle_hass_set_state(conn, entity_id, state): data = {"entity_id": entity_id, arg: value} url = f"{base_url}/api/services/{domain}/{action}" headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} - response = requests.post(url, headers=headers, json=data) + response = requests.post(url, headers=headers, json=data, timeout=5) # 设置5秒超时 logger.bind(tag=TAG).info( f"设置状态:{description},url:{url},return_code:{response.status_code}" ) From f6f240bc4b1470ee44b34149ade486c351f221da Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Thu, 28 Aug 2025 16:32:00 +0800 Subject: [PATCH 10/35] =?UTF-8?q?update:AliAppLLM=E6=B5=81=E5=BC=8F?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/llm/AliBL/AliBL.py | 71 +++++++++++++++---- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py b/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py index 012035ee..a68c4348 100644 --- a/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py +++ b/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py @@ -1,8 +1,10 @@ from config.logger import setup_logging from http import HTTPStatus +import dashscope from dashscope import Application from core.providers.llm.base import LLMProviderBase from core.utils.util import check_model_key +import time TAG = __name__ logger = setup_logging() @@ -15,6 +17,7 @@ class LLMProvider(LLMProviderBase): self.base_url = config.get("base_url") self.is_No_prompt = config.get("is_no_prompt") self.memory_id = config.get("ali_memory_id") + self.streaming_chunk_size = config.get("streaming_chunk_size", 3) # 每次流式返回的字符数 check_model_key("AliBLLLM", self.api_key) def response(self, session_id, dialogue): @@ -32,6 +35,8 @@ class LLMProvider(LLMProviderBase): "app_id": self.app_id, "session_id": session_id, "messages": dialogue, + # 开启SDK原生流式 + "stream": True, } if self.memory_id != False: # 百练memory需要prompt参数 @@ -42,25 +47,63 @@ class LLMProvider(LLMProviderBase): f"【阿里百练API服务】处理后的prompt: {prompt}" ) + # 可选地设置自定义API基地址(若配置为兼容模式URL则忽略) + if self.base_url and ("/api/" in self.base_url): + dashscope.base_http_api_url = self.base_url + responses = Application.call(**call_params) - if responses.status_code != HTTPStatus.OK: - logger.bind(tag=TAG).error( - f"code={responses.status_code}, " - f"message={responses.message}, " - f"请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code" - ) - yield "【阿里百练API服务响应异常】" - else: - logger.bind(tag=TAG).debug( - f"【阿里百练API服务】构造参数: {call_params}" - ) - yield responses.output.text + + # 流式处理(SDK在stream=True时返回可迭代对象;否则返回单次响应对象) + logger.bind(tag=TAG).debug( + f"【阿里百练API服务】构造参数: {dict(call_params, api_key='***')}" + ) + + last_text = "" + try: + for resp in responses: + if resp.status_code != HTTPStatus.OK: + logger.bind(tag=TAG).error( + f"code={resp.status_code}, message={resp.message}, 请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code" + ) + continue + current_text = getattr(getattr(resp, "output", None), "text", None) + if current_text is None: + continue + # SDK流式为增量覆盖,计算差量输出 + if len(current_text) >= len(last_text): + delta = current_text[len(last_text):] + else: + # 避免偶发回退 + delta = current_text + if delta: + yield delta + last_text = current_text + except TypeError: + # 非流式回落(一次性返回) + if responses.status_code != HTTPStatus.OK: + logger.bind(tag=TAG).error( + f"code={responses.status_code}, message={responses.message}, 请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code" + ) + yield "【阿里百练API服务响应异常】" + else: + full_text = getattr(getattr(responses, "output", None), "text", "") + logger.bind(tag=TAG).info( + f"【阿里百练API服务】完整响应长度: {len(full_text)}" + ) + for i in range(0, len(full_text), self.streaming_chunk_size): + chunk = full_text[i:i + self.streaming_chunk_size] + if chunk: + yield chunk except Exception as e: logger.bind(tag=TAG).error(f"【阿里百练API服务】响应异常: {e}") yield "【LLM服务响应异常】" def response_with_functions(self, session_id, dialogue, functions=None): - logger.bind(tag=TAG).error( - f"阿里百练暂未实现完整的工具调用(function call),建议使用其他意图识别" + # 阿里百练当前未支持原生的 function call。为保持兼容,这里回退到普通文本流式输出。 + # 上层会按 (content, tool_calls) 的形式消费,这里始终返回 (token, None) + logger.bind(tag=TAG).warning( + "阿里百练未实现原生 function call,已回退为纯文本流式输出" ) + for token in self.response(session_id, dialogue): + yield token, None From 41b8fac3aaff31b5fd0d083a53b2b65bcff45ed5 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Thu, 28 Aug 2025 17:58:23 +0800 Subject: [PATCH 11/35] =?UTF-8?q?update:=20=E5=A2=9E=E5=8A=A0=E5=94=A4?= =?UTF-8?q?=E9=86=92=E6=97=B6=E5=A3=B0=E7=BA=B9=E5=A4=84=E7=90=86=20?= =?UTF-8?q?=EF=BC=8C1=E7=A7=92=E5=86=85=E5=8F=91=E9=80=81=E8=87=B3?= =?UTF-8?q?=E5=A4=A7=E6=A8=A1=E5=9E=8B=20=E3=80=90=E9=9C=80=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=94=A4=E9=86=92=E9=94=81=E6=9C=BA=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=AD=E9=80=94=E4=BC=9A=E9=81=AD=E5=8F=97=E6=89=93=E6=96=AD?= =?UTF-8?q?(=E5=81=B6=E5=8F=91)=EF=BC=8C=E8=80=83=E8=99=91=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E6=A3=80=E6=B5=8B=E3=80=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 2 - .../xiaozhi-server/core/handle/helloHandle.py | 121 +------------- .../core/handle/intentHandler.py | 15 +- .../core/handle/receiveAudioHandle.py | 41 +---- .../core/handle/sendAudioHandle.py | 37 +---- main/xiaozhi-server/core/handle/textHandle.py | 27 +--- .../xiaozhi-server/core/providers/asr/base.py | 148 +++++++++++------- .../core/providers/tts/linkerai.py | 74 --------- main/xiaozhi-server/core/utils/util.py | 40 ++--- main/xiaozhi-server/core/utils/wakeup_word.py | 140 ----------------- 10 files changed, 126 insertions(+), 519 deletions(-) delete mode 100644 main/xiaozhi-server/core/utils/wakeup_word.py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index ab662798..4812cd2a 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -61,8 +61,6 @@ delete_audio: true close_connection_no_voice_time: 120 # TTS请求超时时间(秒) tts_timeout: 10 -# 开启唤醒词加速 -enable_wakeup_words_response_cache: true # 开场是否回复唤醒词 enable_greeting: true # 说完话是否开启提示音 diff --git a/main/xiaozhi-server/core/handle/helloHandle.py b/main/xiaozhi-server/core/handle/helloHandle.py index 9374303d..8147db1a 100644 --- a/main/xiaozhi-server/core/handle/helloHandle.py +++ b/main/xiaozhi-server/core/handle/helloHandle.py @@ -1,16 +1,5 @@ -import time import json -import random import asyncio -from core.utils.dialogue import Message -from core.providers.tts.dto.dto import SentenceType -from core.utils.wakeup_word import WakeupWordsConfig -from core.handle.sendAudioHandle import sendAudioMessage, send_stt_message -from core.utils.util import ( - audio_to_data_stream, - remove_punctuation_and_length, - opus_datas_to_wav_bytes -) from core.providers.tools.device_mcp import ( MCPClient, send_mcp_initialize_message, @@ -19,18 +8,6 @@ from core.providers.tools.device_mcp import ( TAG = __name__ -WAKEUP_CONFIG = { - "refresh_time": 5, - "words": ["你好", "你好啊", "嘿,你好", "嗨"], -} - -# 创建全局的唤醒词配置管理器 -wakeup_words_config = WakeupWordsConfig() - -# 用于防止并发调用wakeupWordsResponse的锁 -_wakeup_response_lock = asyncio.Lock() - - async def handleHelloMessage(conn, msg_json): """处理hello消息""" audio_params = msg_json.get("audio_params") @@ -51,100 +28,4 @@ async def handleHelloMessage(conn, msg_json): # 发送mcp消息,获取tools列表 asyncio.create_task(send_mcp_tools_list_request(conn)) - await conn.websocket.send(json.dumps(conn.welcome_msg)) - - -async def checkWakeupWords(conn, text): - enable_wakeup_words_response_cache = conn.config[ - "enable_wakeup_words_response_cache" - ] - - if not enable_wakeup_words_response_cache or not conn.tts: - return False - - _, filtered_text = remove_punctuation_and_length(text) - if filtered_text not in conn.config.get("wakeup_words"): - return False - - conn.just_woken_up = True - await send_stt_message(conn, text) - - # 获取当前音色 - voice = getattr(conn.tts, "voice", "default") - if not voice: - voice = "default" - - # 获取唤醒词回复配置 - response = wakeup_words_config.get_wakeup_response(voice) - if not response or not response.get("file_path"): - response = { - "voice": "default", - "file_path": "config/assets/wakeup_words.wav", - "time": 0, - "text": "哈啰啊,我是小智啦,声音好听的台湾女孩一枚,超开心认识你耶,最近在忙啥,别忘了给我来点有趣的料哦,我超爱听八卦的啦", - } - - # 获取音频数据 - opus_packets = [] - def handle_audio_frame(frame_data): - opus_packets.append(frame_data) - - audio_to_data_stream(response.get("file_path"), is_opus=True, callback=handle_audio_frame) - - # 播放唤醒词回复 - conn.client_abort = False - - conn.logger.bind(tag=TAG).info(f"播放唤醒词回复: {response.get('text')}") - await sendAudioMessage(conn, SentenceType.FIRST, opus_packets, response.get("text")) - await sendAudioMessage(conn, SentenceType.LAST, [], None) - - # 补充对话 - conn.dialogue.put(Message(role="assistant", content=response.get("text"))) - - # 检查是否需要更新唤醒词回复 - if time.time() - response.get("time", 0) > WAKEUP_CONFIG["refresh_time"]: - if not _wakeup_response_lock.locked(): - asyncio.create_task(wakeupWordsResponse(conn)) - return True - - -async def wakeupWordsResponse(conn): - if not conn.tts or not conn.llm or not conn.llm.response_no_stream: - return - - try: - # 尝试获取锁,如果获取不到就返回 - if not await _wakeup_response_lock.acquire(): - return - - # 生成唤醒词回复 - wakeup_word = random.choice(WAKEUP_CONFIG["words"]) - question = ( - "此刻用户正在和你说```" - + wakeup_word - + "```。\n请你根据以上用户的内容进行20-30字回复。要符合系统设置的角色情感和态度,不要像机器人一样说话。\n" - + "请勿对这条内容本身进行任何解释和回应,请勿返回表情符号,仅返回对用户的内容的回复。" - ) - - result = conn.llm.response_no_stream(conn.config["prompt"], question) - if not result or len(result) == 0: - return - - # 生成TTS音频 - tts_result = await asyncio.to_thread(conn.tts.to_tts, result) - if not tts_result: - return - - # 获取当前音色 - voice = getattr(conn.tts, "voice", "default") - - wav_bytes = opus_datas_to_wav_bytes(tts_result, sample_rate=16000) - file_path = wakeup_words_config.generate_file_path(voice) - with open(file_path, "wb") as f: - f.write(wav_bytes) - # 更新配置 - wakeup_words_config.update_wakeup_response(voice, file_path, result) - finally: - # 确保在任何情况下都释放锁 - if _wakeup_response_lock.locked(): - _wakeup_response_lock.release() \ No newline at end of file + await conn.websocket.send(json.dumps(conn.welcome_msg)) \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index 66102a97..2ddf03d6 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -1,12 +1,11 @@ import json -import asyncio import uuid -from core.handle.sendAudioHandle import send_stt_message -from core.handle.helloHandle import checkWakeupWords -from core.utils.util import remove_punctuation_and_length -from core.providers.tts.dto.dto import ContentType +import asyncio from core.utils.dialogue import Message +from core.providers.tts.dto.dto import ContentType from plugins_func.register import Action, ActionResponse +from core.handle.sendAudioHandle import send_stt_message +from core.utils.util import remove_punctuation_and_length from core.providers.tts.dto.dto import TTSMessageDTO, SentenceType TAG = __name__ @@ -24,13 +23,9 @@ async def handle_user_intent(conn, text): pass # 检查是否有明确的退出命令 - filtered_text = remove_punctuation_and_length(text)[1] + _, filtered_text = remove_punctuation_and_length(text) if await check_direct_exit(conn, filtered_text): return True - - # 检查是否是唤醒词 - if await checkWakeupWords(conn, filtered_text): - return True if conn.intent_type == "function_call": # 使用支持function calling的聊天方法,不再进行意图分析 diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index b0166fe3..15a9e434 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -1,26 +1,21 @@ import time import json -import asyncio -from core.utils.util import audio_to_data_stream from core.handle.abortHandle import handleAbortMessage from core.handle.intentHandler import handle_user_intent from core.utils.output_counter import check_device_output_limit +from core.utils.util import play_audio_frames, play_audio_response from core.handle.sendAudioHandle import send_stt_message, SentenceType TAG = __name__ async def handleAudioMessage(conn, audio): + # 检查是否在唤醒处理锁定期内 + if getattr(conn, 'wakeup_processing_lock', 0) > time.monotonic(): + return + # 当前片段是否有人说话 have_voice = conn.vad.is_vad(conn, audio) - # 如果设备刚刚被唤醒,短暂忽略VAD检测 - if have_voice and hasattr(conn, "just_woken_up") and conn.just_woken_up: - have_voice = False - # 设置一个短暂延迟后恢复VAD检测 - conn.asr_audio.clear() - if not hasattr(conn, "vad_resume_task") or conn.vad_resume_task.done(): - conn.vad_resume_task = asyncio.create_task(resume_vad_detection(conn)) - return if have_voice: if conn.client_is_speaking: await handleAbortMessage(conn) @@ -29,13 +24,6 @@ async def handleAudioMessage(conn, audio): # 接收音频 await conn.asr.receive_audio(conn, audio, have_voice) - -async def resume_vad_detection(conn): - # 等待2秒后恢复VAD检测 - await asyncio.sleep(1) - conn.just_woken_up = False - - async def startToChat(conn, text): # 检查输入是否是JSON格式(包含说话人信息) speaker_name = None @@ -119,9 +107,7 @@ async def max_out_size(conn): text = "不好意思,我现在有点事情要忙,明天这个时候我们再聊,约好了哦!明天不见不散,拜拜!" await send_stt_message(conn, text) file_path = "config/assets/max_output_size.wav" - conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], text)) - play_audio_frames(conn, file_path) - conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) + play_audio_response(conn, {"text": text, "file_path": file_path}) conn.close_after_chat = True @@ -156,17 +142,4 @@ async def check_bind_device(conn): text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。" await send_stt_message(conn, text) music_path = "config/assets/bind_not_found.wav" - conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], text)) - play_audio_frames(conn, music_path) - conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) - -def play_audio_frames(conn, file_path): - """播放音频文件并处理发送帧数据""" - def handle_audio_frame(frame_data): - conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, frame_data, None)) - - audio_to_data_stream( - file_path, - is_opus=True, - callback=handle_audio_frame - ) + play_audio_response(conn, {"text": text, "file_path": music_path}) diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index fb62bd32..c61b0d0d 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -30,7 +30,7 @@ async def sendAudioMessage(conn, sentenceType, audios, text): # 播放音频 -async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): +async def sendAudio(conn, audios, pre_buffer=False): """ 发送单个opus包,支持流控 Args: @@ -38,7 +38,7 @@ async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): opus_packet: 单个opus数据包 pre_buffer: 快速发送音频 """ - if audios is None: + if audios is None or len(audios) == 0: return if isinstance(audios, bytes): @@ -60,6 +60,7 @@ async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): "start_time": time.perf_counter(), } + frame_duration=60 flow_control = conn.audio_flow_control current_time = time.perf_counter() # 计算预期发送时间 @@ -76,38 +77,6 @@ async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): # 更新流控状态 flow_control["packet_count"] += 1 flow_control["last_send_time"] = time.perf_counter() - else: - if audios is None or len(audios) == 0: - return - # 流控参数优化 - frame_duration = 60 # 帧时长(毫秒),匹配 Opus 编码 - start_time = time.perf_counter() - play_position = 0 - - # 执行预缓冲 - pre_buffer_frames = min(3, len(audios)) - for i in range(pre_buffer_frames): - await conn.websocket.send(audios[i]) - remaining_audios = audios[pre_buffer_frames:] - - # 播放剩余音频帧 - for opus_packet in remaining_audios: - if conn.client_abort: - break - - # 重置没有声音的状态 - conn.last_activity_time = time.time() * 1000 - - # 计算预期发送时间 - expected_time = start_time + (play_position / 1000) - current_time = time.perf_counter() - delay = expected_time - current_time - if delay > 0: - await asyncio.sleep(delay) - - await conn.websocket.send(opus_packet) - - play_position += frame_duration async def send_tts_message(conn, state, text=None): diff --git a/main/xiaozhi-server/core/handle/textHandle.py b/main/xiaozhi-server/core/handle/textHandle.py index d5dd1338..c1e8f6be 100644 --- a/main/xiaozhi-server/core/handle/textHandle.py +++ b/main/xiaozhi-server/core/handle/textHandle.py @@ -1,13 +1,11 @@ import json -import time import asyncio +from core.utils.util import filter_sensitive_info from core.handle.abortHandle import handleAbortMessage from core.handle.helloHandle import handleHelloMessage -from core.handle.reportHandle import enqueue_asr_report +from core.handle.receiveAudioHandle import handleAudioMessage from core.providers.tools.device_mcp import handle_mcp_message -from core.handle.receiveAudioHandle import startToChat, handleAudioMessage from core.handle.sendAudioHandle import send_stt_message, send_tts_message -from core.utils.util import remove_punctuation_and_length, filter_sensitive_info from core.providers.tools.device_iot import handleIotDescriptors, handleIotStatus TAG = __name__ @@ -46,14 +44,9 @@ async def handleTextMessage(conn, message): conn.client_have_voice = False conn.asr_audio.clear() if "text" in msg_json: - conn.last_activity_time = time.time() * 1000 - original_text = msg_json["text"] # 保留原始文本 - filtered_len, filtered_text = remove_punctuation_and_length( - original_text - ) - + original_text = msg_json["text"] # 保留设备上传的文本 # 识别是否是唤醒词 - is_wakeup_words = filtered_text in conn.config.get("wakeup_words") + is_wakeup_words = original_text in conn.config.get("wakeup_words") # 是否开启唤醒词回复 enable_greeting = conn.config.get("enable_greeting", True) @@ -62,16 +55,10 @@ async def handleTextMessage(conn, message): await send_stt_message(conn, original_text) await send_tts_message(conn, "stop", None) conn.client_is_speaking = False - elif is_wakeup_words: - conn.just_woken_up = True - # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) - enqueue_asr_report(conn, "嘿,你好呀", []) - await startToChat(conn, "嘿,你好呀") else: - # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) - enqueue_asr_report(conn, original_text, []) - # 否则需要LLM对文字内容进行答复 - await startToChat(conn, original_text) + # 检测到唤醒词,开始等待后续进行声纹识别 + conn.wakeup_mode = True + conn.logger.bind(tag=TAG).info(f"检测到唤醒词~") elif msg_json["type"] == "iot": conn.logger.bind(tag=TAG).info(f"收到iot消息:{message}") if "descriptors" in msg_json: diff --git a/main/xiaozhi-server/core/providers/asr/base.py b/main/xiaozhi-server/core/providers/asr/base.py index f4803d31..7885261d 100644 --- a/main/xiaozhi-server/core/providers/asr/base.py +++ b/main/xiaozhi-server/core/providers/asr/base.py @@ -1,14 +1,14 @@ import os +import io import wave import uuid +import json +import time import queue import asyncio import traceback import threading import opuslib_next -import json -import io -import time import concurrent.futures from abc import ABC, abstractmethod from config.logger import setup_logging @@ -53,6 +53,10 @@ class ASRProviderBase(ABC): # 接收音频 async def receive_audio(self, conn, audio, audio_have_voice): + # 检查是否在唤醒处理锁定期内 + if getattr(conn, 'wakeup_processing_lock', 0) > time.monotonic(): + return + if conn.client_listen_mode == "auto" or conn.client_listen_mode == "realtime": have_voice = audio_have_voice else: @@ -62,6 +66,14 @@ class ASRProviderBase(ABC): if not have_voice and not conn.client_have_voice: conn.asr_audio = conn.asr_audio[-10:] return + + # 检查是否处于唤醒模式 + if getattr(conn, 'wakeup_mode', False) and len(conn.asr_audio) >= 10: + asr_audio_task = conn.asr_audio.copy() + conn.reset_vad_states() + conn.asr_audio.clear() + + await self.handle_voice_stop(conn, asr_audio_task) if conn.client_voice_stop: asr_audio_task = conn.asr_audio.copy() @@ -87,32 +99,13 @@ class ASRProviderBase(ABC): # 预先准备WAV数据 wav_data = None - # 使用连接的声纹识别提供者 if conn.voiceprint_provider and combined_pcm_data: wav_data = self._pcm_to_wav(combined_pcm_data) + # 检查是否处于唤醒模式 + wakeup_mode = getattr(conn, 'wakeup_mode', False) - # 定义ASR任务 - def run_asr(): - start_time = time.monotonic() - try: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - result = loop.run_until_complete( - self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format) - ) - end_time = time.monotonic() - logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s") - return result - finally: - loop.close() - except Exception as e: - end_time = time.monotonic() - logger.bind(tag=TAG).error(f"ASR失败: {e}") - return ("", None) - - # 定义声纹识别任务 + # 声纹识别任务(公共逻辑) def run_voiceprint(): if not wav_data: return None @@ -131,48 +124,83 @@ class ASRProviderBase(ABC): logger.bind(tag=TAG).error(f"声纹识别失败: {e}") return None - # 使用线程池执行器并行运行 - with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread_executor: - asr_future = thread_executor.submit(run_asr) + if wakeup_mode and conn.voiceprint_provider and wav_data: + conn.wakeup_mode = False + # 设置处理锁,防止后续音频片段重复处理 + conn.wakeup_processing_lock = time.monotonic() + 3 # 3秒锁定期 - if conn.voiceprint_provider and wav_data: + # 唤醒模式:只执行声纹识别 + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread_executor: voiceprint_future = thread_executor.submit(run_voiceprint) - - # 等待两个线程都完成 - asr_result = asr_future.result(timeout=15) voiceprint_result = voiceprint_future.result(timeout=15) - - results = {"asr": asr_result, "voiceprint": voiceprint_result} - else: - asr_result = asr_future.result(timeout=15) - results = {"asr": asr_result, "voiceprint": None} - - - # 处理结果 - raw_text, file_path = results.get("asr", ("", None)) - speaker_name = results.get("voiceprint", None) - - # 记录识别结果 - if raw_text: - logger.bind(tag=TAG).info(f"识别文本: {raw_text}") - if speaker_name: + + speaker_name = voiceprint_result logger.bind(tag=TAG).info(f"识别说话人: {speaker_name}") - - # 性能监控 - total_time = time.monotonic() - total_start_time - logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s") - - # 检查文本长度 - text_len, _ = remove_punctuation_and_length(raw_text) - self.stop_ws_connection() - - if text_len > 0: - # 构建包含说话人信息的JSON字符串 - enhanced_text = self._build_enhanced_text(raw_text, speaker_name) + + fixed_text = "嘿,你好啊" + enhanced_text = self._build_enhanced_text(fixed_text, speaker_name) - # 使用自定义模块进行上报 + # 性能监控 + total_time = time.monotonic() - total_start_time + logger.bind(tag=TAG).info(f"唤醒模式总处理耗时: {total_time:.3f}s") + await startToChat(conn, enhanced_text) enqueue_asr_report(conn, enhanced_text, asr_audio_task) + else: + # 正常模式:执行声纹识别和ASR + def run_asr(): + start_time = time.monotonic() + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + result = loop.run_until_complete( + self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format) + ) + end_time = time.monotonic() + logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s") + return result + finally: + loop.close() + except Exception as e: + end_time = time.monotonic() + logger.bind(tag=TAG).error(f"ASR失败: {e}") + return ("", None) + + # 使用线程池执行器并行运行 + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread_executor: + asr_future = thread_executor.submit(run_asr) + + if conn.voiceprint_provider and wav_data: + voiceprint_future = thread_executor.submit(run_voiceprint) + asr_result = asr_future.result(timeout=15) + voiceprint_result = voiceprint_future.result(timeout=15) + results = {"asr": asr_result, "voiceprint": voiceprint_result} + else: + asr_result = asr_future.result(timeout=15) + results = {"asr": asr_result, "voiceprint": None} + + # 处理结果 + raw_text, _ = results.get("asr", ("", None)) + speaker_name = results.get("voiceprint", None) + + if raw_text: + logger.bind(tag=TAG).info(f"识别文本: {raw_text}") + if speaker_name: + logger.bind(tag=TAG).info(f"识别说话人: {speaker_name}") + + # 性能监控 + total_time = time.monotonic() - total_start_time + logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s") + + # 检查文本长度 + text_len, _ = remove_punctuation_and_length(raw_text) + self.stop_ws_connection() + + if text_len > 0: + enhanced_text = self._build_enhanced_text(raw_text, speaker_name) + await startToChat(conn, enhanced_text) + enqueue_asr_report(conn, enhanced_text, asr_audio_task) except Exception as e: logger.bind(tag=TAG).error(f"处理语音停止失败: {e}") diff --git a/main/xiaozhi-server/core/providers/tts/linkerai.py b/main/xiaozhi-server/core/providers/tts/linkerai.py index 3306942b..04e83148 100644 --- a/main/xiaozhi-server/core/providers/tts/linkerai.py +++ b/main/xiaozhi-server/core/providers/tts/linkerai.py @@ -1,9 +1,7 @@ import os -import time import queue import aiohttp import asyncio -import requests import traceback from config.logger import setup_logging from core.utils.tts import MarkdownCleaner @@ -111,10 +109,6 @@ class TTSProvider(TTSProviderBase): finally: return None - ################################################################################### - # linkerai单流式TTS重写父类的方法--结束 - ################################################################################### - async def text_to_speak(self, text, is_last): """流式处理TTS音频,每句只推送一次音频列表""" await self._tts_request(text, is_last) @@ -201,71 +195,3 @@ class TTSProvider(TTSProviderBase): except Exception as e: logger.bind(tag=TAG).error(f"TTS请求异常: {e}") self.tts_audio_queue.put((SentenceType.LAST, [], None)) - - def to_tts(self, text: str) -> list: - """非流式TTS处理,用于测试及保存音频文件的场景 - Args: - text: 要转换的文本 - Returns: - list: 返回opus编码后的音频数据列表 - """ - start_time = time.time() - text = MarkdownCleaner.clean_markdown(text) - - params = { - "tts_text": text, - "spk_id": self.voice, - "frame_duration": 60, - "stream": False, - "target_sr": 16000, - "audio_format": self.audio_format, - "instruct_text": "请生成一段自然流畅的语音", - } - headers = { - "Authorization": f"Bearer {self.access_token}", - "Content-Type": "application/json", - } - - try: - with requests.get( - self.api_url, params=params, headers=headers, timeout=5 - ) as response: - if response.status_code != 200: - logger.bind(tag=TAG).error( - f"TTS请求失败: {response.status_code}, {response.text}" - ) - return [] - - logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒") - - # 使用opus编码器处理PCM数据 - opus_datas = [] - pcm_data = response.content - - # 计算每帧的字节数 - frame_bytes = int( - self.opus_encoder.sample_rate - * self.opus_encoder.channels - * self.opus_encoder.frame_size_ms - / 1000 - * 2 - ) - - # 分帧处理PCM数据 - for i in range(0, len(pcm_data), frame_bytes): - frame = pcm_data[i : i + frame_bytes] - if len(frame) < frame_bytes: - # 最后一帧可能不足,用0填充 - frame = frame + b"\x00" * (frame_bytes - len(frame)) - - self.opus_encoder.encode_pcm_to_opus_stream( - frame, - end_of_stream=(i + frame_bytes >= len(pcm_data)), - callback=lambda opus: opus_datas.append(opus) - ) - - return opus_datas - - except Exception as e: - logger.bind(tag=TAG).error(f"TTS请求异常: {e}") - return [] \ No newline at end of file diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 26484b41..a608049c 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -1,7 +1,6 @@ import re import os import json -import wave import copy import socket import requests @@ -12,6 +11,7 @@ from io import BytesIO from core.utils import p3 from pydub import AudioSegment from typing import Callable, Any +from core.providers.tts.dto.dto import SentenceType TAG = __name__ emoji_map = { @@ -274,33 +274,23 @@ def pcm_to_data_stream(raw_data, is_opus=True, callback: Callable[[Any], Any] = frame_data = chunk if isinstance(chunk, bytes) else bytes(chunk) callback(frame_data) +def play_audio_response(conn, response): + """音频响应处理""" + conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], response.get("text"))) + play_audio_frames(conn, response.get("file_path")) + conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) -def opus_datas_to_wav_bytes(opus_datas, sample_rate=16000, channels=1): - """ - 将opus帧列表解码为wav字节流 - """ - decoder = opuslib_next.Decoder(sample_rate, channels) - pcm_datas = [] - frame_duration = 60 # ms - frame_size = int(sample_rate * frame_duration / 1000) # 960 - - for opus_frame in opus_datas: - # 解码为PCM(返回bytes,2字节/采样点) - pcm = decoder.decode(opus_frame, frame_size) - pcm_datas.append(pcm) - - pcm_bytes = b"".join(pcm_datas) - - # 写入wav字节流 - wav_buffer = BytesIO() - with wave.open(wav_buffer, "wb") as wf: - wf.setnchannels(channels) - wf.setsampwidth(2) # 16bit - wf.setframerate(sample_rate) - wf.writeframes(pcm_bytes) - return wav_buffer.getvalue() +def play_audio_frames(conn, file_path): + """播放音频文件并处理发送帧数据""" + def handle_audio_frame(frame_data): + conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, frame_data, None)) + audio_to_data_stream( + file_path, + is_opus=True, + callback=handle_audio_frame + ) def check_vad_update(before_config, new_config): if ( diff --git a/main/xiaozhi-server/core/utils/wakeup_word.py b/main/xiaozhi-server/core/utils/wakeup_word.py deleted file mode 100644 index 70274dd4..00000000 --- a/main/xiaozhi-server/core/utils/wakeup_word.py +++ /dev/null @@ -1,140 +0,0 @@ -import os -import re -import yaml -import time -import hashlib -import portalocker -from typing import Dict - - -class FileLock: - def __init__(self, file, timeout=5): - self.file = file - self.timeout = timeout - self.start_time = None - - def __enter__(self): - self.start_time = time.time() - while True: - try: - portalocker.lock(self.file, portalocker.LOCK_EX | portalocker.LOCK_NB) - return self.file - except portalocker.LockException: - if time.time() - self.start_time > self.timeout: - raise TimeoutError("获取文件锁超时") - time.sleep(0.1) - - def __exit__(self, exc_type, exc_val, exc_tb): - portalocker.unlock(self.file) - - -class WakeupWordsConfig: - def __init__(self): - self.config_file = "data/.wakeup_words.yaml" - self.assets_dir = "config/assets/wakeup_words" - self._ensure_directories() - self._config_cache = None - self._last_load_time = 0 - self._cache_ttl = 1 # 缓存有效期(秒) - self._lock_timeout = 5 # 文件锁超时时间(秒) - - def _ensure_directories(self): - """确保必要的目录存在""" - os.makedirs(os.path.dirname(self.config_file), exist_ok=True) - os.makedirs(self.assets_dir, exist_ok=True) - - def _load_config(self) -> Dict: - """加载配置文件,使用缓存机制""" - current_time = time.time() - - # 如果缓存有效,直接返回缓存 - if ( - self._config_cache is not None - and current_time - self._last_load_time < self._cache_ttl - ): - return self._config_cache - - try: - with open(self.config_file, "a+") as f: - with FileLock(f, timeout=self._lock_timeout): - f.seek(0) - content = f.read() - config = yaml.safe_load(content) if content else {} - self._config_cache = config - self._last_load_time = current_time - return config - except (TimeoutError, IOError) as e: - print(f"加载配置文件失败: {e}") - return {} - except Exception as e: - print(f"加载配置文件时发生未知错误: {e}") - return {} - - def _save_config(self, config: Dict): - """保存配置到文件,使用文件锁保护""" - try: - with open(self.config_file, "w") as f: - with FileLock(f, timeout=self._lock_timeout): - yaml.dump(config, f, allow_unicode=True) - self._config_cache = config - self._last_load_time = time.time() - except (TimeoutError, IOError) as e: - print(f"保存配置文件失败: {e}") - raise - except Exception as e: - print(f"保存配置文件时发生未知错误: {e}") - raise - - def get_wakeup_response(self, voice: str) -> Dict: - voice = hashlib.md5(voice.encode()).hexdigest() - """获取唤醒词回复配置""" - config = self._load_config() - - if not config or voice not in config: - return None - - # 检查文件大小 - file_path = config[voice]["file_path"] - if not os.path.exists(file_path) or os.stat(file_path).st_size < (15 * 1024): - return None - - return config[voice] - - def update_wakeup_response(self, voice: str, file_path: str, text: str): - """更新唤醒词回复配置""" - try: - # 过滤表情符号 - filtered_text = re.sub(r'[\U0001F600-\U0001F64F\U0001F900-\U0001F9FF]', '', text) - - config = self._load_config() - voice_hash = hashlib.md5(voice.encode()).hexdigest() - config[voice_hash] = { - "voice": voice, - "file_path": file_path, - "time": time.time(), - "text": filtered_text, - } - self._save_config(config) - except Exception as e: - print(f"更新唤醒词回复配置失败: {e}") - raise - - def generate_file_path(self, voice: str) -> str: - """生成音频文件路径,使用voice的哈希值作为文件名""" - try: - # 生成voice的哈希值 - voice_hash = hashlib.md5(voice.encode()).hexdigest() - file_path = os.path.join(self.assets_dir, f"{voice_hash}.wav") - - # 如果文件已存在,先删除 - if os.path.exists(file_path): - try: - os.remove(file_path) - except Exception as e: - print(f"删除已存在的音频文件失败: {e}") - raise - - return file_path - except Exception as e: - print(f"生成音频文件路径失败: {e}") - raise From 95b80f6bb1efa5da719b8210035915d851add60c Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Fri, 29 Aug 2025 13:47:40 +0800 Subject: [PATCH 12/35] =?UTF-8?q?update:=E6=9B=B4=E6=8D=A2Demo=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README_en.md | 2 +- docs/images/demo6.png | Bin 313399 -> 113830 bytes 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8480e2be..aa5ce96e 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Spearheaded by Professor Siyuan Liu's Team (South China University of Technology - + 自定义音色 diff --git a/README_en.md b/README_en.md index 85086968..5432eae8 100644 --- a/README_en.md +++ b/README_en.md @@ -93,7 +93,7 @@ Want to see the usage effects? Click the videos below 🎥 - + Custom voice timbre diff --git a/docs/images/demo6.png b/docs/images/demo6.png index 18d8f5f36efcdb639bf039da9a69b012dc1336a1..dc4edbebe50a602d1423e2f6260330496b918bbd 100644 GIT binary patch literal 113830 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%0D+@mar5Tw+h4D3ZUPLLi)(wt zB0iq76e$ClLeaiJ1h#=bP{U!0Y!aeY>rmf&m<``ZGwMcVy2rFl{jsI+@|bO0(;KeM1P@_tm3QZNu6J6w zu-qnzSA`YFolJ?uUqk|0Cl<66JZ*1p0}4>Fwzj#kv2lEO_u9o+PUI5hHi=VK-aBj< z0T3Z7=3?V|4j`I*t8e$_{)`7zcwyxtpmD`9#jG@a7-gYBAoS+_sIm$g2jn+U;dsC#5W4kV}m7KCDRQN=zcuKP?x9bga&G12V{6A(NX7`z61vmYAM-}NHev=FzZ>==sIqnDjCY_t+R zXJx4I5i%1AipW#QScTJo1V*j&?|1;RHg<}IOa?cifNHpl5UwTUAuhh1{P5(-haY}K zD8xjAV%4^SRNUCu*}3)Sqr*4%#|Sz38+*+`=dx3CLS@7tOM!(QPil(xiE!6O19`ww zSToHKTMC>c&MoJuYbmB3Lm^W!3JX!^4D%*8-6R;vTqt)1D4MCzgeQoTpp>tQP$HNp zgRe3aE(Ar(Upb*!h|pOz$v}t*AuuRe2HS~oV`6-M=iyJX5`;oj+zU&wT>^{uWGdDQ z6dRv@dh3JVkGIqk8T*l?toHnCS&Dl2eo`RP3n4nj!B_Rrz4>%FRY8ZLO`6KSUI|Mv zxK?UditbjI=U?uvy-neg?NmML3I(4sZh!k(uqN+~GBM5RJ;1rD+Rsb%U0 zDKy(b7N5OLhy6()fhz?Q$VzDY2`eoK%>N&L4}(i zv6WdKBp`oh12$C+MH`LR26EO}gRE05Nr#%D$$8|ELQu?8kZ`3FUu0cUs9CDzB)2UO zo)n$z@Eor_W{ZQa1N+-yC<+x2aM%_Uw;>vf8#iujyhAK*JvyAcxko3`5=1(iiX>qY zj)R9s1P|E>X;fW+HY;x8NS4A>_-1h&rb3G>$3>mmG1YGGL2nVQQGZ$;X_lyBndgWw zu~Qz6hjUty0~RbD;nQ{f;pb(pn#uBL)MJ$hp#KiR1iP92) zB7BFSXjITr&{m)bC`iRSpyI1rw|0&Xw|==$^1HKf_ZZ8WK2P3@wnqsp`t8Wr-E;+T zb^$&pJCYPr#BC|f{OPqm!k_pXB|COPd(8bJ6oMqLzAYqlj$o6Le^8Al6A{w%AR)2x z)Y-<#QpC#e!GoKp#kb6*BK(BS0i3v1Lvj8=nF+K)KLinym@pOVvK8AmgNmEgRuGDJ ze*PI`5R1Ea&Sfm@#_cLBzf#&BI9-HIgs`1<$6!P%z3Y6!eIw-R3W6D5T)P7%QkCAa0bJit0ytR3I(j zIHH>apYCn_^w<3}&_E*G^dJ*RRVbhU6^JpnSHnGxZ5`iagIIr1JeeVO({}wB3b?kll*OM~1(2xjLr~yEDt3N9oZMkM#3QtdanfZTwNE;2D&nP?Y~?`bw`%Z1>_kICVWw1^M@dl7Qh*BD32Bp&CRE_~Sw7^o-3vyXV2&C&%uS-Hr~w5E z1r2SFQ%+B$Hm8|Ly?uQ1{cCM1;zqKvhN2Tz^p_;z1-E%}y25h?GZYC16&9`=4%}2| zdya-mhp`ApqM~gq1~iziP#7<3F`n*SdnhIZM3{-{KS)Jo0=iwaX7{nNK`6KvAA*SU zDtkmOwMJ>AER!zvgNnFGkW5DG$x>u6jKzF8Srzs%HtI@-V$m17I6;w>mo*xi2Ijbd zMOGkj@^VjrLM|!^)}gTAZOByUU1xbAMIiCoCqD%fRsg|WZI8%r-|P^vi5*acpDh{`%($2Te2>SgNv) z6!*HA*e*;+vJl00Y;KAKTvaM?Yh#00T)iL`&$rRkX}-5!v87;2(-@cvtNntWr5H92 zrb2}j&Ggm)nf)jbW`6BK%&sc#B5ou~*)(WY;VX5et_ z*|!vq8C@|D4kSeYuKe8`rDZ8KE(}G|Fck4ivOFU!%Zt|NYCy9u0E(a@2b``vBNKKW zm?}O!zxsfV<9=y|Lh30e7b2rFVMDQ2T?cs(3i2MTglH@nX&fJ&PTts`F0rd@WELb3 zlvGp^&f}$&Y|56xqamWKDEMqn3_}@FH;P9a9TP3;;|2f3|Ab;#wiMV=P(3Ubb7V6xT&bNz6u+Y71H|D&43Ef(*Fo zXeQMGQ8NmK5UGsqKnz)EOI|>VFwl*W!G$2kg_?&5#zmSey2v662@$j<35bM>+J#v( z;I2E*IrqEoo0ppzE1vh>eRrl-XznlP`|iK@Dz%VQL?BeN(xBiK%_7*vBOfjmT=+KC zh{%1#aHb3OH04Y?9s0s?kYXQ0@3_+@U-t63Pajl}3sN8yP^!*9=hJDS1EJ`u+pAr4 z9o+jkeCJST?~hK1Y`8b{k@r5#DbkTFUxL+Fp*1|^LN+mZ{sx!`HsMU5uVGeU`jkw_ zDS*UutyPg#)JlPaU&PR1YF27ZaUm>z`{}}mx5&jgYub4EmUpXqTRRf*R-WjP+!p-w z?+1{+(;M!wEFO+Q-J_~Oft$>F*jipt40S`Hgv69Cq)4f_b11BJ%0~ zw0`3tITn8WtA;SDO`8xB4uoO{&gctpT)wJVIAu7m4PnDQp9s0X(XY>$O?}rgggTjE|flPvt14Q63 ztALbJU`b}Psqj%S(W=lbC>zr>ITWgcrde(ldH>!kyXOoHBcmX}N@pY4$#JMO>5)+2 zsiu{BUj>i=69^o76g?lxtjvukAqgck90@R|a45J`!H2s0PJ0N3JBC90etf{&Q;Lvh z_Hqh1ME6nFU^qPA?2kmTetl?r2b1JtqDUsJE9(6XP!SZN012EPN1^~*K%~FPDnulp zib9Clu@3_neE6xDNVsq_u##tNZnqZidUztyJ^QJ5&;=;;AfJ$6 z=ne@CItOm-34}e{DjkX->-*y_91Mu^OC_BNUrO$QJUvm18M| zGm+zRnN#G%#!i6o8@3(J99_Jn%+sN8dD>7w;S?k@3~o?U|=~`ZrLZ z*bEFWJn`#YjBF-Qgq3$ZSR05sFsB&Cari$ZngtY!BQz|FAC8BPLifWddf0?dmD)tQ z>WTi=<=HCQ1or!o+1j0TGIjXEp&vW2}bp3WK7RXYDLk%Tt6T+#?i*4r@e0 zF$SB6xbJZF=Wn$e)xhxl)1AV-B3X5p3|pzX0v3_rks_h+LAg`7QS?hRbp*TUV-&up5D@emwZM4Q z#T)8~dh8Gt0;0skYhhLtnS~{q(_o@LHjSFRVY*TQ6uB?5VK`y8Bj`tsVF?OC61ys_ z5e<;Bx;BYGqMmBn_)dmp3rV`+>*;zpMSSxbu&^C;R(4Nx40>TmBv6P5MQWqL4Wfrr zkOtt<7mEF36#8r-qYOo;x>L~cgas-I6=0yeJxDaz zc$G;s;DiVToC5i!Yyz5XV%c`R-g+%Sfm0N4vJ4B>9$8!FaJBihZl^tVK#p=#n{z2ss5=;3+`*KcAhK4^z0JY4}|FU1UvW zQ3@YkwUE(u<0%Fb4el&Gb^fm#FbT<#pe$(E;#`)P62)b+qPQ?_Rj&hz7JZ{uQxBw- zwVpltJRkDp^J>vMg&XU^?{zMljAncSE=t81{IUvSjk!lve5e>>k}j zgihT|PLJ zo_$3Q91AJN5E|YtZ9*N@3pj}b4 z6hhSKEL~c;i8BlKpxckQ-v$u;Y{Jq`kilzx{lc$D@14A~cx7p4cN@YxB$V3OTiNVq zE;a@!hvHD0Zl?VcHwYD0EsR1bT9{R^&|wu4!NMBA#tC{t!NRa$$>TsBb0}DN$D=45 z3KTp-_y}P3l8{J-GHt8Ht|(q6mAvHgl#E*$22?LRF?s2q3?0^#N5^6 z9c&S!c~o*G6$l(GGmjn#6k>%JU?3P-t~+i)Z5(ur1!xOopkuPJIx}_l)uWS_#D;!P zZL~+SAH9sj!QiQi%cDwa8S(VLELl!rzk2L;P%ZJ^;P-r!rreLh7Rx`CdxTQAcDP6V*-yOv1Pfl=yx`!n}`u)qH%R( zZ?2ldDK6Vkg>0dfYrH~rmnK16{PWvn!E_snOti^FBV*zTGK2=%037U}ynA%=JNgFP zZg^Nj)Ee-yy?6s9Fs!-lDyxix?gFSA3Pq&|g_29#U~c&YtNo#H`R9ZRd=+}5_8@M1 z;Y-;mq@9EbOEgP^6*`sXI1+YCI;8`aE1lc^P8ntd!iOlt1f|>(5)x--ef@qbXSx85 zHIQb`?VTEb$Dm-JH1sr(QHYAd7+gPEN+DYS6GlRQn-U!St0u-E5K+M;GS&hc8?X(c zvADFfv!kDx-a#DpUlx|(T&R%B8K|j1$0;ghpq5hv)HFqoYe=U{4bTq?mk`kDNR%Zc zOsP{i6Z^wQl4q8_@L?;V5Xa|KLG0L`Z7CM zb9*b#%w8uIh%vMR0D%-TlT|?a%00uwhT0!YbUG;6{9;0N_(X#TzFcTEn_&?KjIvhGv<{e8G&ut0@!%1_eq65Z~k@BJRj35~E0z zK;gZQ$ihxa1)=cqD>+3EF4FM|lpK{iJiNX8&<&&$T}+e{3g7&taS0#+i3#~c8%&_# z##)Wh6DLOYR$j?JGAL?-LNWx7+Pb8&ojO6ct8GoB6~TkjNJ}IS(Kx z+!oewuCIZNb)4(yqs&ZAz5D1(ix2R9rBEOPTqu+{7o|RDl5RQ{kl@iLlLj%-9Q_N}QKe|`05 zio|v96AerV3Z)Zj>n2oV@E{HlZkH!m&m;m44`FvD;z zx`-&tI~)E%btl51U{AD=@xUX^DO`MwP7gU0CeRQMk>FIM@xyx{f`YlGuy6+x5-Ap( z_b(21xH0p$yM1Mun>wOT?BD8BZ2o#NFu?%{3S$o;cC^`&S=NE7Cr3u+=8hN?=%GwE zm4Yfh0E$Y@MlA({;NVT|P7;6v?SesrM;_7$Im8%nzzG;;x3+N2@>bS54q$P5?ey8R z7tekiAB!z8%rML^221(H0Z_zOPWmWkA!~kv0%ruH-1F!OMPDY|DGtjlY%QL_QQB7A z(<&@{#G!7Vic14XTz_nIK%p14yY%4q9y)BFs6-&7$b@V{*=2=LWO=j2;K5&Q7VS>8 zIzFu?YVXzQohpKwB} zL0hZF1+JejoW3x1@ulY|av?AjO2|ccv4$GbM=xAPihpux>MSaFj;)|bp(t|wUg4uZ z6k73*TLAKrI^M9 zq6U~i(;)(&Bn+ei_wn(Ok-bxo&wu)nK`~v)1qCgoj{g=Dr0S^F*xZUDh6Ds6NClx_ zU+KRxr%;^OmEL53NI`GsK-;z*bHO}g*eJwiWO z8|AjqZQQ6xLhnRV>Pv*Ez;%ppI1}l+!u&1QX;R@%0fin=0z^8I9v|wC`LD$=*Rdao z_|zg?f+FS<$}PuQv2Q)0xB*!ZsB_SZMa#5(iNnVFf3?_TJ8kO~o@JN$unh>VO`8X=S?190;>9m5o#0u{GMx#?oI*PRkxYPvcDr8hj5fxUOw=c;ow3%$ z#1qGlJbr3#<>jYe1QehGz7j%2tI4KstL=@7j6;|RKNdi4hv~1plkqMsM2p~4zM~=v z*RJ8BSmPP+Sl*afpJ8ozd1K?f4?h1v`dBJ~LB9|e#ReHS1y<~GI?9bcD1VTAmxp&h ztg43K`JbJlKNHCciexV-ksy;id{QjILQY}q^FFw}Gr9a1=D#d(sPLVEN+{xnKpDk5 zui0ZUF`6wRMmyDdGwU$JoS)z9WH5{qM;_kWTY3G-nnTgl0Cp2C-C}TnJ)E(*(OTAq zW1yBPwh$j#M^*_@qsS~*0}Lo|Uh6BoSz?*RwLrzi&%gNmi!Tfe=c3Cm+%3e!ze_*9 zDAh;^nMl)2wMOa`(*Jh~X-DR8VEAx36lt328pPD|&ROmyrwD}mgvXAzf=^ryV({Q2 zF}dQdjS(o^wc48#)y;`cb-q*Gte$*=iRQ}d6+mIV4+u7ac(bt3>Bu(l9SgwG z&cj#*I-j@{Q^L4-b4!^eLI#L1Cg=vdQGb3Pk3!d2m`KI><>e3Fd++Aw6v1#V%q>8L zk_%DcK=_=PtNVj3=9Y{Iqkx|aBvjhX0*W{l$4Wz*c5|gp0Y#5c<=+LHICtyp z4bFcFib5j7#K%s=&0jCt_Aic9H5xDqAVEz`WLb4{b7FpT6HuUXWAd=EAX%S#aN1f<-(eN5Wq>DYH;2f%<@PabTut(ZivD{tFc7m~x?w zLy@lEb{3+{DO5nH5ipZ7iQ`4beX6Vn34KD)N6 zFrCk4e=ET<@*rXwFPLmc(jq6C01KGJ0-3P+1ARe=xJkm?`PX)B9zBl94$gy#PMkP- z{N(Yin|JLY69WU6jvhUF3}7^H!6>3`0W2hjX!vM2_)2|4eZm@q-4|xJn9Irv%`v4@ zr=Zp%LQlnW>mhZDW;G$ftEtQlS*K7C)W{mQLF07o2c%#{86xxyM5}s6K`1Cf(I(Nu z7OQf(Sg;%~j?Bl)v7l3)kHyNBIGm#TYu76y?|o?E2geym8Yn=~ogbWLQeg`f%r=MG zeq+ce<+J;ccNPjTib48>3?OjBCQg}%u}m0*2|kDSY~FhO%9TZ|-E<3QMPENTx^?p# z!)J~f9aApUE))xX=D3)Jx<%7Ox;h0f!XWViRv~YI!Y$%p6AKp;>h|a7jcf&V0+%2RE+*Jyb01dor zCSZ^VtKt2-x1PKX82(x>-@Ea^xvQ271`_4MV!l!?6vGFL4MmPe zOWi>y(C3R+%Ef%ladPoo5t&6i04S>IbW5`J@{jdQlL;hM>g|n_ks~aef$%r(9@T1nOM4gsR76wbGNx&xxwf>sq5p@mBOaO`{Sj4W;D+h`^V2Zeh`dTS;u6cnF?LM2QzzVk1oieh%o zF8=a7rpY0V9qvLV?^RF?5ek_H=R~>8bHoy!OaMZ}03YRYyyC=Sm5LLs9`1@9ekGl~ z{9%3ku@6+R0@~f%(>;|hW{dd(C(aQ%rkPT7ax$b)$oCWqgR}j!v;7=-B88wuXkt0M z8HW`7C*5)7nM14c3!8LG%E{Ei#t@;xwc}cy0!>RjQqMGCpo!c@A#?}}s=o)t-O7Z9 z4_!#%Cc}b3r+5gun-_!*mO&Qyy@tjdwc6t@SiM}7(RLuQ*E)% zwo+%FLy%Io^SA6!zKvmNu%pnkP%A9d2cMvVG|Wi1e=`oz`#-b}T&P_L6&4FM3wITZ zY`C3*s}RV90l`{@ysS11b&9_?L#jeNvoFBnFUB1d-ZXp&6ZRq#e7SMA0D-nz>yToy zh$+%~6>c=GPS7Wej86;!h_MutLj;cASgxGtDIr#bqmJL#7pPQ9rMOd0Cg~KJXq8YL zZkfLbs{j>*0s;ur(}ZGQAvQfE^Pg?VCsNaG9ogw@sxUQ$H)Tr`c zA<n1sY=CETOLVfN5}MZN`@S@*|T%Y@QG_jfCPac5fl4Ium9=Kc=6uF6UUDq z-!2**#p?-t=}&!S@yYKMi^dqDnT1l(d=|6WHb_1!7?y*Z@3vAT-XKlFxH+!hEhd7H zdc6>#QgcAFET|=b0IWI$3-WpTn>MX~R4ziHA__L1P?AVk^ICUh@x-o4uDYXEE#Y_V z7zG!C1exG$d5U&Hqrj58M0d>V?Jnj}r3(8z$xJX1$~dK1B~wW{Ie$FbmF}uWUOC(n zuS3CHvoo9LvWw}q`O-c>ftpq4bP1&3Td9szXJJ+bp-3>9NgTO$WMWx7Vrk+}OadQU zwSDuR{R0C7Gm{hvdK!9Z0ENu`yT0G9!&Wy+SI!-Uny|2D!C{EFg{#c1o6pS>HatZZ z3?1#6M%81+T}puhAspC7p|>RH;=6Xi;K6n>04rce7S@vj60iXm+WJQ~;%(!GjXQVl z+^`A?{bL(8f!J_{at`-@88sBE*iiriO*%61u|3)>J47g0gPP`YnpnV-CGBq}@nvC=%kM=}vl?ub;o7(ZHCO7C61jA5zq z8ia{ycmwX~LTP?1i|RHC6Y!7}ph&?Xq*PT~o zKF|~oBu{`00S1Fj56WfrFuZ_vpa=kfol3x$_R*x>t+!4(A zTUx5=EqkQr7<;#2PP_!{4X~P&a)mBOkVbFoOk3y=mBU@-qk%yW@j=359WUnnj z6SqX-%INOl3GiU;VS)|A6E;%8P+~N}Sc0}~+qN?YUp+H^=)nEAyLB(z!WBb=3SwcF zs6h?s1-Z~8jm-zps+hy^rugg6c7r6K(FxjdsG3Cp5Kx;|O9ozv)iRyqAw8i#RG|8z#I05y`Ja7TXy*Uo=UtN&xAtdTqYB)RwHT5 zex#F~r%wY4P+{v{+0J4fP^7YLV`FWdV<=a(weDMZYPKIp2ocK@`~f3!ES);(`srOC zeQf~1Fs`UW%*{=(AD-djn&G+Ofnm&y;yGNmjHkDqc@tEeAr`N`dGx?K*T49_b9Nx2 znT6IY>>?|7?V2J9$^lwC9H!l96D0#5fG`gMJJdD27R7j z%mXH(;paTw(3W(-Q>nz`<>Z!RE?5bLs?}1)T!HWNfT6JC-eU z40iNS0f}Y$#4l9R9T*DQsT%?SFUbb1 z1K>hNND9OT3=QlA1D9;9K<`FF@Gwus;jeWYdF2725a&PYG?>RaWE0Qxs5=VfOC59Q zDOM6WD6?42W!*b(oJ zN4B)2s}Z!8Ev5Hh6`TOEBM>MHF~a~QC{45$3NIFBmuoNy5P<}9ViHC$u=QS7*mz^| z6O%(Mi9<|+2bctK00}j;^R?P}tiHp=HcK+_Ra(bOFI~FyHnE^v2o?v&&o!qPFEfT5 z``luor7UuBgBR7#=R?Gx9t$W7!FoLH$N)cn%Yp4x=I4(ajh)E8nlG*aha#fyw?gCt z8%YETG>}oh>H}kjj+1Z5K_oY`2{b`Mfe>}nl^Qc0%2o&-+&0XqE3DNn^#-wqKjDP@ ziJ->|F9`Vj=`EpPz(J8a?hCh6x`Y0%u9gVR;_xfEi?9kCKa5!j6=SAmp6b9f$HI%X z!ZLLPM3~U9zd4yW@%+pu9BY^yf(_XymSH01%+Af7t<_N9IC~at78iB??D^UA0tRBq zOJ^>P3l@-Y@v7KGb9S)~F$7qAu5JM?@RQm^2?f>(1PA{7g#uG6YLGIpmNw{1UCIZ+ zX!@JnnCz5};`^?w#H9YaGbaC1%OR8M{c zDs~6Ti9`_5Ba-oYf}Y5ZOvZT*2}H;nh?gr#OoU{Tqy%#xU;>41XY6+aMJuroD6#^@ z7#dDlEYzMV%q`C{Zh#1sCOGEU-V`}dZd;;%uxi1GF~c09CJs@X{Aj{A;Oku-R+1R?$F0+)%iwkP9|}Y~nn(f0<$Ea-9hQqAoFp z)M|B%6^6|5>v>tt-!|9-D4gyh7G(y!&-o)>U#2DE_XPZraLDgCxk@tQcu>>yMk4-d ziwr-~$?4O-pXOZY$7TmQv0y@B15`gFN9_o!+DD``0i8P0gv1_+DD;V$$%z^q;*>1Z zmN^pOU{+@>d`Bd{`-Z#DxxsjWA{SWAz5nP{PyxLyZXp>b_3pdJHa45Z`1sLdz(T0_ z3{-pxGu~kAg8sl>+4$M!j}kaU773N1K%R<`bPZ(atIF5~yZVcp3rKnr;&uuOJA@*xu|&|<+nvC|%%CS7 ziuio#P{!l)c7-AygpUfW!k=-RcsT6A3Qdka(xKeN-+u=bAAblael%2|6DIJ7KE=vJ z?a1=nO(Iq%A6axGA-Mzws_+Rq#90Q7+SwZH;Vc_N;#T%Ed zUOjfK0SlqxT~P7rp+_4tizknbe`KlnOsOzXkP1rE39AQ{ktV4{=oiebCh|87Lz>My zT&A(pK5)69V47*2f<%b!eR=V%VX+D06FLv7!wm>e;J}+Wu$ecY0jtQz;1f(WN9Gf` zWT3k@h7(r=F@UADKf{8$=L^rGb-;>E&)oGMGp(av-f-u3>Zm9%k7V?1thgTniU)=F!5` zg{cb=sX!AhUVrWCq0c`*1|9u~SU@(0&@0|O*pyqWJM_1xP|-Qqv6qh0x3w^lr->!@ zGHoqXG;viXbUdqXns(Z$b4Z{-Peu?q%ps|k38?;I!;Qz9$$C4>1XLI*;DrJg1#A#3 z6}jne1Y3WSm0(ZIAMhb&q+2|I!t0NQTEf17v>Y#M~ScN;c*YQviY&c$n(# zp6czH`ntCl=QV!aD`W^4dv+cA^qqG;H(Y#VxzN-CSR7bvFpC4@AAMw~AQVD{$tsiy zx9Y$U9l4~fA&nrol>zqpxHTV2g|Pk_lx3nNTGb4}~*3 zGGR$7(oQSl2U1OwRG7O%{$S3x$S9OhtN@A0tqmFG$>Eiim8EMWLQ0mbcQTXsPI5}b z3L-H$MPc6nmT7eNCVCTny@{`(ZtOwm6)g5(*Wjn0f(wG7Tu2nLSd2f}lyA~3G_R0k zQ>>z{8x1h9S0HSsP^_J!+Qg!Mn~rxK6Z@)AxO0m&!-r;?6m5c^Bx=`JsZHoChcOBE zBJqiW%zBC75P0v*0*qo$cd&P`#C_#KZ#bAho!cArGi4AcqE6JGL2cWqAkz$>&y_3S zBk2b=s8#ry5D``VtVq=>Fk{3*YQMcK~tVQ%bmCdeMXhQj3Nv=l!2IQK11uk#LI? zn5TlEQAJ=G_~fXn6ytglii^psSBp^<|` zSSW^g1R_2!k^&t92%-o6M~{rm7mK-IcK|s0<$`wMa*H<)tahn8 z&#k6{0YstrYb0zF!%#HZ6%cm_(RZ~DHkXKn1`rn(ylrB^M3YePmhD6sh<F-$`*cbQM#FIX8ZJGJytld%0 zg*X?OMlAI9PW5nsc&SLu0|_hzn~&u%i5tX@RtN|G{;>6#C%^oP5-ij%EEZa}xVo+} zv9MGCiY6+Eg#3}fxp(6=TpoGIHVtxMb``A&Q>yAbJQ7ocKSD*8Hu^JEg zWiDt?D5I=d<{J<}{D@Se5vT11K!E`Wpr|83CbTqxIPv_;tFwuLC1#imE66xtC^QM~ z3F=2bJ9T#9!opPV6dJY?ar+5{ZHx2V%$gH{4){f|?}yE!kMI1FYT!b!FhNAk;_Zga z;+bPjR45iV{*GPn(S(NWRd(31hEv>kot?WtqtC<3UvN`H>@?vPQ@uE9)uISz5c{&cr~b(nls&hzdLcP#j@?IX5e%DhQ=2 zCYa(A#oj3#SpX(@vQi90p4Ey}zC!%g zC+rd*CvdPFNgs+!vF5%!F4EMGK<*Z6?rTn@qLtC5hIl*~s*DEf2t_myNER_-gd_3F zx8g-{`A{&14%47jK+$MN>%I5fJqfR1ers&vJ}1MpOpJRo%rQwY4|600OduPz2__bU z{IL6n)-S(geFEl~vn+KZPyhxA9-v~HR7@MkNTqJS^%lIfe3uA@@e3CV_FQC|#f&$v zx!O;K#t(n^5CZgKq(mqcUM>^}Lx9}p4jDY_Eu`HQ&Y@J~nIC{NdoMhXvoK9}mHLD} zaM;MC60IsQ!MJe;hRHD|+KaVY_GyvTs8$n8#v6{HWGd3jP8Hhtg zIFd6u^K~OykB-K`g?l?cz{ZO_ zzJ=8R`g$M7+dVSUDRU~TIfx13?R%YsvRm~ zj2u?epGQb6bOaGi-CNV z?G)aS!LM>V1$N~^u)wBcV@BE}z3kV^9^?ey9rI)EQu0YL3TqRPg~HCGaiRuxYE-JF zR%0*}t&Mgp_9P_}QZ<786Oj%yK2XZ)+H04>(NHKTW1vlSmB|JKi|Md%ftWE32aoZU zI28GEGATM60N_R1gLn z>EoXykAn%Zi*S9af8k7L>H{QpVX_NSK`dWJXDh71zeVllQ1r;Urp z17IRp9}2aP-uu+u28w$jrzPrV;@e?^#PNVN$d zOeaJFM3Sfaxw)IpIn&OKv0Nw?nW=?R0nMD3g9PZ6(j-u009ie1WR9deJEd+a3YTGl zPXnPqYa$p)u%Z==0?}h}PdtqUgrFfD1{M<6(~|GSaGC}bK|qmczhm!HunJ*A!-mB}L2!bgVUMl#hXeN2ZmQ4`b62&PO6WS+%U2nW>_$F@Yrcx%j5ehJ| z*s%JP(iw7zOJb$px?kAwY4Y^TvBVz5C##$0hEzKJopy zhY5CKVi%);IM9La3|w3)zS;}l;2ku|Qa9{Vp;@G@j$0wg%pbpyOjubi)Glns`I*lw zm~LL}%{J95v=b{A3_dD^3B8ev&nZNjW9I0i$Z`sva%Y;7Xrk!G_&Zt-)5v6)5QuP% z!f9>bpg2(yJ|Rz#l~Ps>&CTr{BN!6J;w?lE1nd}+wpepmPzZ)X%>mA}lP-vOOs3q3 z0ujaz2%JFpT!O(sFhHjmy)p08d-4t*v07@sh$4j9E(H`|(c z+!;(DVg?&+_Xv276B1j(IOXl`jP z{yxoYBycDL&V42&%7oIO$%PjNpHu9(bYV*RFb_IJ;9^Q^8|h4K9U-SUCfZ3T&{5SV zbr3lPGu?*)Fw;*lf_RPD63OOpG`NTf67`Xe`liJV%*(B;EJFo?7`hHaSO*eL;9Jwp z?Vv=8=Haz^DwRqtfAd4$r%&CTm#6(zhKbEWPgC?|iv%SJ=FMN-{1x`SU#VJ_A7r=N`N8O4Oli2S?WAq@Qkp7}WTFOkAc-STq0tF0Mp7+F@LZFBN zisR{OKmns*yiUdgiCCmL5~Wkr0g6Q3Vk%k}tqjJKX+V(_Dwe+K#gvA2v=uw#DPoBP zZ~zh*lltg~ukQZtyHB6G2m0-v-=6yIsm-T0sR0iYhg~MrDD3JW(K~Ygvh4Dj7mHqZ zWX$w$F0C~R6MiZjEc|AXn~GbRR#+--B^6r@m}*L)95`1$6IO2Q$hL~hay3A7^0#8) zb&B~h_F*!M^VX#?frBQY1@6=(In<_o703B$7z+!%u`Z&;U(s;1D6z+t`>ZC|LXWn=ik5*)z@| zKzz6P(4dZi%^$`~z`~A$2^QFmq>QRtCGi)Y_@ep+k_%t~ndgpR#JurxGU1oT5W&JE zoGukJIjHzdsCZSVkN~pPlpCc&E;*(Bnc56dr_k8JtKIF37KxmBCZXuzD7i3k&>_M# z3h(TFbX6%76aoax>G#db`-z>!mEji?BUt8vKVrF>@LktoK7S&}S^JA?*FCznTo(-m zFyNF-clM&hDwtq{g!&PZ)7#L21K1Dr{(yT)%uVjQJYarVq3BO6AFd+1e%~a!Ae}hJ_S-r zB#Zg7jBX^>oSu;C*$5v94`q8IiF$NhVVZU@6{xE}f~<0yRr~u!A8*H)2($qmP>}|! z{{H^C{)&qHl0pLosUQ}|*0tTS+QotrgM(vZ!@~mu-QC>--FMzOF!1#77h{9(nFWAd z_?DXC+vgX!0P^VgK+ijP6@YL(7}t6{4JbaBncU0E+vnHMWBm~OfO9+;-F-efPx9C_ zjw)k?Ck$ifi?@F80-7OHQ*UUx$u`Ii?Le7QDx72!%FO{iLgnk%)~nw5M$G*qVZP~C zW>;m7eY|d&01w6`R%A0(4~c~2mu^ZyAS@AmD0+&(VKfl~5urpR6q`==vA89nz_4dD z5K7et7hh{jr2t3hNIL@e1ir7ewU19LUMfh<&CTJ{_n=_UL?SjDvmS`_7J4^#>T$jbL6^*hKfs}s;Yv5l9Cg) z9wIf5Wj!j^*j4S^iI;B0U&53y91cZ7N#qOZ3C0yPK7yD^)5Z+4 ztqoJJ5~z$PICbuB#Y&LorL{HucIFcb!N7sR5K#e}5T7ti4ALja#LPbTcmAYh;t~7d z`fY+toILv!pN*~x{M34uY$AQT{ihbeL=R2-6oLZ{Pcivwr!V4qh%%jnU^N{9fX0ewz^a6BA~ z&GPXqNI_$mt{n}w)g@9$Ba66;N$UY-}tIx!S7YO02ok7zM~42DjzP!pODnLKcd4eb#kBU zXD54($^Rnc;orB^<(Bu)1y6xeAORjXUC#1lT#4*!k$UBCk?LlE@Y;m&2}?$vTW_5} zSfj9^!!XfmlS`n$Ca9=dG;6wj2Wv@4ymfop$wa>7~ypbS#X!b5dzeaj#|*lpXNU;9**#gn}kPBFID^b^}Fv zg#Ce>=NJy7Pa-mnaV12P5jX{^lwhLOqdzY$aXLjLki3J_vJc(-cmp~FOz?1(j}4O3 zc1Vy=CTu=YJLPGNYVfd3P|AdS$EvnoeOfr#1@#;Q7Wz++i~Yg{; zD&<7i9p~u00uSmG*fgJ@RnRE3Z^DKRLIE#8>>w1PSQ2wA%h1$77eo>@b&r3Y?CVQU zMSkEr=4~Ai$z3L50tHKB27_@1x=jxx-tw<0G4tF?4akI>vrboDCwYdus+OaL!M7j^n z4rkCg&FQY809N`G6@dgk2nsKvqauhJx=n2xskHzmV5RTCKR$V1Z}alnGMIqi38jRh zxVV@{OKD3nFZ+jvTqGPO_%6Ss!K1OZR-Hm}f~Sc@#&a#UuSJ*ZLXVH6s}HTLOtv<+(FtyYsJNYW2l%K zy7SI%da`|=-KL%fkA<4r45#>F?2BP+ZnMKc^a>h=TWeu}8LNQNqg)US$26#7qn6?p zSNlN(k?8g|gKeY}Dm1Ow*DX|xDHVc+l7UOM3apa|5?H!;|CzXg5jbtp@>J4XoKJ; z(h#oXXfHKC|g zC>~+gBn2kk;|HV8a^k-gVIO~l9i<+_c+VOJ^yHHRAasc877gv+FFS-YUvctVZ3sI* zao0I+06A7;y@FV1+m~3l*;I)mn{qp(SgY8;1}M}jKm}%a-Flf)k%@uk5_;o8p+xu> z7#k$KDW+WvXuu#6MkIo=Pne#dI2)SN9Az3?xM)vVIKBsSE<>DR_ZCvmWVo`9Wd)jo z-{3itcigx#nYX)Hn80s9|Jw(;pYGYb>eOwnPCftp^RKeH{;%t=zXlu6MysbdntAA< z8*X^)u~*7#YY*?L0Zl!wSz?_ zQ8xNGCU^sigT^R;#DO3ChaV|F-@t%lOI@?zM(C^fJ&-bFu(`&{QuL4xS8k>C0epz! zKALw57*E^YPO8PqBoy4Z+pJ<>Yz$1ORS1{m<1TlrvI7Ge@|N+#ndUxf~;0P5+=DxU^GQOV`Rz~Z{m-PN-X-?;Mry?yN;7grU4iKA~1 z^gOg}+YJwGJL7^gpbIX*M|=U!xL(Ek^ZDwjsu{Or%wyaBn;wIE|7Xp2U3Aq|7hPo0 zddO&V^9E=WreswPbPgYuXv5;}PP`F@OzT;*3R*?Yy7sj>$)2VBMi7Y~cnrmvFZv6U2oYWoED5=aZDxfJxCEK-KA9Xm==2G-35Y%0v$N9f zm`Ee7h&fOs+5E&byI@&SJQT+1tv$(bJg}!Oid7zqO^-J0#gkxGR^C6(7ZA2R?bO^54`i^Ojt4~zHIH>%72_Qhis=mJ;`+*JHFnx_w z&?+v5Rfrkro*5zhBq$5K_dy{^rcEfE1;jwXGSMkHCJ!2fn8YlHJfztHFp>g9EGAIw zsoMiE0w|3Ml@$epMRj%2HbAj>ZPD&$s~=vOe808*-1$INeU@?Ds5pl#2>Zjd66WaA>~g(8 z)6K}c3$p3ND^6TD%;}sM+eJMQE7phvV=+0$b~Iq~FKTY;Ks*UDObi-2Yf3g08=I%d zBy%>kbM)i^JAJ>;`~H6Ky?`xGmV55~{eIWf<-DJLpXYs^=Y8Jo&Tz{t*-{p59@-+s zuS6L(c2Q!`=a4458%qZAMc`VHx4#9OssFQmyv{){+E zh0m98cYA-QaA1_=FWtpK5oP{0fmELjv?g+o+d7o?V08g}Up zSxRa7NiGc%k4`B`=@1Y^Sem6%O5LSFkPrj}lv?RVxzlIJ4~M3uj$4Q5}NvR2eH35{#U$k@h3M% z^nK6lOXtp@H5wk}2-U)P|BfI$XI!}x;pw1?+8;|PPw>l>AeK1rjgXC`lp#Y?faM?c!kWl;tr2Uj%d**r~V4#aDV(7Etk!J|Tcwm~~PM4nx zjyNmLC_y0z>GwmMl-wIwRbbT+uf|sL;?9tOqgRRi@38NmeEo4mg`$&sWj`L&KgPk? z(eXjO(fPNdWZMCjH3@=a=Z%!IjfE=YNsZ?vQ)7}j7HH2mn@CWM8i_@13h;VHj#05B zv}gppoy}*&0nLHqDC3>2cMv7o&;P==4-0N8v|983y;E3!?srN=ff&RK0z z6JceSPf!oJrEdAq#RO-Gy5SL%tkOrl^j@(|VtO`kVT|^B={yO|+K%b04C-Hr3_R&{ z0w$_FG+J6+3JkPmkcf`UB%OWnrSJ2DO6P#$fshpvr?Wlt*1XQ`SHCxFqw7I?X_y!x z+3vU0v(k0pCg3CxdAvaY3L4$56!J4G?+aH=tp1A^#mE2kkn)r|9<4$apk-82< zUU|-cJRN=Rp1BV;=YAb$#e!4~1@4MhsCCo9;ECtuEH6On1&0;=-p3HwP$HFX2Qmb&lD2s8)KNTh63@Ij~ zq&;7Sl8|>&tstdvBwhWBv_>6ZL=? z7lm-KDkImzkM>J6IkMdnXP9DT(@{&qmPwDg+BnH zL^3aMbmE0N1>TWZIo4&zhdF1Ot5i*e#Z7OMTf?8Zoa5qPO2>*9bX?`H`frK|2(Bgg z+2o0o8z_;e&@fu}sMuxe^`DVplgZTf>^4fVaeCfDTT#yyQ|j1%KSmHj7>}*$ifo%q zGt30n{b!;2zI$zrfW1GFUp{I@No3*sCxy~#39W6-$cbUOQ|BI8*PaHQ&#Uj3bQksU z|L#`sZtZ1wSp9fl6#v%si>-&&Cs<3jn`JROjuqN!(^V+uT&e`8UukaVw`I0PZnZq8 zfUSxElxukQaz4ghUb19rXFPUxO}eBmL%${l-4aYs>`xJM9S;DiVz)HvAYT< zq>OH8HXgAv5Gm(g1ILJ1X2TJN4NvPwM|GS3h@dPBV4)G*R~J`fmix)dF1orbyb#B) zq_y5U-hGZ<6dxd@`MwqgIPozw#=2MQt?%uI?v6j3xLE3yQExrA`gwOwiY1nb9VN^? z@W8rlHok6ZF(EG-Re*~a{GJc9eO4yFq!h;B)J^aI+9KiKi+AzzUA@fvS$Il0l1`B3 z&c_bTBKxL-z<~Wnoc))C5Z!;d>8hngdEZv+EO3)zFY8SIx!o&rzd2i)?a|kMF0iX) zl&lw%&3dep_IJLT0Lc2Gry%@ew4=hw>4l~+;vIu6`%zbE(aT@&B2Aq5e7Bj7M||&E z7YcD$6;UwO<&bNkvyEI2xtk?Jlolnsg@+4DM6j9qf-raCfCNI-Fyy&^TJ#*a`xeH|lUWp8)K5kIJKrNPLin zYai4Ug7ah7YZvET3!&WAWvA7IfzY(wl_E;=Wbvvvg9eI^)Y*(^wc6MZ9&AISP^OjN z1BXi;!l*@faw}PMW(DW-Jk|P_5A8Hn-DD77%Mg*|lprO}q0L_imYK&om9%Honhiye z-D0}025?H7lE>Ls?J~~FPc+#*PKU0<|2j=W-$#olM1#_V<-PcKuMVcweK%wjeCF{X zrpbltmSzH^8#E9`Lgn&GKZ59}9BZ8GV1}H`qCcC@bb(mag@**if59HoN_;Av5Qqw& zKv7Q}&Y+=Q0^&jsi>^K+^g~r{HKnFw&X;!z=*^cMb$T%UxezV{x3jxQO)o=i&VzTg zX=v}n{%U6jOO+;1J)UzMD!;O3>vyaG!Z0ClB2O~nMj>!65=g*=fJyc1)x}%xv$MUJ zB>w)!NU839%YU=wi;H2}COSIN-&jm|G_CV5#ExSJV#Kr@mrbc(nE`EYG+}NJap2_ee`DWdu31)pIdVc#IC`U{Mfv~r1Pj~? zCdqZK1pNJAgYNVEszd>9Wy7c`wOIdKm!OwHQ5jPiw`dL{zTENH;9wNUN~qFCqhCS8 zlnKdqw{YlKLZaqa|2oVWN~kb=AdJlS;zbb(1|AxnT=|$&*NiOc=~=Ze+}6g;4S9M9 zR8@4;q5h@5s_3K5le#ecYIQxI!EgLXOJJV;XH|JYp5#Ww=N4sv?WM^+lO_5K4>o=$ z9@>)%?8oE+Q;X!y%%5=pjM5g@?&wK3mo&iRl`jQ4RDRuK%Y443bInJK6C#UGHYr_J zp;Y=e>pWicLc6VRu474!1(R&9#&RQ98Qb6RN95q)&~|DSQ9t27c(`i9rPd2KGuwGO zN*}6MktBQs9NG{~o#lPCL>vC6n-)#QKdS!?E>VLsT3+Q<^L%=PgaeSg=kqlM3@*EV zU1J&f5Rm<+>YrI6K+V_I)_lAEyN-^oO5-6Sl81N^pG_Kpwu8mvK+x3k1```jLX-V@ zktCc9yl_=q|`b>xl?I8jbr+5L;W%}sQe?M7Rk5!I$8)qH=rk6J9-pky*C9S?F*QVn}ubvBY z&4P)vOeyCs+u!O=L-0II%lmnK?r7qwqB`xdFyZ&WGqRWTst6WffZE`5bb; zQ!#@9BNq}1NFa|~UN=3$LwX}tEB0ok`k$5iVIu<-ghSq~8kJ%)LT=Xjnub4JcUK)2 z?3`aoX*@IIGkX^3a%o-$X&5^GQRfw8Xc!mY{CSfVJ0z+r^=`zzCd{^GCN;hICORFX zX|C*K7_Xok_}xoUx**Im;5UTF%#Y*#J83sVtBs;fVgB`7-t33lbeZk=pab z?!J7FpZpU4j7wmox4%kMBeG|aeggvRImJ=l0>eqf1M)4)Qcakbejd6ew-a$TgxDBa^{5Cb>s{W3+V^? zl?>|F6wh1x7(9=B)!YGk*_x->#$<;RM{X%wAzQopmgVR0hDv5Wc|vnyS=!QDLsVN- zyA%MZH@VVXHw2sk&O| zLyK<)o+Ie{j6Z3JgABu-F!HEOsMvZjWe$V_4PQ&V*G{;lX0M-^?{iv4LQ_UUa6zR2 z(B@#`N`GHtaiB;5Nl4IfHE;2-(pyhg=GA2YGY#DR+NyGbX_}p!UOgg4nh_%#tbP>l z_M|9APahdPGgr3nk*Lp9{ z%V$l!&)Yq@P8r2x-hbA>PhFq%L*Bv9wS$4>#oe&!^F)`68dvk=>tqMQ8<~jr!49j5 zR0=g;iyDkkCiUYiV#b!*2f7IPuXgvdAoKS2rm9phDJbG4JV-gn@M#(CoBgC zhN=7()L;FBp2EDwr9UzAFBm-TOPQUYq2LjwHRqt~wWD%}D-5DxEqOk5Z!=7NbR=hy z$#BqRE5Je94Y}FZ7X1SMh9rV4Bq(MmD2`65z!Kg1tErF@>{T3QdtUM0jvj{$Mh_k@KVi1mT7)*OOF~3dosM_not_&XB<#$0$CT(QWi^$MN*sIEfJ}gPd}t zJ(`@Nr8y_@44z#|vhOc9G>llKF<)esOrxp643*!#u6mD!JWP)1kcXF5qX5Acqw@UN zyEW8|VDUI)vpat2o2-bfqM5s0ssR7m{l8xytk4%DcZ#&nhuOvinDH=Is#us&m#L2k z114NZw;AO!x8u3}L5inOGCTIAEIkCP@v(KNcf^&eDTg>Ql|EIE#_Z9Wg zJ_3lagFD5Xj;u8H^CdfloS*)< zoBZ|lKV16_(Ct(|5k|0q{HJ9ix7y55-rb9{hq@4Nw2rS>BPsqmcQ#D>-e!{ZybZZA zZ-RXLCxV_om*KsjwiX-*EQNnqschL=caULhpoA{$K#DOoFY*5-(!#~q$&W@oO<(O{ zR~R)k-t5_Tq2lZCkSNB2Fcp@g+dh&Xw)^edu8UNz7%eN-X3x8S(bTXF<#??9?{(`t z>y_7zO`#D|{b?I<&kvj4ehKAaOmX-VPULlBq{thFp7!JIh@k$S1%jRLUz!K2n<7Y+ z4K0%|8b@$o-_mzWA6s0T5(x{_4uo-d{pF(2T@bA%jzvOo0c74^@dg%f&69MipKn_Q zHjlp}iG5konPFKS^6e-bnjN_zvRA*;`?}-ln;A76=jAfZr+*K}TRt=E+f=gkg+apK z7j7o~BtbicYmeX|CncRgrPEOx1A~Mhv{1=Yp?T3i1}H{fPf7W7;Ll27 zDcc3~+)?1==m^}~qip6efB>kfmATUwv^({d_KP~M4Mxm z8(9p^%=o;31`Il2wf-X{;&Vzz>WybOa%gSHnNZ|f4=$`dqHu?Ck+%%U))ZA$%r96g z+8D70hvKNzI7vri-<;Ey){Y&{TfMh$%j8L@8T7NRaL*;B)vcg9*Ai%oGL%XS z37Zh`yHC&BI$@#{E24mG5qAYI&=;;$%tM{CqB%@1KV&e-6r95BoVVID$ALExyY&0W+gQhs#Ka$&Lg zv-JzZXc*?63>-zn`19+n>*nhhCQnUZFeC{iuv!ba<2y(f(9rs)@gD_!e93?-8P%i9 zsLW(AZltToH*}U}Q2;$4&o{d)7l2B5LgD$;@mwbHg~n533Qh_!h@7PSZ;~#46*X)a z3_4yY+czp+PVeyu)ixFm^w6LhC4YVrB_IT8Fn=6yrA@4fw!{10d|li6Wtw|fn&5yP z@Fc_j7Az2bO}IDv8}KD-G5_g~8S1-(Zcrd!Oj@lCvBZnIJM%z-h%imVAsN6HDohRw zB6$0ERw{_!+xy$a`pv<7G?Ad?lJdjGx44lk1ypsJP%b74A6ky?$ax>|^VD$KbIS%% zeE-U%NKzd(dnoH!x2MKv#FqCYedXx#hHwW7NVW39Jl}?=f~(uAI~J`hG&!6IMGb%F z&AcX)2pje1s_uToDKWJ2y4!(m$X84shNaS$@nPHh%5#=jXisHN+I_Jl2m(oRf-!_R zrH9dA{7rnXW?N|k|Nad!{!qzeqIz)jJwv>Fr_6xYJ-+c{79AdDsfrLvW#D`2?@QwA zI&?L39(HGFFMj*qy*?~P8Cq_;qDupXYhZsBT@Y%msvE_FqQl(=45oAqh{5eAx0;NP`l*CIe^_fGulM zQB&7Q({EKQ&CmSI|H=BWa^}OekgS~}5^f&o1#l0(un-e~JSue}A>~6s3E3xvtT?bc z6^SDb*}eYYF@lY{6mSM--=sk)3CKc`-wNEiu`onjt)o}zyQF#TRO`0|M7WVy>_FQ@ z$EmqDAdWH-d1F}v=Czz$5bx+Z6(@dLX_L+iNH)TS=x@b#dj@=KCUtWY0>ZvWix)OhrwmNI z;WuQ*kW+2)Xj=rKOA7OYjkSipTi&=Eso!t<1qSI+K$D3Pt7X=(`1@&hk%i)t4ztj7 zzFc;vd2$5^R30Y>eL?vOmg5j(fCoRSbXHAbX^r}ewdkgrBvOqssc=4PqI7+%TuuK@ zUjv&wKL#)v;C;7@9h`GZMHngHo))XiWdBEFGGCfV`xV*%%vF)fPOIkZ*f-7aM=vRsTk5~O@)sqXbP z8%Oxjema<=R$SO?Z?;2Tq} z$>*>>qUZ$g7Okh-*U)>UfML<%U-kb>LIZj9IH0o2H;iT0)?Os9YA6;2vx5sLT^E@k z(g71Jz96~dwme%k5DIlrUBZPEQ&!6!AFZ07f#V+oD(rZ_rC>jxb2By zY$9J&KwG;jkPl5Lj}dIiI4#lSYKpBB5`3u7i5*{>u8X^4h=6*s$_mK8v|U(qsCf3u zo*{A$Gp=fbkZ;S#n-{-|G+vSYSgaR{G|rTuD-r^fqOif+d9~<(MetT%A0F}p7z_x9 ztcIymXIE$8G5n$37Zk92XJLmgv%hiN>@%WsbYGL=s4qsH4=KY7djwRx#}45ub*i4N z8dKu)EjLG9u4V~q8Vx+~4mdPgMWG#;;InF5@aaAoIYzXx3z3YJAbOev7f7^!Gap<} z!cRgF(EH)x7%A{uoGntzaY5pgT?pYv?^(JO3>0Pt$^c}1q`q)(Pm&0oh~bPHFcOT1 zlZ_Kepf)kqQuBIT2_^tj=}m=v{ZeNNm}rcmQ6?E_C-$EY(P(Q`h%@03C~+{NV&F$yA7Z)oV@@3Uf!DSOEY$|j zxD36bQ$#vH&+I7^6qANW$YWO__TPP!8Q@f6mTef`>s=kgeHX4Vx;t#Eh6t8F!0&yqL0&?@w1!O|>l>ZIZNqKvX^~}5o^IjQ&S~8XMF9q(lAkq* z=y)t}CGDl}OW1x*_K0sCvm~lap1o^Odi6)oN+;4YINFmDMyHgFT(vF7lU?a$?d?5( zrHfR;P`SuUy(K8I({M?U3n!+43@)%VOnumk8b6%i=?%ZpnL(9ygwR@(!to_Q_H6ma~YkLR$T(a=NP3bnb0!` zb^A4HRdZb5S-qI87}^<@uh&9=s-|U~>+h3-K)AVo{|=&e4#Qt%1K2vU)Lr>N2b+{8^`q1hMzHV0B;I*lyY+9j|=rL^7>Sd?w`OVb(xJKtf#Lh>Ywp)StE=bz?fN?BItz`tl#Rji^DE*}=dyghtM%lFa!`hf1= zUI1vpg-!St6pQLqG@yn4R8)Ah|1hagl>+@A|M)dUDdg~$U+ia4__WW0s3Q|935D_D zx<2Np>TB*1r>oCF{xUMQ<>Qs*Nt#;}yD5g_}(;@o$Z?eoWvGp5AfT#T$+STTKDdiA6i2d+!nyC?OF6W4p4o3sz}it9b@_Adq)dAg zB0g*yVN0ITG4RB>y8E9#4EZZjOKWVcq(IVu=I<|3eF>D{K{K^cDssP=UJRiS^_~Jb zu9#*#=s^XZ)om(;YFg{^(&;z;t+hx-77B^xs#03xLve|7NR?}l?Cbx&$qVa*0)VGE z-7xGA(FH0D9{H>=uQ|@%gs7-v!~2Z|3fqNTV&(TLXXUr5Z-`(-a~{*{Vwrb&W_?x;ypfD zsui&$;Dx*#_73p$wJY4!h*XZkY^v%B()_IPeQNHjx_C{4UKUaMlW(d~BSwfMCxXaT zhlXws{Gp^7C9kyc@&7Qlw&S8fOf84Tz~vgJx`>1n(4lHrByLN8FkDY-jTpfE-7?gU zDV?uTWcVEW$k>F~hmDB!7gDxq3oMY1Zj7Q8HpZwiX$2+ZOs3!^RdED4HiQrdP*Q@|WRG=b zN_HR12JGw}TR3lZPh0-YMJAENDT!LHN6DaYT#{2ZpC1rFX59%f%7_kFB>3Smd}ERF zTFn|>*ddnZ%T$fxF=YvVM9zb+C1n$yu23mkx73E~Uy7&s$w4-oKS|YTSUXx%{=p^s z>|dqr{;H>vT>0be?NTqhpq800yR)(tQYiWnA~T0#$MK31M>$g1ol{d%hA(RhS_6$P zEw4ktzLMcAn!ZF$*SwT{bcuyjQlWmkwGr*b`fSiE2!(pxKa(W}jx2)MaoqC;1?}XQ z47vNOMDzl3djoH{F)x2v+9}<9lUdr7FF08!smDdPay&qRFbl+KZm5t`-DZ=_Izni zf01i?VXX;=@l}$BH>c~>;UWbeAnFq&(@7a61K5F&Q2Dl*l>}D3WUOBkZMtuRrt_~Z zjnORi)X|v?WmTtYib-lS8jIeNSK+7-((_V;8M(k*McygG5}E$<%CG+n1?qm;w}-Se zNQ&^qicOn%K5UtK4{&A6oZ=`8yO9|x(4SC6*nad~k9{Tm-$izI9!Bc`4zsnbo?J%a z&LF2H{iadcA3r7rIS4kpQJ6KV;n{-EU1j|)og*ML5Pmn9W_W_Um)}=CWNnyb9;oH0 z6CdoWuNGbmiX3tPLjov}x2$wU^26(82Iw$TWZiNW$Lm7zcLx)GUDR~+W%DiCOruGM z#Bqg20xVhtVOn9Kwq>@fa){OLfOZppWg=$7n#sqzV;qqBg$Fl`T>Lhmk{4~OuLz$) zn*`|TBAU4>CoJ68Va0-PQvW7p+l2uFnpo8GY%|y}CX>X~8&;vvTSU#^w&jGB*qAdJb%7oW zWYfHHJf?fzF=b8LB;Y9x3j~~nYiIq)Hqb{x%2j%+9;sfX++Y~^DEGQ%Ls1Xr#TWAnogGRYIKdJbVG!u|CQ9J+V7}$E z1`5UosoKn@#~X_`Mp(Mb3x9If^kRCub*%bY9PMNb_%8avs!0X3L~Rt?vn9@z!hZ2m zQd%Q$Z3+sMd45kkI-5#~AmJ%oO<2&H=9<3Upcn1ITjEdoL5NtbLxd?D;j$ftv+6${ zG4@)f(}Pxi{1Ta^9UOHW2{e-MzZ;2KekfKAuyeo>1X4dEV~mo}jI5w3C6 zGf+0pYA+2xz~*Bb>?I>e&9WqTV;vag;cJeF!18|3AmuD7P~Eb@XUyijo_r&Xb-aP< z4D=+JUkRc4onfnQ7?Ikzn7f@slW`pi=ZAvVaJi*kvFn2-pxd;GRq%HyaX#y5*5XeL~&@sN81I&Q9=ulPwbFp z!btJ#jtZ5Gdn|~QD{R{Dx2dN&8s)}CD!uL=P$Wr;PA*=8sk}L7WAfyXV#)WZd7NPJ zGjE8asu^3eE&ZAf2XyD$jSYHW35BZVFMf~WR=yzQqX1lI*U#B@Jv0o7nLu5D&#@Lv zfGfZm_UMUCB2JF(z&5vz0<5j`G<%#i7^2}t%Lyjp$;oBz9n|zl0}tu$;X>=NA;ZGE zeUZfrW@;)}K)}5>RN?5o6-$Wv2Jf#zFhqs7LlIpn40sa4=~2_X0Ki*LOsNuz-wIm@ z#D#ggKY?5nJ15Xt^8RRBg|wd83#cHc5uCl_;)CWI4_=S?isRP!kQsJE^s}DS%_~E}K6L=fIF|Qbdoju-tXVN*Q#J zDbUjkou5JMBF2X-K&O|@@ffa^8KLuPZ!cVOct)RQh#{=Z~z^PlZ!BB$9Ca*Z_Xgd?=7-$m6UW>I(+t&P*Jx%#*Ys_m(ndayGV=csiBVozlWU3S(JRfw{)jE)ZOQ5r7F?1gSPf zynPK~^pqlaVaNJ*DRe{^DUbwyjiI{uUycJ~H?oOWmvm?o)4nWQ9JHhi1i1u;$SdSp zhA>HbpZ3&9q|4RT?z}a1@`7ig2muoA4$sjiz|EvtaGn7ol8R6W4ztX;j-fMe{PaSU z`K-Fk|Ep}yV%|V}##4H1M@VvE@e$WT!k7d!8&wMlWNXpQO=|3QDO6)+=-3g?_v*b( zj_rQ62D?Z*zppx<(y~H)Spl*sX#hkiN`%o2YZp~J2s~_`cJOcw^!w&Y8%g};bhJ6= zx(yrOE1c+wH3cpi4oJm-DTE;9&&tE?UCAMUBI*RosYl2#F21N*GJa7#8fui`*jrslF%2}1MF*B-M` zLX3ezJKy$#@V+J?2+TZ~x~#3!_?LDNAv+4DGr^*2w0F>8AnqOd^OJ2%`sQ!Lwruif z3pNBA8rD=&O9A^~jY|Hp5c^$0gO2XO%**6wt2jD76Eugur1f#c;dyQP%PW5^jXCSI ztX+0HK58}t3!2Vm^JUY2wH(IL&>5r1{Lq{gtfpBc_pj_BQf_Qa!jVR?Nc4Y)4 zMLCl}-980QLoo{{0D?mvoZg!s7BKsNv#Fm|pZEK)`X?T$Q3T_ipp(Vq(J|@n__g>< z$0{$9C%JZFZivGuQOQ>$he^iJ5K<(HFP;bj(%qi=aS7$KB_%g7kPtI4VEJq=1fN~* zZIT@C9i5=Hl2o0TJCGose-+FBRx_U9k0RXp*@wRVz`Py#@?$7zWI*jJ-p9(S7ErxB zk`0l;qSCH-C$rtTl2Isu-y7H$B0ye=>!}LhI|-57#R27X@D&D)Lfroq@Iy{`OK+>t zl6pE4?#K};DdQKe?x9}zpyY_i>C`VR`b|aLid&Tck+n? z?_!cqqlW``HbWJT3xOLt*gzTK$46zzDt+1eKH8t>ZL~jQi=8AhG2?l&qcx~1P|+qq zr!wmo(XaX+bN!zxzxn`a%+->lsb><*yc)5l1J?Bm4tci<*{KGf8vJ_y>u)V-lby8r zOqSl+jY$#d$MM4_Wu^VH5my0ItrzYcC0Q(R_8Nnq!$$FYB$#LSqQQx9G>z5?uZgnI zYJ}qFSgi7N;4K&JPgHNy}HpC&J`7t`9)50%+8)&v&PFP7;;zhbC88w3+ zDCUQIPFOTz!sovV?i3`($FQJo6+8}P@E4b-4nEhf&>Y}_<>-kIuRVta!*@D1cy}0b zmbhUOGQM)a05K5pGPTFHh(1#w7MWM6L;R8z8|*e`$dGonTjYSmCPTWBA%pJ;0E-|e zr5^8xv)aC?58tZ62DgBlkJ!De*;<`qIRrkO7Q_~oPdMO0_)`7CSHH@8RYrG2vt}jg zyp-YcwW^`niv!@7>{>QJ9)p+i{m;iTbk|4B5)|0DxrL7ghQ$QtN7(ERU4HVV6x1CV zJeJ}69lG69lBzC|3BVn#IQaQsxs$qVg9S1Mbp2~y?~DhHXShE889rF@ZG#I3ab>I> z`++T;z(a2v$0D78%urSsehs?~LaXj$*hK>dym;^57n+WXJoenx6AOaFu=mU4P zE9-R_*&Cm4RWAA<11AL=LBg6G1twF~V!>w|aTpmLwvy=xLL9@hm`D+h&O-)+yAs~Q{gt`@pb&fhl zmchwEwkMc(Z6<#dfq_$?V(t$?d|B)L6ErHfL$u~mdB(rl?tK?dm^CbU(Sz|)W}O&{rJce% z8Rb@|M0q}rBqdP5-7eSCmvDRdPHbajSqWy`UnUwqld7K8i;LHnMS=I<08+!hO}VIa z))5_#)c8+uA`FPU2-}Io&ydJ2ol(ez`}e6Uhz~AgrF=e8G~bl%VbaoR&iymk_MN;Y zP#WmILC#Bps(yi|t*@lN`5-<{$2w{HPC>->RsO7v>~&em+{U&Lf`k3>BR+3K)SYW} z-`{r@iZ(0M$T)!sKx&5&YKkN01Lt=LP>WSp8O&jHOVtIA53?qIDRYa2Zd0olKg)P~$=nST1T2l!3C)(j{r9JHGSk3FVtv)u z4;LimzgAOjp{#r~SEBP%J0~pu&-w7+tkWOPm<$r*$~ht^8F;t*M)u)(7`?D7f8bM0 z-J-*FYuZts`kPM34-ee|IL$T`VR6BM6!VfPx2wC%xz}6vh6V_9DQv~;JW)PMRTckt zG^3Yv{|0rboPBk;Qp!>c3so=|_u`$6gOZZsW6JcI{jZe#msd0|UlXd|Rz$o&`BYcA|Od z;I58`*&S;eL4TW*(xuEPGvwKz8)!@LY3N5>VgUHcToW!tXcUA@E;bjpLvMqKgX9WeDKk|iT{9sB$SCg z5K+^v4OC|b32h`**LNlSztn_?NnFmDhO1yjx*Tn`QbSmuB#;ix%3N=ui1SzQR)o2bUePDq~c z4uS5ubpw;j?b1*}nM%l{MU~Yfku(=k&77|npS}4S5rP zdl%5qc)vk${bu2mWS3VqweyK3SUUVYF6WZNgt9LGmR5|~bGp0plya@{_pUMcsz284l@BBTR6n(4GoXsG8N}{{sk8|qX zK})r$Nv6%c@z}J%BRzd+?=M@Wh3^Vac|Gn3( z&wSNPV0rpfVXQ&<>baw=05?L@d2HRuO`OPm_Hend_>OsuO-qTbo=f8Bi3wc(P5cs{ zK~W;J3rsdy&7g$Z*WQchQUBxnvO*yPF#FB&y5b+1gO0aKIkp@?Ix}OPhZFM5u8b5J zE?<)+$pwG(b5#ZaLbtxdl2YDM%vRVa4ydyN-7l9GVu{f1dKs4l^ z-nqdFVFH)Qmt7ff-4~yQeNb5B6>59y!u6$OglL|s|I&Q@@zb2^2uNGY%Jk!y@+&5Z zG>XI3?4UhXyyV(}abiZ_@`K0!YX5Zi4pqp(p}8q|ugpq(;o$|C?BRS`=G_>VALYYA z9U4U7hoBB|f3YA9FCq7Cu*O$$1-?P|>fc7dFeYf{J_F6{&e~MJ`hmV!4J+Rui}-4>}2g?fd{$^e}X^;5gJ+f zx)Jl=PI!0za(aCI(KK1u^q=CzW6YQd2LW_Db8e^GRf866g_o+(&Mg2we##qckPmYW zwpH}o%f*4r$v`%Ex&oiUv;%bXb8O#+tQ~jwhBPR)*7vR&9Zb( zhcppustPb&L;!~$qCbH&eFt!mKheC)?Nc&uHTewLMFMP}ymHwv2Jk;udAOgGE{Lrc z!4%MKlg-)gz9LiKmvf;qrfzd*Iz3imcyR*fT0N?8u-?7L0xvY=y0N2>^ zyg$yF8c6)eK~fdK$tCvFGoCmQ#AfH_CpqY(V>~}@N`6H%dJo=AY{>pd8d8-Y@-c$v zn9sfObmz@UaU|mzS?rIe1L3j5*q9H-R&Icj=FMwZiw-3uveXkeq#gx43|}a(m5GN? zgX@$IVO^0m=zWIN>@kx1%Zy7(`IW35yHJjWy!a%fFnPMp#%vQ=7xg2PtLHRdYt=f! z92W;^3It-bO8?MW-TjM}h7eCq6V+gW4(2oFcwl~+@KH0fzy7EPUZy2JH?nI+qwK zO`z0clr12b+6zMPfs@MfnKkt(nbZN-kC3v|h300JaEMq|w zLv8ItZI|HY*=JVatEme&fN}(?5dgM2h&f<={+3?V67fkhkHi)}Gs_$d^?PKAdu5#) zljO==d&ih8X9rAYt{w52lEdNAa%wkF*r7n!r)^X~=&j1-?Hh+@2h#z%%PYMoEW#>2 zr_WwM%PMo#k29n1Un-7uo6q6yPHFi<+$pi$A~h4Gy1iN99O|3yU-)!2y(Z19x%4x- z`FzY8b3E936H1)>@+rGTPkKF4M&CLc!;-NuG*nQ2*Q^QY!IBJ~?_S~v+eFmJJ7o4w zufAg>Dh9uc=C*+#-qgG^k%Juc@4kcbR&I!4&}r>4kY3c}sC+LU-4$`#JD-bIa8y5- z|6z>{4kcgJsT{qT|4=19PAS+z#f*<}Y*sv)dHRDHCdQ5rh3f`r*_04alR~l30m=Qp zEcFi$KUw1Ae^OvP>@)hRh{;{LLC(1ce|1nRX~WEB_S!Z5t)C)P<@h)cea#D;$P zM3(JkU-(G|5(i0hFaC!OSzgWqGhUBOTzy;PhTT1X8Ezn@Zp1knriaR!pk?Uu^GPBJ z5=+XQt<}`U!Ylwb&u{)?_S-%MosU0#ec1G-;@Drn!tK*TBpzHzB-qjzrf}Lm{+9@q zd_)1sBcD9W&@s>>Z;mh68S=vu>Zm%K3|}Z4gSr-s=((SnwVDwP8IK#TF!GAAOW@0v zf6jFgjmO!0rOhpRRAg9?nl8hZ1nWMhtgC`TW?VVaa?I2_q@Hhc>NF=rBo6i|tajro zd!S5S`fohD&XyVekoA?2Pt5NJ#R)q-%YL6S(1=PGG5}0orJ*KJCQXA{AD&!2G(S;z z|LZ~+2{H~Z^893u;T{Ww*gp7_a#~$~>n9eUTe$!I zzdqbbY_eh)9eGfFb(*&b%WJ|Gm8CP zTY&uxU)VhuGW^P($l*UoRuY1fCm|GH3TJKTchtCir^Zv5mTu_Kg3m$o!H?Ybjs=39 z%9p=(4TJX#zvJRU)OrI>zlB0zvQh`VX!$%vM3&+TdU^ltV`JkKWVh5}Z{+$(ud+SB zhHC3SjWDHt_JzwLd990J1!mJTyE99o?uMhsL77uY)eAZBs-yH{$2d{E@6sd6WTUu_ zL6)5wJj~@2j6uzMG|%21_qP@|Y5) zKK$HNZ&BX^?hKHo)hXVp**3&)i$oJtpB8loX(~3}X*P?Nnc7xF>CLGzW^oMc*H9tKC7tR+uK^zhC#4iwOXr~>I4fbyBEN`^5 zY$;F-MKxc^`&drB4F7a_I47rvkC`gyQGCPiC`&hCQ}m86b7<+vi;-)jAU8lVRAFw2 z2`bBGNNp+8ySGyaX0BJ?VJZvIfA%qvNA!}qz;-)*?>SfhhpJ~8##w0xZ83I5`N)~? zErI`q3+`KyiQkn+^B zm_j{JBrkrOf$wJavYs-3u8wF&!#~_GoR9>W%AzGDxbt$ zbU!!`^=#Vbr&vl(XtbH8?Z8KaGKM-p< zHE(M(Qo?zQ+WR;l5Iosivytao=nKLq3m6bI*8CKCXueaj6I(pB{8dFbLvwqpvEl%d zo`$7Dy=vc7t;hUG-ID1$Gfa{DZsInzB3lk@og~c$Im&$8yI=uci9%1f$K96>Naa6! zUiOyHTjuTK0dY&Rsg!whq*l?!7V^H;yf;bzK%gIPxpfR+45CT^vSlV`)0+Z?+heO5 zX~S40q>dcYa9*P4nd67CG4m}-iS7CaR{!kJ?`|lY)!AvT+yXxkLPtznDEY0i`#rRtD>2?@wldRFfxspyo9}cI`w*L@k%wb;j z>v^NoVFPy3?WOCRj#EThI8sO3UpS8X?EoEn2_e_VNfR9V_`hraeumxQ_KU4f691FD zZLu@;IdM`QLhQXpK=PyWWJeIhrH|I6WuG}b8;W@};rIBXgAJEIEI&vH+0Itp;FGb) zdi7Ni=FueOu>zTZH2PXHCJb_K`BgDWApjeZ3fL;R8d^v#!~)WjoviOy7Gq-s-xFol zTlM$vb~12rm4cVR9%r$QS?PWqN78si#T3NR<1cUJrk0zW-=j=$| zH8luOH~+~X`$}9zlwF= z@ZstPXx{eH2^-+WPfip`B*N??{FCSK+=@h^*4g`hE)#MmpdLGI%FAf<^~|;>OV^*O+S5aoaP(Tg*0wrjLr(+fg$n7Y2pnf20z%b1ngI;K9_N;*y6W zBgK~!rB4$12HRf2GZi+1w0pN`(31R6fa>1&KUDs$8OBAOhZQZmEIVp-tmVKWCHPRV zB;@+$A)vr-vHnJdNtmgP_DIIhm}pz~ljN$lzU&rq$$=1I{W)Kqk;S*KX^UmUCgtX=fP!F3n|1ukCk{MnI0q^;~iYv_YjeLW8 zFHIP*d6*(0c}(Gkm?bi|mNUA}zNTV9HMUykj$+}G$&PZo?z3%G&#wN?m7LNWIOI#qYm&h`hb&p#=RY z;ZCg(guJy<7!ABvLtk6}`B0}u(-vd9!QM{zpnvj=9u!81-+u&azyXbygvNIK<(P96 zr=I$&)IH6T>#FWypzQ@wM5@pJYG_psD`f+#5(5w0l8Y*K7*tS7@EK8Yooy?X#6(Lk z2bMtPpT!#3Kq2cDs@aPVelyeg72yJnPNKMs+*6@p>t>}QVoWi^dW`FF3Icm#ls9@BHRiwG~OAQUMgsew&gS+>*^8 z%{5o;R@34{P8A#c--??g11GyMa&bv56>JsIf-OWAso{GF(x+;yXPkAJtDlO=^?SRk ze377%?UkSsM)%q+fW7F2o+^6C7ZRr`>g>QQ_*4+qs?-6#?6s>AWNYj2`PYvPRA!Gs zvkCn|6MTHETB`n-DhZF^s~QDuxtUz0MI#?T|1J#xbHiDln4!w=`eY+Wm}#`JsahuZ zJ}F!-9OFctugDQXIKY}ew>GA{|8+b?-kc!(u;Wi=0n%(;&ifW_5r2Q~M&s}%Ug1GH zhSDZz3MaEwwwM|5wHY|EhFaU?Wa8dyDZYK$z!`1N+QWJVugymC9eRttGFBGX=X%qo z0Hl~1zc82!?czwo0f?${sX9MYEme`e-GVAEe3HR}HeM51nM;qS$=K~#xwB+}N zFCPY>1c^bgFys3=5lQN|_zLBybp%$A)}K-3`{1XWuR^-7*8r;dTgBJiIbW*cr2|wPdD;!4~(Sq1_S|>Q+za{D?T9iT$wpT0xK% zLby`qR$;&QsYmSvG z3*YP+hu028vDb{TpvcplpPGDM%VE{CKE)q1#Os%YdFeWJCdoWGIhJ&JX#Z&t$G0ii zTc;AW1&~JK$h{-m#fp4V$b?#Yrq#+X%wjG2-!|_L5%2`4<9s76(yOaizCE=zk-mFt z-JlW$fV4OS9O`%ct5yj5A%xA4|C6{PYV4b?o*!Gc^=)N*4p>K&;6OL+kTOwi_E9hCgRN{R$MulA9l0g6Ju1qqg92ts z9-(tsK1~{uiWj;~ly)XI>nZY^-3g^ub2mBQ$p>}r^0yg?5BSMbdw%(}9A%IMdprU* z%hr9F$VboEYdAQsg^>L7UQpo(AU57^j>KEHE}>J&I@nW(W*S^QwTJvzwa$DJn*HrC%gnM``z zu)ACSMnJun;#u<>R+Tr39w5t|R(&ey%!}8kO&uYM>}ulJNnNP?Ketuf$4L3aIDrm2 zh%B`hCN235uRs6syo{{l-ydmnt-TFu)g)Z3LJqOt^?&5BFN1yQadSI{iw-x6^klZN zs2Hec$P;;zY@{{;CB^c4o8TY|tuLU(KO)-@6eqNnTXdy%B0r$Gf(hann2I!e!iLy; zo5>tjG5YkHQ6E=z*$EXp1^Pj-SwJ_MnNdFrx8D7H(Mt_N^ecUy*@U${d;SQOCZNK2 zia$GTf8QsIOJ#_><=O7aTK}>z&UarYbM@cWhkCSe(-{St;1ya2GhrO-0aU}#5KXD0 zg}m5xo5HROcgxneF3-&ZV=PeNX4{aTpcBpR3@pSJ&&@f&_% z)U%%Y7!!R#4PnEfAVc2xRv0JgiT5V2~v7)A_xghc*Np&6%>$-cf$6nh^9OO*`jG$~{{Wv+1 zPY0tNTQ=N`__)XBwN|5jiGFktD{LVq-gCwF(sCVel?%6T;^yrd?zi${(+*jeBuj zf%x88Vy9Z#dP3?W;TO%Dh%YmaZ2#34NWn=WTZX$H?}vN{_okPGINXQpF6C+DpYN7 zM(0-@1GusPtZo6_6@32!ZuZ`1z{W!oab17^E8XyY?UEoz54=nG;$K$; zI&<%1^cCc7h{WrbF@F_Q;cHpHPjl`7&TExVKd@;8f-3q;(q~^2zFfHrW&>MvZ!y-S zm#!B!0&T=E>I^>{$bVeHcsQrR1CGjp(U2+uFqxb1N~>k#SK3)*OC~Q#`xUnIYuBct3#zPsoMmtd zx|t52$}RmAFm3W)`n1ve?t{#G?`6*#Zbh81Xkmz}X{-?_==h)#63QXy#bQfANBHw; zv5ckgEtkN14C)>+|LGJt5I^^@{&x)v_!L)b_51;`CDVGZRQ&mw=qNbR4o9VolQ2xdT9Ne9UHFJ1?4=I4(nIG^~>vtSUue z3!*t^~s!HTIrnYvS?zC&zBS= z9pqj$Aq}hhosUG~+*r?r?8|^JKT9qywyKYpaKw-GW}G^>u5O^`g#w3;jf%jDI@vbc zoQCs$TkNVrcZ)hwsEIm8m>!LvJUv_Ddg!{>f?dXYXfaQsKC@WOKPr5U$o{HdaFxo< z5wk}7k2`M7jn6i}T=V#nE2xrFeoe$3^x_l=xto# zDwB;oc{(}ua+HSZ)u+X%C;$=V)_Tf+PS04zO=LP5oFM@d)4JQ;qf+HV)Ss>PDR+9& z#?$mD3sMxdhy+jlYb@7=Td6qLd7f6;m;-?e4R?|Nw=caB^od!D);SZGk`Pix4NSIh z5Z%&bnWM@SQ6S9p;%)YZ@h;jpR>JGYNq}MAvh16^3G6A5^57Fy0`*80KtI4k_IZRS z;-NKorhU1HV87y7zmhb^kewZ3IqZXUqp0CS>+u5sYB9DYQVKoq>AYT?dpr@FzwQlp z`Tn+fHwK5QPQT1w#^AIz;7ak@AylavOja5z29?BTD^kVNV&ZJm&;@di+rmz5+@e@&%I5c>fv9~v z)l6J(IPp2=rK>^}%5#YwS!84IJ(`sP5$U_*^h$!09uyGTb67?vZnNn7j~pZ*3H|qL z=)+AkDIemoe(aY3?Wr!uu_}M5Pv1_Oay03}9Ra-kq}d^?Fe4g+f)Gwou``i0AT9JF z@_34K$R-WWk12?cydEwDv8gF*jZp`htGDmbp+Eq}Nusj^$Mwe%l8?CkCTBy;t7~wF zaDzEm1i4Wf0p$s%G_R>j^cc8>XZ7dvDsrrw5NW<`#kJ(Ce?-z7x-y1}y@YHK{hPA~ zy~?g1bQP4oCY{w6(;pkiKeyQ}B0{JfH%mpAG*EF{dyhvVj$5pr|_I zN@bWiG-GOaz;gFveyR!rUJ05TNKgo#d-tzE{xGrAOldw&g#E~bFd?n775_e+gtgUH z1kTIaxsl{0Lkv8op(p2pfPGB$kze8=WO5NIS4_!FU5d(Z5KLp4GYnY6|Mt+Nwd%23 zH|R0o%T>sZh{xUNlHXiPMp%Jrwj~y7spE2e5uag(aPT_aCUMJIw}uEN@soGnbKNVme=TVrP7 zGsmsH!GL%^C+VpW_iwwNY~9DB-GqPl4Iv>q0kL97YBwW6bfhDe*>SP)#!+}ri4U6t zf9+(Agl)bK?Gd3sLzV)?&l;@jLf{}9n=h<_WQ?EhG3iT*tIX?{F*5)~NoZgH)%)O( zx$0Y?=5M!uJ(@M9n!g!IoOEwJ;3ylfTPa4KN0wXt;-eqXf1#`W4yB|eX{n^aBR4!2 zo&H6aC566-WOT=C@jZ-V%DB={>`jci$x_;_@K%-$mm5ej^Dxq{5)Mc}RH8pnD+D-O zl5B=AVgyDZ!Zp~_DiY(sx6RB$I|gQlJUv5ZZv*R}B^iH#JvNtPLkD{f!NE^6WG zj+>1W$F7Y{`Q1+__qmi{MRvJ;7u|GH@2nQ*iN#CTF%^wsIT4Bh-{J)O@1#z zRlbU0q5KI8Dq(ti(LkSD~f0ijacp%tx7v z_d~!ni+fF137ss9dH>sd?ate&KU{PPF7Ey$a7pM<=ZC?EtAwokBQnp*pRw#=?Q05Q zN(eSZZ0g%E#CArWiDw!+J5(-vimHa1sm8Z4;kzv{a3U1NeX%X_%IIZV`Vb<{g-z+@ ze{>}|SArC_M^6n^Q?$dXQ7TQL&#ls&<9B%UiKt)6X7gr20lb0`UqucGfp!MVJT>|R z(;3~F^%Acdi~~x-jup>bcaaxRVYAaVe#6^F!B_71ZRbcVJ~m%L{`B}nw6iVbF{xf~ z<}Bk+A;TaO@gO`-^0t6`fTX`nQc8!9DM42F&GlarRCRWtKsJYKHDNRU%I}7Cz0@|) zw~Lb=Qk>h*n#!=I&Yj8;H6q{)K=-@$RUwoO`S)YWvk)Kzhf>kn>N-yEEgox9rQbWlyi1m=3>BH-&*YEs6H-~;ggdjqV z)_KL$B%U4buRf?)JsiX)SrP@&&PONtu**Jd+HBS?3TSOA`SQEdv6S z-D*{>bzKaqDe>KQb8hAQn@{thfw&E9=MP5<-sc@__Y9o!PM2Hbo%>qE=IW2$b?t<6 zEuBgfgTW8YM}uEwxjk$?&GejgCwx*d;AK4-+QHW1mOmRB4j=}m2t#gHzN|JlA%K_8hzzl!xV$oeyo;~MZwNc*#_g;T) zG*b83W;dO{sccyWs^zJ#lUUQQ_156V4VBIJ9kPb^ye1y($#!-TWv6*Tg)E&eO4e{y zXNv8QQ=NID10c08m_U}QLvTv%dfZB^>NAz_k<;9g!1SM$Y*&0QU(3&{utEIsw7Zqz z|NVtSLU2L+PT!`hfNK5qwM;9`Z#~$pFqN$L_dowAc>7!?g#2a1+(!sp*#4R)xX`)0 zXCla65}*nRLFg_S|sfUe7H{*b?SpB-*O&`7MHsw zUgicnzC)1dYr+?`lC%i<|m;jY7fD=id zs85PY%*P_lLvIu)4$PSLKiT+fQB+KD>d-3fmP46h{L#+Jl`Ia`n>NlCY(+;u$wG_l7nU~O z3y$84w-PV0+OA77-m|T8t9DaKd0I_xziYoaY}G_5;@YgqAC{j4yc1%}LwVE(&_XNG zI8Tsq`>xS*AWT7LX5OQ>aY`6WnPqG_*#{4z$dSdrlL_X!k7?X>Mjbk3znw*#vb_hQ z+4?ICM}dHU1(TlGF$;ff41dgqpE!zUr2034=voa7o$uL^`8$HtPy=R zXe#=_wRI&gjHMk$opm#h7tZ172G{ySM!9JEx7T~CpySb-f{x2GAz(`za&j8IwyO6qJrwiGn{E0LyNzU1$- z+NJpY%VjG~i(L<%QGPby`j7kjH$i~z^x{{Mde((NH`z#KJ>{GB-1@=A%ZMPzyEHy$ zH!v2$1Yb3etj*fhi7$&3)-AOaX!|l-34YH6VIr}}^Bc&z7%?h@#$0#oio|Qi~kS&j;qTA4h0JOWU~G5_xQ#wUP5*}vY-~~ z)^B0hulxrMeyOOdc7@A*x|#2zf@Eh;mx7bWas*j*^t*76`pUJGo21e?&ei8QMPe^G zWZqD^u==67CebX}W??R*G+u#9@v0?1UrQE+#W3&^&ebpyYG8H|iNAXfIX`p6>c0*! zd^tZQ&695Ostt^AJ(X+kFn{BfzfOMbU*Rh)sqJ;Kg4bWeuB4@Y^hYO>`7I;3w_xBw znP@hI&@7$9?vwJz;GAD(mrF3i9@y!8T2Ryhb91|H{;vRCQooHe_AOZurWkcfAC3mk z&#^w16R!IC{QeB%;A5ZG``x1=p)N~=l3XrJ$+7r5{tg)X1Yb*+j>^L?3r$Xu7n^{L zAi>@6rc4cHhAzw7tCYDA9db%k!582ZD$+<=OAbh11Ue?GwA^hGaXhNlUc&MuSv_HR zTV1!$Mr@ykNqhXCXI0a;lH6w_*Qoc*`P+jvXsTyagE*4EV|2$V&<9~a3J~?jG5mC$ zIgXq*mg6J+Pb6?OG_^GzO^~}HYB64hM|qiPY@ad%W+bLl4mz~!gteEhbMXehGrA(* zrWLqZ*?Ri+_^mZ$HR8$`m-eVne5^v=+uOcVehVK>bIZGSGW1mW7OQ`!TTw`-DdK8u zYoJCH@hDXsXVByy{m9po%d+3%p6^DNO&7g(PP0;*Zy*14D@FN4soBjk~D<4qPk|MK5 zGk7kn#1WMfTs*r>%Rt#(CMn|}Yw*EqiwVT@-Q2XTvkb?8f-)!R;0gh`D`(v=IleE7 z-keipui1R(Z$AC%m6^;#_1eSz7HfW}fYeKKL#{1JfcO}DihS$JlkI$4XS79Dj2tEOS#n0WKkw*ll(D8upIVS%Tl zADbpzRLpD33?XWlTIQ(fP&y-%Uzq8xiIA8~ADrsj$!0(?L==fL8ZPD8b-MdX$JM%K zeDY7j&GqK~qY@@5c-P$tv)nfEgIL(Y{UjP@e)+u}Cx=;|MtfunL6scxjyLVZArWju zHvudcGffu%NW*cUp_>xzQc#wED|e0&|AOP8q^fG8b<%tFY0q>? z4Y8n1o@|%(w*gNgK7_CvdExVRo@v?}h2bzGOR<1dHT8I@NB9f{`!TSPCg{MeODK$l`iC0;@UB@>|=jI8oDbTEoT_NMhwbrBJ+pX%T*ohNx@Tl zCIu-nBu6G@@?5D6*Z9T~voH|7F zh_lHhlmR?LxlD>qq+$o3dv7w2X_FG8Ja#<1aJ4u3*0{de{k3ue zE+jCwryTQ(ox1b+8n*mI$rl(w3zgtZawix=OAme`e!5$xn=cM-cd0u)<*Al@cvJFa z$T}w7I{H5~Q#g|mUF+(iV^o5S29u`m7De#o57y3i$BDa^H}f6~z<a%&Sh&Bi^Eew;CqLiB%C*Ma>w1&9ll&!;D;7sLrIs*wg!MAR3Wv8YXQ8 z&cJL^Ar zd~R!$Wbvpq5?Q(Zb21LF-`(Us51z&86MdF52|&M|Dw8S5%eeB>|52e@zx(4WoPHvU z`@3(-)$QB;ohyQV_go1S!9#1WRogd{Q{Dhn1%xP4BhhrHz3Z>Xk%VYZtx*y!N5}+| zS~w0jtXsx6mNu*E)j$Mo5Q*0E{Y1v<-#oIwg8;>iEKl{Jn9P=bM@?VT!k%^%zAUI5 z1vS%7#Z{MtlCR=MQovf0mixYHFw_6~e*9r$0m1r|Q~T$pt|vjc{Hv6BuNAYDT~%;j zdD7HHO-zbwY4(4mtsqs^Y9+mx@Iu&2cF0Vp=`%>FEhcjm!!$NqnX}|}_p4PBLwKO1 z;(>OxnhPi{U7og@&^E^#)aU5ntpY_BR>}QSjj|9H%*E3j#Y6j`oBt@q?Fqu&!G)MK zyAoQLzcd)uCO1A{4*7-Inn@@4NC^{qyvdNY%*J6FI857sXG(*3L>xk+ak!^Ji?#@p z&qP%0oy;IyB&}1BaP~$bz|FcQ&Jsu?e|^G>0b?mhQ4;N_^`eM~pwvL1KK1dt%1xq< z+YPW52};)o9;(_GmPDaJ_Tu$Vab0(mo4>l{SR?wY(%oWC_FrB_7Z07{?37}Nv&$y0 z;b+blljCba@ne1UVtn3=f>bX7n%2&)g7{d0yGA?ChMM|`^M$jWOh79Fqt9X-;xv$D zuZ&;H%PcBlKNKU+vdyPfY!fUPeIm;zHjk(qf-#iI(V(twHYa2PScn7BX()LD;Wc?6 zK@jK6{)#-FUTT?HXH>I>_-qIteC- zI4f)Op=(I<-Li&&(o|mMhbS#@eO=0_Vi$I7CKfOn*8?2?u}a+~9PrPAOeOJ&;;D`; z&L4J&ygXTSdaZ; zd~o?AaZ4ju+8q0NW*U66Mvw<}-|bvXH^&;k0)20rt7bbVUaBK#5xld~M}hckF-X@* z($22fTd@l;)Um#tCmJcLnt@9l=Meuii5p^!`AvY5;VKE0drjZv;HO_yL6QYF-~Y4x z2X;hFClql<02D$bTZlAoby4WVXVM$w(ml@q>8PDE3LkAt3TJe#{3)PAc3eL^4J3EL zQq8CJt|JzY@}z&)f}U^RJLq!o%trQjfAS;Uf2juec09%fV8%$hv* znrF6zxg1Uzmp(?Gm7EEPTfvK5eZ1y^VH$oEBZ#y^FtlBXv>7lwgb_X=RHyxr(d$t7 zl-Ho-(=R;9mTgV+JR9V4I6X0)Zie8U`3D9n zMzyH>CiKbkb#MhC+K*cu3sJ*t(Cmyw3>VO82dVD6X0kn)APnyTLe7MlzwA^zt9i*KG|h}G%>6NNUmegG zyS~HfmRbs-%Duw_)-TDNi0V;4S~dQ>Fg^YyZ3K8lceY|;N9k;e*RMi`dmQq(vagYl z{|Hqi06ImpV)yS)mp&!VvD5_sS-pr$RUN+mseHLZ$wZ9$Wn^(i_qfBA(Qb7g+Kq>s zo25*M$^;g^2^=36p}i2bdmG8$5^NeoW=`0MD!n}Ko9iH^AhW^DDDW`YV3pvVwFk`LT<`*( zLr+Dmp7UgJQbZl?gCc^5T(jIOzOUnGt>ry#y$10G>L$L{_iU6Fx z_!|RbITHP+5z-G3HoPq$!X%CNYaX+qF-Fv#H>X==_<4^~qI+xqzs_$YsFu=&P9rz4 zfH8t8haFyX4nObqJfN#))|vbWOZmJ*=- zdeqhxVah8uDY9`9(dVkx7eTR+=|@P$iL-69tPP`}z$U%q=fbAF&a(7|v+WUD$QJW* zm=~N4-cgVgj#Lo9U$Z(2oQMp2?jwg)pcduKU`t_ ztIU}yCZH*s{F<-k-Ii52E^Y*x`zZFO2+zu)QlcO$C|zF)QcN3x2iyPgk(`_K$`pg^ z9FMN7OUQ!wH|McIIkN_hVNfH5eFC%}_C~bq7)b#BRd=A3WYlj&Xk$bY=^&$&*S%mi z$Cvr@3>cA?A7?QHhiWd4SP5mlc73zq|}xI@{w!9Kq&u=mTK^W-`RUyRk= zE}A!1ymph;wzUwS9Zl*sO9A(xZa_QXef9;(FYbQlTQBCyHVY$;4vN#JDD0$ef))~n zm%mYf9!3gk1!%|1a*;!}rOixlJrSnIe$W3*GEw8ds2KBx2K)KQ!$U&}1jDeR!>3t> zT~6IuLohV~kdUugl%LvT3#KTxZq3hzNia=cNSwbgh6c<0V7Q2W@6PY-$(w0k0ROW~ zCi!>PdrQn;4%)Sr4NTtSQPk*3ztpv-fXd;3O2fRcI9bl%G5GqFvf&im zzqmtj%5b(Y-C#Sk4P<_Iu)FOdBV0Nh5S%^V=EE`}AsjULq5CZz2-KfVx$i$^cz98P zX~;28qSK$YfV_NIzflyQCM6LytAIKaxk#Sq;e!C^_gUn-u9Run4?{v?1fV%Lvp0*d z?h$liHKsG3KnfzJVcim5R1QSf+F`c0?nK20^)Nc|>Cfw$OISZe5MZiEIGEb6oFoAd zd$)eibsKk<<%AX`IJwIQ#*ptk(FZFhN}hD+`;u9W=-=KEk7I9|u=|BcY8u@a-q`GR zzcIT$ar28)hExW?gPzK5xEnn!y_N%!VvOtPqTZAVZO6~2T6@3N$*H`d9oq#2I~Zj@ zwF+H@11`p(1nT$QZw=wooZv`Q)UYlZTKX;u`4S7RJ|GF1pqWja)cRTY>as}w)obDZ zq-b3Vbr`C4?GYkbaYAnwWYdz3lC(>?gSp$x)jTH)5T&h6+^M$WqY;_ai6SO_7ow0R zHMn{oUJX&*5Rnk;mc3GBp&lnEbVa!m-bi6+mVu9ZOpikH4fFr?hiF18vehmdsLHD<(?dvMFiFgfPPbj zdpQSLMU2Mmi36JajF0xr11}N&Tnx_P&CLcxj3O0UvB;N;IPoI0Av68j_etZ4jn1Vk zhJ0ll>9fg2#v&!;?8((e^xrfBpxy>wnGUlLXEjvC-{ZxGDLIV)VJLkmlTj~-O93mO zAa5sW&f8o=d9E15YAzk32Pzyqnjj$R0|XM>>S#@g-(z1?4NP$z0QsK)}xP?@U0(Fp7AMboKyE3pd`j*g)uiGj`sm6=~YOA>=D zw22qj_OEXagXE|bg_1^Q8?^N@L!skA$?d|BdODv9)WG52VY@a9xdU%86*ONhv%c`Ywylkq zOZz5u)k{#etNW&$vh|0H=M_GD+T!B0bT16P7&+8ezClBb=T@!b@XK>kQs4;Zm8^J_ ztKpdVO~A22zt2b?R-`Dd(g`-EW};kmvU5uJbTERgoUjB+;>#QmqGC#PQ)`{LfVR%lp~9{8wP7IcXu z)TdrSW8nw*>s>-YKfFZ4kjrV99m$4I4)qq%Tm}pQZh&)~2=5{`ywzdN7OX#cP-BMn&o;h2co<85K2V>B3E8JBl@7 zNoqsbF6*y5MY)se2x32?}NC4{K^d5a{eQQ zew>s$nUG;^U-LipFVyq4W7tJ2&t2+_a)Ox`clj6x#+dk-M!(J+MEd|DMfe_9Q5#lG zg<5eqc_8$I~i{qadJP z;<~Ylmb!roJaD1WNuCWtSq@BoUJm3N4nCihVZ{Yl+)hZK6?whvF7!ULJ8I5o{VXBl z%q#h9V`NY#pYp=q_$~DaC(%AQ`}9DNI}V=#H6~3TPY4dpO&*MA(@Bz1=Yh z3(EW|zxAo&#v2epKCK(G0v%)>#y&_Iu2drAp^zAG4~gNkxcJu2FKRyLw6i-IO99g3 zCt8`nMdcia9PXqHemCk3OV2(HtDF}Kg^_X97hvR6zE_0z1U`Mify;8HfPPeyxVVqV z+u@7W23<43q0M|a&7p$RIvd(^NrUqje6P0*f+1%qJPCAEv3knNNhKz5?b`Sk{xU7{iue5&W zJP5IlZF7g(kigAms)#^5=~dTZW!y$@V&*_!zO-nM@XVsibS&~~YG*k`KoRPTc}q^R z=iWw4Bwzm{_ru_gEqC?riy@kD$4z|lw+D7zpNxB#HBBGeR($}PO6kfuSMZn8Ib$sZ z@J~y89vm%HZ325@j_c?o3aW+)s(Kc^iS^IXWU5cdv5D1G9VU;{@YxJ6P7vgQfogC7 zr_y;rwR@b#Q%J`N^~aRqKoDkiT%d6pF6u|_k3fWr+Fj;vlP5(@CD{bUCHvwM@A9fQ zbq=R0aU6@QxT72UK=}ik+{`jR7P>b}ZAy+L=Yubyr5clr2-pl-%Ag}Mt?wMEU5fSm zYnqWuuung=QR)?olncSkTZM(GWz6H(M57{i(^YjhdO47XCgazMZ zW8nq@cs9-B+B`b^ujO*wtS2QUyEISOmCjAKI}%1-4<)c+8Tgtz5sICCl59^*!piPy zMEqH|Uii%RZgo{3uGMfJktqm4suc+Ej}ogS*2$7lVt=qwP}0#?kBKcmw}u1wiDQP! zYU6=Y*+`BvZr*-#jJ;^!2W`S^<8X&@knK7S#~{`!E8cLB+8 z)ugsDZ`rc~IhE9W>UB0&XVqH1^k{p^Ll^0e{%;^T)$?%9tXPCJwy* zl?D?7_C|1fnsZxW+7h3&QFE#1dM+XPE7D(j+G;~>{hp(1pT`1hGkD+px$PNB;5+T~ zg#Sx;5^0MQ7OSqm7Z)cdjn5>j`J=1%Jwg={6BaQC!iy|Fd42t<^c^PRlv)n|o0cxZ zNd1}s{wqzY5NFEBlWifmOish%<0)J;sl43Y0qn|Ixu%z(ub1nj!(T$;;RLlbO;IEX7aE_TMCuby~37&{(^x5ZzO=bjt zcU+1dt`_Je# zl%3&A+zG4BP?Ev0tPflF>BP~~6ug6{6ocJQoA?e->iPeCF}btEm_lD3gT5VR+GznW zpRJZfhrcnJwgyAK6N(eCn)g4eB_#mrQNT|BKYGa0;^6QHp0h&%k=8KH>Bg-_+wYC0 zr39cs`DZK#-P2CM9OA9+`l{cfnXggaPP`)Y2}&j2J93rJ#dbxXXU8t_WsBzY}l6#sFQ@>$q^KSHyf;C)7GvO_zkosdF`lKm*(Ge3@J@CpZQ}nR{kYpnz)FO9rAmj?S*wjBuxgkJ8M5O<-hK~oE4}G#JxU%n0*dZq( zlhpg8+qYW!;OL(*Wo1JnquU7XF{4A&uG6Gea#qZ<`MC5E&NpB6A_L7#-CA@Pm*EQ( z84Q(oD()cZ?w|UQqlp0?fuAKA22bWIN)+qtwP_*f!riZ`%4QO{lMohwD`w)$#SjY< zsbu2`&TmJfOI1pr_jglLi(J0D@iM>rnzQYhKFINIY*w)DXnKJkSdshFP;+B_Len2* z`NgC>40RF=SmlCymc5mL|L4r;??!tbPukiQ(NIi_-S0~!Mx3HeUq)0z+ixa# zz24(A$k1F0qyqO=0|5|4nBvL7r*eXQ9!BObD_mriS|(OUMT%8oAz`?Y8(l7#>H|!B z)4U2t4(;8C^5g+$btA{+aGLfVv)O7o4HQBr1MllC<%qpA5y^`!)z8wJdK6W5jealN5RYPN^87G5vHvN{0vl>G9eMZ zDjH`k!8kN%(U$Mew_@GqSDPc?Z0b|j5AD_3(e^7Ev}ngjlGQn(GU8`Y7p6%1tPj-u zSSw`~3hy&Su5D@fe_yUC1aeBIqFaKdV*YS|_tx-$tipf{jv+@PNjcI>=J&$ddfs9c z2_}Ucirr85P!oZ_D#$@i+`1atQ5p-+yOJ?sXm4CxRgKcWiKTQ;{|edZ2MyRvfoq32 z2NT;WJ-P!qbE+z{w_e?kxXKPrsTgjHapwHp{&M!3+nakJ%sDPi)ANtNuh8cYvvsHe zyarZ38utWnp?C<3>xdOqcfx3(iV25e!9wjz9V#Zr;7#*NB|{ZT*hV>=W&}k}6UpS& zm`JY{GVhjmJvpZW|Bt@z8g6RJm}2d^LyK*CUUvaG9yYY$)u~NaJmy(US68cbqTbu7 z$^N17=SD4Bp>1hwYK7K(f*$p;VL5T|AcsqFV|Yc=Azj&vTiuWJ`pXqs2#2(L8l%t~ zJN!K@9p8ECh$_cG6Rvd3=lV7b523(28E1Il5wt4fY006V?QgMTZ2H;XB}4Gja@~QP z)JZLno&1#GL}Gjiu#QRP#3zFDZyy^sBUGCrcpb4`FKS;uY`yT_#Gvn-M=HBi8rrHT zG@*>?6~pu-*)RPM)<7x0xRs~`N~8p$PR zBp3n|1N%-IiH|8HX5Vo0>;MIP1Sk}fUM3msDbTm>{zO2-zJ#W{a!2#!pO0cv`{=GI zfIVQtVgfsh4k1|+eC-I#z>W^NYHcDL}z9-5{uV2 zQwK}$LLCr;;$^1e)Iaj%DjMj(DrT$VDcbUC6Kol%0y!v%6>(o&GhQR3Vg){((%_@A zAT~Re+RhR|AtnM844seLEU#gJ4Ff5Sxm-|SIh<@T^D$ti#0JuR5MuYCR3IyJI2S57 z=w?Q)o_pPOBqHa_Lx3auq3^Tz!y#8^H9oKDvsdIP3<{x8QWQKoL*aL# zv82K@1xZahY!dt}RhNUz$EC9m1>r2^tRA#ZOlJBAO)&+a$AkIo*foaxv6UKw;k=IuyTq}frXT}1*IBl#izK8QBn^D zNC*lI+)bNF_8fTrHI!Qd|20wj0lU6tZFLH!bc%!dE076EksxZZ4ucBvt|%!~q|yG4Zj6`>&k8Z_Bdhop0s;@ELH>6^ap)Cd~Cryz>TU zJ`k@doTAYFfvcv|#eeaOUZUy2>v?#`;jc;5zZ-?563mA*1vqpKJ`4*JACzkM6<&q4 zRJ3Q?3tV6!BmjzEUc*u45s3C`Op^db{bV^6G;mWe$A;ic2nym4B%PtNCY=`)#dt9` zDof{XLnhV`uTK>cIb6+bfh&|M%kwyP3@9uJg^~&~+_?fscnji_R~@I^dWv2^;RP^| zp0})L7KmyXo~C*#M1|!ml;f6f0TnVSfU_Rx3O3OMlJc5q4SvL8O{NftM+I@9fPxIr=7GrsmSBBV5bj8E}Cpzu_LjAl?3ZcBwy6&?yr zeq@W*c3v@z*gqu7_o&_@rh!&ia~qE|7AUHc1UVRjiH2N9E%A zR5G7yj#b2yS=Lr$6Nwysnz{Nk2~(7XuH19KR!eN`>L5}m>Z8&mYbAWno3V-D{7{g@ z1`3S-@P;xX6&3f>V91*!s3J|XfqF(Q^pSS)?9aR0NPWZ%`ZFIm5Ct8w5C zIUE!r#GJdaF8mfbOVDUrx4+LbuYHpB?&}vWo-zNgLIDz8q3|Wr7hQel=t;_dUNLam z$ho6?Uv=^IH(hVzgipqV392GMAubU46z}_n}ZDD0>lo=n~p1wxmNgeCHVVxyoS{E$ROzyci%1P=qu$rSr&q=p!HzM$e-F1Vurjd#&a^$Z6K; zlCYq-VPG$`gk&JX*B%&;dep>}DSS+>t=4Q%a|T1wRNZpht@U=WHj&RH@@zqi6{}*| z9KJ+zs@RlvlOL%ba_``ORy z)2C0rE1!)#?b^R_*Ldg_jFcV3KU=OOcz7sonClLQ%Oeaf1yJ1Np}2GI1rm2<3K=-; zhD)!$>e8EDBP?V@M$69#@hM0+^Wj}hQxv}S>Sb@WZth?{I)@*hj)sh}AG&sJYj<_( z8dFZ%NJk()cWB^+FyXP#XAjf_0clG;zD3iN+v>HRyEax|Z$8Csw^0*osgAS7D5n+O z7!ft>=Bwh_>Xt$whgZQR;{?;>Qbio9%BE@E5EM9?1Uy-Tf>(uG;ZAL?RW0J7TLgP42JA5qxx_sfhl(j0-`b zvd5-0AxLfY(%4dYYyP<3K<$l){F7>_Fjy4QtfoyP(+MGFsNP zX=8l_pe|5*5;khBXFSIwI^u-EMaFSyVq1o+=?{|)^o-f;a@+QEH|z%b7<6hxH$WVB33 zu;55jgMOCutw7Pab{?`<<(o^~>RmfxE1#l(07c^`j{S1ph$}AYKW)&UX}3=sG-;ru z$1bX2+=NmJt7TJdLdK+?HvU>Iv1zHzv`{RK7j+0IhTX`eD1y;{m&>MW+Y4&4Nf+X2 z4uolRP^ZH(2{E9nlZsH8_3$Z8er3@|K~TJtslf;elM4mK*x`a=oDCNX4P?B-!HVeC zV8Ore7{4VbR7$tmF@>fSIv4p`g?BB?ieN_YED+4dp@;hrMf~&f)GPPz12VtKTjVX- zDJXb2Cin&vgGNsBP?)L+P)H&uUbJA|>}mabML0$JqF1khBj#R8JRz0{a(GAFIAiz7+ow%3C{zrq-88vg6QyIS zb3k!xQ!35aU@=`UA}}Cnodl{%v$h}O!IJ()cGV>6fufir2@JBUylD|wpqV4QmNW{r{tD3Acf00#wqSBS!o4I75|MjJuFhu0+5iZ#ximf}nl zM@m)FatTh~GDmJ&Rn5hUMbH>C7H(bkJi^p}T%Pv4Wvxrhj{r0UvzFmlAt=h2a8L{j zP`pN-W>gOgnnJ4w7TkHm$NTrcBDfa(U*MEZ|B_|{(yclg! z4#kJh836J-gLS@4&z0TZYOx z?cBLEwR4RWyCdhR8oUXbbrYPrFXrhCugF&xn^FZ)k!whj!KkYP3hgwq>{VVP=sXER zRiKTa;FkrC9G}{LAL}Wu=x=H6NE{Rz1{$KedDK(EhkKVTvEPzmacfO@hYFzJ1k5(p zwQpHBhXbPc7AGu)?P{AAi$rcGe&?z~NB3We=lS=`vwpa2-I9YK<#faT4 z1HxVb3U5A)3PYj0`QT32^aa;U964gzMF9%_FL2Up(uC_-dKIpPMTv2KEz=?g*6pXT zkd%rTPDRP9KuzcwsBoSp;-L5%P*_YVC|DZNxZvolk$rc!9ol{9Xxr|QBL)cyTV12; zVfYkdYSWVqiqs~xXd;55mK~2HE%AmN9t&QCV$xJ) zHl>JET0#+scPMyiNnSCZuM!k@35vi30u(!zOI29PN-dfjoE72S;oyE@m(A6J0~D6( z+N|}sbLNaYv3lK{vj+)9XvE*QbKmM)RwFmwvf8?P&#qo|aP^t7XU|{&*xTRs@Ulz( zO2`?q8SvnB(eD#d%hU^DO@bX))3<|4=z5P2O{0R0#Oav$tlG=t< zb>y`>uLA4dG5zwRyGKlDYnwIe=+Q%15JeyND_xaQ)X|L5sIlr;A_pgqi3b@oP892t zc3Uo$=X!O#5iYX1+C%~2QfVTgzRbFg4t4QV#o3T&6)>GOt}GV$>N1)th@o3LfWn5( z`(r-3LV*t0O#H!5U72q4F5UJLb1sMQE>65A(a6wv{U_Lu%l76YN0#qAcjh2E{xC)G zgXeBJLg2Cb%sCm6gJ;eiIe6yG{9DeA-HrDNEMqzQ($@O$vMZnWyk~#+yVsVHaP706 z_1x!OvTT3rR(Itv=m;|$x-v#_gfi-hE!*T%*u$eCC?-q;3THjA9}bH0=mYVKb}yf< zJ5aMnSkIPU{BC?n`VYJAzRO;ElTVD>zBDw27p*QaT@mu!fe0Q!JQQ}~K~PxmK}hPD zDAF~#V8O#cG4jytL%Z8r+jcL$agy>B9*TM`sc6n^!f-TY5`-R(iWWlBnp{1_(w10C zo@Oytl?RI~xo&Fm2`Xaj;KeHB$&PWhI_7s$s8UW!Gv{YX0RzE^afE%r-D}pIJ|7e$ zGl4=*g+amEF|Qm5JWAe#@|*UH4M1V-IJ%nu`#op3EI)DvBvzk0yK~RBgXbWEALyAg zDxfZzaqjG@bNz2Q^F;sKZ=A8}?74Fn0gc=YEKJ+c)@8#>vaI#!Rsvgh8U`7wcO}LB zc6Xc=t>TkYy9g+Z2^V_kB`DAoC<-UO10EK zfJ9I( zaws=AO;O5MxSE(jf%e!T`#}sc;P*$)-m~-Uo^gBDue;^QHlR4O8l5jF$Y^nQ_Q;W2 zo;Y*%+!JR8-9BR#I^>pf=alla{1zVyPZ0N8N61#f1W*J&Le8Z>I){$fx<#$S+vY@? z!k#X9b^F9=bFYb9ipYGJ{kTt1gk>eRh+7+%doP%G_3RPmT)1SOhwS6SKK}9hFd_U$ zsH$GyySJ{(Q70*_t2hr7;7|@l**0dUDSVnjP!wT;3lFDWbWPiAjK+kvLyLE}wM`%_ z6%?#F!*rds(qbcG= zO#~_GD8_JIuppM7P+r?bq<+bfmb(jz9~l&-5wV_jDC5Wg`Xp>ag*vxMQ5Y0};GvMi zS@JVx;(7v*R^8~B&|}BXt=@La;m7u@UbJZJmN|Ro96WQ-D&n^Q#fpWi#~wcT#1r$| zR-aj=UTh^Y&O9-8hAe~ZgAlNH1_c5RUOTuOgt`Dh3WOB6y0UB&csUdZUu8ZMAnfF0bXOPz(~n zkM8y(_!Lf4cqm9{vPD~~Y;xIr$AZ}tCM+H?t^c$M6Gjk%j3iajA1In(VvOyk$Tc?= znOH>sOcahr_QOLFF(DQV3M7I;YNB)|3|rzh zw7+Qjg6Y>xa9p^?)eGnWe*E;ti^*kDYrmLss%Z+ah@d!t*n*0(GHGW;;nNgOQLxSi z^I`QcV}gd5LPy8N!}@ZeWZ;DtPNHmn`;8$*WvVvL=xud{6{ZNBFUeW;=9HeS3~D1SqQVpQx*Bzj0dbNno( ze)#6&*nKGq`#XMuV%gcVM~<910~BYb&7gJW@Znnqj$KH03vmPlG@v0WtJ~2aThVy} zKZ0)G$Z)vk?>RsbA>qwOfC46Xg^<)k;aB2aeieWBC0G$e_azBOwoku)f(5B;SHsnu zMqu)z-ZoLj%|YclN4vU6saz^5Y?TdEbjxRINGzQF016k8lF_7!DPav-N5|Yr{XycQ zffx1~$hZEJU;-$JK+T_U^I_F|CB?b+)+tzz+S?$arlvNLWmc?6^HmBd=Bv;Njg8q% zp1DAriUfN)3su>=syvM~4OO~_&)|{eun0f!G$ZR#XlQ6J<_wA-b@C}-LQvoxxakf| z25cA<>g*N`@-YPhRdegT87Euf=E#78%Gtiv<7bZDx##SjgJYKsIDX{p+10Dhp1G0K z#My%k2H&`P(Y`YaTgRSJD;|IEZ#j445?3$>0XVSt-Ilx-N4AyIdb7-Apdx_6+Ie@j zF49B*gG5Xxk2_;b1Sl{cBPTj2t}m&I0EKU-ke0AJe{gYqbkO`Vb1%>rqi2nQT7LIZ)W$b1^}Szl6x6qhtE8E22fx>Zg!c zMiU5dC=$34nlqj<|@BOTv zF=yYoTNcZFENok{f`GM6P#jb`Jal6DZkv5&IJ5}|jfb7k@d!IirJTB5r+nm*Zcy|x zC~zrAC-5>TgoKB}Sq~+zUc3D&$Y6b{oq4cpa!w7r=&A(^F1@ZVMTjd{`+40RFS(3K zGNP2L42sci;Zf=I2N$CR#h*T3>d&-CSC0Z!;Zj!)3YWJkGoXi2#4fO_ zL`EW$Q?F)@?54rM5QtqT_19p4V$^~KcLgYVB7C4OpI9L%Xy(R?G%LbVBbX?e37HH! zD}1ws;jqEeLeToy;Ug!HtoRk0;&M=>@5q@OXBZS) zm+U-z7BplTM1bqYRNuC5&?HZ8y}N4YZnK00)5Rwk2XW^q*K7Kix#Uhz_|&E}#kASp zr+^9vg;x|n;gj4~-#PEPX@mL>yY2=m9hYC~&qryH(DwSn#q`m`HqL#`%_Oj3fFy-B zcE6|uMRW<|UI)d?U-zfTe4IC(DNXV9uLqaHpui9K3t#6Q(`psQVPlMmf4Cr0F={Jl zYgM3Dr4Cf8t)?OkQ6P;X)T`p;DvEm|ircH=-eSasBkqwnaPO^g;Wl;PN-!9&#^?8* z^W9HGJ(k1QLecm=efN3aAAkIDk=0yXAJ%t|S(fpQQKP2-YouOUH-d#ll^im*Hr zgg|#(Vo<=u>I>ze zbD#Zu&dO)vft-H&nYTRo-APyee9C7_Z@ugArHGB&E_w2Ur5|t*&?>qi0Exg8&5g1c z1RSWKTgCpjVODAszRn1N0|yMhyY7Zd4GKw$ng54k7cVISDy$k$H@I$O_-efYHGXx= zG58hBxBv@*vH7|P3>9(0gg6ou%rF)d=Fz&T4}96U!QIb#$U||lQSt8%X#fR5iV{#1 z`FzTvSanq((DFcpP)y|7$$4N;6ebC6=18MYjt;h8RA3=4dJDzaqa@8s$ux=4Vu?#R zpwTENJm`@VFXP)3)$t%(A|qB(1LmT&S+(|8DU%bT!Wyzgmkf;D{fVGhA1G{z4WPKK zfp$aI1q~8pWA<6;1OmYf3-$^sm|BI)W+ z3Q0Pgsb>E=lg?BS?T-x2d?!hLb)1tn80p3~aQ% zC&IR6R4fM*B*JnIiuO1~SY63Nf4*=6ZfZf^2Z^z48c9KnDq2dHCOInBDWQ;!?#i*B z#^Pj>Sz;u~S+P_{KD~V>dW3P5f$`O+%7UoV zm9Ks`bH}TR!IQ%o*5Vh`q_33LkIV@2SprKtjXdGE^n$RCEWv608htUlOB*n}P z>UHu@DA+}Mf=pr=j44lG^QmYyqMrE28GR?Q)_pi zaO*=x;D=DCL-#^6Gfa_^I2RKoL7}|F7e{WoA}q|~L6=Ah85K!MiKL+DK8Q(!xxr+t z|DaKp1-c7qL6PW8?HrAhIgcq*4ir1?O45USoht(cerc%~FEz#@u75wLnys@D(!*M| zyADTG<1HDv{VGX{O$0@-kQA!}#ig!`jYGf=3F?yF0uuI$Wkj5fS&_jB6!gU9x|t;V zBadXJW_a@CXLnR8HxNpYYx=M?TE6$m4^G*slY`(zijdF_eNc0AL z2K61VyUw9=L2;(y4m@EeB;|0>prGq~lL}lK2*q|S zKw;`bD@c8WP}C|6HGKafGNG&U77JkVaPs27#aIsFVmDNTBHTSdt*l5M&RlvP)El2$wjnGEPhm{Hw$w<56 z6r%@?mPY#tUJ6si0_IU zid7*Z_~HpujYwxVOic^CQ3Lm->6q}yeJj1vTgMvsmJ2?0VrZ4yRg*M}6X=(=w z>5^#y#C5|EI809w{Tf%eV`DP$ntZD+M*4hzr)nJTw?&?``yK?i6s{A2-^~g^E&zpB z8MN^J?j%z5%@R6WR%o-SmYegTFf3LS6i$82hr*v4493XabytgWUaMPj^lzn_>t78Q z19gkBE?fzPpa0QhP#`7@5TFQqdw=+P_vo7*3Yd_n00 zEbaHpJzH)NBZ7tRQJe#qru6As7-7ulR567B;T4fIp)k@0!2l4UkdpYGcfesia9q2-C)Z+2^5BZ0sZ zH@Emnw+BDwn;ISs0mTJ(ZRz;ROiwcppIrlWXZ{t69Reth_Ek2pzzU(Llm2MC>;7Xl z+vKLlutMAcx#eY5DQLxE)RR#ocp9DyVPJw{gea(kqDo2DVD>}php%6>)t~O@M>zH) z6caEJq(}oOia>Gw*K7T>yaYwC1ZEIlW&l3=!_9TbO^_4oS7dXvKc63@@s*%JQq(g{ zE***PNQV+6MLd;_6RGHorfIU3{51(5`$tD zYzn74M1(R)e_Ypmi=dDPioGyy6%;-p%5KqZAGK)kPKfTkBZQK@KJU5GQCmIK+{koO zNZ0@xFN7xv8|=~|?D!GtXk=p}E_g1W#rM3@FA1h?H3Q^^3p5|*QvM*yX>6>4UF}#G zLLo!btB=JpDu5xhDFR6W6s(c^?;?=7iA!~?4isTA1=U+8*9}yyMU)f6(nB#aGSXC6 z7eE0L(kxtv>_nr?0JopfWS015Bu?&iXcfETRm6JQ|ZN5;rmb2EXIvdumlR%{; z#|Js?aZtHPq(Y+NZKz1363J2|QlHB9LPVsqqomkJ)VlcMVx)6)Qbm6hHPt)<6#SH) zSDmsjc_Isw<)OIUpm4FPFi;^V`nYw>ev}CzC{PJu2CR7;RiY}q5DKS7WLs!jBdsC75byr zprD&b6Txv7+6O4K+*qmF+oM2n*fRZbp69~awR0Tu@}G?BC5Dm-pd$TIRbL z7U_@8_Tz+@x~r;tzve^5%LfUH_Jaz=i~4g?A8)@+P_sB$Q0Y~+k$V1AT4kD~#v*Cz z@4%ull_1;=5%!f&aY9g!Vo6A3jR$eTk9jYK=2{^XUR?176d6HbL*>{F3UVRH2q5td zOM(hba1a@wBPbXly4SdGb;BKF$gG2c&oobh9n%AQfDgVK+%bb2;e>}epwWxnSsv5E zVOyJiFC3T-<;+SaBMgd-$EK#IY41Px|4<+)7C?c^;Gn3gle}mexbx1sW47DRYAM{r zMPIL~gCL7eUTa|8^}L`^Uy&x4GEr<(MDVDpy8ZUst8RaAi_p+yxl!kaL||pwIiL^< z%B;Uwg!@SbY%@yx4>{zDD~>qAx0rv7wOsoIsSkiCs9niKM4gRB72KRGALML`BxRbV zQm)LX+H^-Gn(EL5n39f$#T$uQ>d0W6<6;6rk+PUgux53rJsvIzS7Lux9_k57II zC50E91_iYd289eq2LeEh`}7B1ryUowFlQwb)K{Qa=(@j`Wyt_V1T;&1STI!68ETk9 z?55bu&e+Rvu+cAiaJ}&jccuI9iUs`@M5LF^l@eR1R>A&Yq|R+6a{ai>G2+d5L`7kI*QY>!S4g!S0k&n@Q;5{u)z;OGbk6CT^rmOFrAFQAYqi9rcDs0p>}uAT#g z4VhBP>l-ov!a*@#Qn)PX4p*xhZ?Ur}7D8e3CaZLFwc96u7%tx1%w)Osk+fhQa_6V3 zZhWa$u`3V7NGtC`P~eB~s%aIFzyj_3^jSm&SOA5H@JtAa(8~Nf6uVwOa`KVK90~-U zg58MaA&1tjd;fhN?^EIr6xf)=uwuRay^JKyB{k_GG1=dBk)xtapdy!yp+GvwZJ<3O zvBpv~m5+4Vh+1?-Ja%oW5$%D1$mbQFFbr(cwIqaP<$$JC5)>QUI1dUoLGk-Z9tt?{ zPQ~8~{g)MI3Iwk#kQ^4^1d0Y{V0avQYH&)eG)Ja|q=(FmK$obA5<4G!O%B_*rg<7D zBoXM?E+}mJJu_kYre>xyYt{&6=%D_Jq1GkRp(VCxmXX%+NS6LJyISj_cp;|SPkbVlmmg}LNF!#hn{%GB zrDGyMu^v#YA}E3tLV=73dETqApkpU$+j!#p1^fSw-?<)zz^$*GGX|^~r%% z+ zTm}WF!V;QRyK{2F(xmysAVJPjm#VMc<|~#gca#hHYbo9B+%a2vFx!y^4LYor$b;g2 zG~#e1V`74ph7Oivp~!ayD;yOt@!-w|#eQB=cq$E=3}+d=xnnU$K3O(=_B9`_oBdt8H6%154i>0pRNDs?-#j2Dn#2S;2Gf*ltkWiEO~6C|g+xW5K}=A9gzr@%CQu(@!bWFi#g5&r3(FTBPe)K(LI@LFXt0cS;A6@hRh@`829M(h82)zMy12$*3pfg^I~odZ=43V>jJ@xwbn0Tm`J!bVr>K446| zEIAQW_s2j2tus$DxMzP?9|dJdV+e_EYVx{Dxf9yE@iq8HYj#+rv^hP%nMt3 zL$fj--l$-AP#6dj7$ytkgv^2iAV{P5CIvpZ-T)L%`6$pmg<`4U+qm`ycRd9TG6-aK zN|KBViWd1)0>xJSxzFEz6)# zOj9hl%4n0=)m$8g)=GgJBDIHEfQ_FRmZvp`Sre29~9LT z>JyaqD^ii~9_%KDRT%8LwlD}2g;7C~n=E&iV=>zIXXE5OU?N>AGOIR4(;8N`4A3nu;>0J>Y=wFw;U%%&-&q1OYj#J!>NuP!`%X zVb1T<5|3Oo0hn~$0>xv*x2eM3Rf zfc`)t)U3^q8^FOt2(RFlC|uFklkrgCDui|>P~5mUloa!!kWFD9)4T4gyTZy^)~7x1 z;-=x@Rtmb|qSkpOthSLmhoPsI;!IN?4vIF!zL}hYLr{nbplB5o%VdQ?5oixJRTUDf zx1X7ezpf#7H))9gbx0&o!Iwf7iwXfZXKlogcXqAFh@#6NBSqKJIVDX)6 zS4@#EA-rx64xKL_F1h7bLIn2~9CVKbg|Ejn5CnxU?KddK1C63fCDF~a@UE#@7>ZW_ zf-DHs3pd{}!wui7p?X7ikE`b-BTx`>G3Q{WX2T3t2!m?Hj6_I-px|wqPC3Qjw&+zx zk*pen!XXhtv0}?c9*TYDOA1y9#eKs;OIErOtiRc!L)RT1rdk3gQBd&3t|hC~%m_h& z^jHQ3FNREfd_dE$=nqdtC@MS>9*Vc$&QC7+^eS$?2xvLGWrr5*pCh*0PHhr5V{PKs z74Okp6}_lepUMG&9V>Va*!+3oHfs6tXlWLV#kb z1{qS>l*F1q!GZ}%ig{4935tLUXH^Iat(T1oDz4rt z)xY-5euuCCfFm9}Seq(czDk%RRcdU%_m+F13lkF=(cL>S5qTYJ;skOZ7j>0$xm4Fd zZ|5k!>OP(ltiour%s;fSiKxUTTZ|LHYL9U9Rdt6|5u4n;Lk$z|^U}W8^q9gEs&Rzlx_KDEJ$4KL}mS zO%L^~{cljLvgqnSeV{!;^#K(?VP(5Oao_u^swM}mNu`aaaUeBf`Rf;9(Yp zgW^6(inakR1N8wE_s@qSkQKzPLMVP?oWo?|sW*ON7VPTl>;C)633kC2c1zS+b*0Ug z>z|l-v{(6$`pCqNkG>F3oPgy?hBVnu5%0BmdK@7rx<;?%EMX!+xlMKn+9Oq>5pyw> z%Rzu;&pQ;}1_mf7qDKUUT#-g)KlmmnvQ%aP#qA>oh0C0)!o)*yxJnj=oa5mOb6x3O zGfz3uo@lfd7m_eay?!CNGp2+cVE+(_?D>5Rr1I5~QZT!dy zneJN$DCCpcEBYm%sLUZL1jX|I3&qj%ppg1d4of!0$7lQG=&EiE&PqWOo7UkG>D z^oY#T5un&oP-ulvjCd&SJ75GTKJr*-fyITNp9m4ZJpmA}Tw8eOjcDife2h>0YN%MW~K{z0|hf+?hK(YDG@-?96+(G zT01jOL10?U&U(2(&s8>Gt*Ms+2NfAnEnR|vG3P*VMg6>8WoFS@k`?lijDP~RPuNz=GhdhF1mV$f0q;<3P}n?#hTlRf>jx$ zW>^>^+V)$tPn%L-UW*6{KEd|Ab=GRhN5eLcQ7*?5^*wU-C|GynFDXva%tJHrI)uLgJ#X z-00iIoFqlEP);Y~og~KMN{JN<`IOETHxlcP5ITfDF8b91!2;p_0P;^xF70p1Qm%ssQ zLJC42C~=2vDo6*#v$^eNDF}+0hU&F}VzzpXZRU8L-9sVuF()I$LYkR{lER>9S$ycf zONw0_6s@um?!WJ*&77>@ORAh}|82jPW7-H*i3@`Qi2xKE&yy4q6};}Dcz5I@1u9t1 z%v31Tl*j&;pZ4spBvYiR!n-aO)F2Ta{$nktBx$dVir1Gg^5vcO1kuc7`)Hx7zdk${0UVkkriO{s z;B`N`bNf5py@l>kC?NLD=%{`ISLIlqrkZgzZ_k$#NqRDO6nE78+XPS~Q4&P2I?@=P zz)_+(o<=#OrByph{+nD%`Q)Y_W&J zw8i*bA6i6E%z7EYD^`8wXvxT%&=Ugzsli@xF6P?oJsYmw$J;Eip`bTAo`u7~-oUTU z^~n#Nt6*A)B6B(VTo}BD1WM03xUwM>e+yHr8c2%8S^*S(c#CC8*Q&A;fV#V2(e~aI zt6ZToAjt3)t&NQxI5&CmSSv-DYZ(=SV$J!IqAi3%!7CJpb4CRP84o$3g5oWj0le|+ zO3R13!5Cnk?siw;gz{{@012C`5~=9zhYErf-H(focBRwdBA)A4%S2ibPTzCq>JaOH%O7n2-S)U9!2L_{4i3EXM>w z^z=T|#}Fw^g{g`#7vWWIw*O-wzI6knLnRbE6rrT>feN4y72kgN?6DFPFyW^| zy%-i`Ha5PFuvpPEVcUL(v>dsJY3$qW_fIT<)1Z(+(OZuaL1ZL*-|458n}^CPZ0aB* z2iut`mq;WEknWV@_V3;ey5(nU5lEP;^szGNCJ;3iO zje(kQKd$i_MUui)LxXIGX&1v58eFqav2Q3dRVj2aBV@+bRFq@PY|;h~#owb>oL>2h zvvZG)e2U^Y`-cSa4&%8~`>Sa>itczOs%*z*Qq*J9HYNN>wc=6gv85W1c&rGCS@BAB z>$Te0^=epM3|$nfS)ra)LpCE(}=6>d!bI(2Z9L&d} zSX2Djr*Mf^%(N31bwFQi71!c4Kg(f}rU38O+PxA9WtzfojS>A87T=6tDA)4FQp=a< zjEAx(C_}M&9kH^KQ$a8GhOus9+P&>mC=msfWlVS|PHC;S6Q!q6F_GMEN*jfEpG|n0 z!;~?>|D?ZQ6$RYGAfSy{TY~9NP!a9mHnQ{L08OH=1#@_KD)*dyERY^#ZF<8^k1y#K=aGLKYHVb9|{Q-TwWgvn4m5q zXZnhqbN5u#G>lE1JQpa2+9`!sj)T_}!(lkw8UlqZNTTH;%!qbE1VvM-x`i6dK@fyt zTVo3*BokCsrMO9|^rL!iREdk{RyYgx@Xk4g|=%~3yf^32>ppcuQRzvge3 zq;#@oqm)IbYI6sT7oFZhpVA;WMO}iDCvogJ;9nTlW?HIG3R6LK;z(8?S#_98C4z+u zTNl>0WvQiDTMHBmGD)mQTl3Mi$qH-=r_tqDw54!6NB~bcDDE8ria5u{3FRsF2MTvY z49|jr@nN$i?&Qx8g5rm0A?o#lMK-;Ga1DhbD9qcyG(^!%jk`0dq?RRI?e7AWj9ke_ zHtzxig(dDXG}zvdY5@mj_CM?nj_j{Tjz@RInxf3n^ycG&c=>^vh|3#U6(jMm$O=;E zCyxc$aoP#(ZZN+SuflS7&%amp16AVlDGq@OHqWTQxsTE`B|GGUV}CzeNbv1Tv-^t6 z4vHfwQJ8F6-8@; zsR=bfv@;0#YRw#tH3_SL;^-r(Q=yL5v<6y8W2uxCa?hrMBy*plc*$zi1C%tkrBc*t z#JuPgn3z;HGg~Zfz4b^b!$0N%O+lOjr-GDwV}ns_*hvsqCXeJA)}c6@0s&RQXem{; z*Wou!qb(A&L*!s=b0SZ;f_s*y$&oQKy4_B?1t+m02g<=9EFpD5wnP-AV&QQDiJRo> zxkE+w0EMzUA0{4(5&8bH@5-$QP~cOvmo&x6F%&Q%C@ug5$;_fd5S1#KIm;;wzNWn$ z>8-2r6$*Y_-o3TXk@|GJY3H0?aX3(^?v2&i(_zxnqop{N2{8W z%@aQw6*c`7M;w|N8k(w8eME8a=Ot-Nm>q7ZQq!nb%j&k8SBhGw@kg>1D3EF^!rO-B z2q|<#sXSJtSv<@Xy=rL$!eR6j{qa|mYLP`9WHZWKLf$m9C0Bmd{Rc;L^W=lcHndFTvL~SrxS#a zgM)Ip8I{J#r`>6U1En3db2xBJq_j!C{SEE}5og6XT(%e}4$^@D91q6*1Vy>X(cBv- z5KG;4aSC6}-+WGAv8H?O46Po!7oj5zp}Dc=d4=O*@5;(=mv{7%;E(g*-OG6r5d&A-|%gtE*vZYOHBq^K*3=*J7<|lXEai4+rIvo_y5NnFB%Aw$f(NqO(DWk0r z1{dx&5L_uuqe3_^f($S9iQSGIY>8=yd)!4iHf{x3IUU6I86j@d4i<#KmJp@dP8LNY zN@e@qb$tB*S&o~R>JRhPDYzWxM!x?_P?T5MnBNW*T7B?kQSMSxYuBLEV40bjcX*mL zVrPFtd+)qM1QGf|+CrieLXUiO#iHIzyBASrcSjQVyn8uUqAig{M%-bFnNr^9qKmG3 z^hV@*`I;a46z+$jGv?A19L}jt1cz1~G5+Jms0v|m%~(BMU;Y}uik75_7K6gdyDk?L1T@Q`xM}e{ciwFFqck!HLZh^Iy4dAc_${-= z1Twg>9GaVT-;a9QC01CPCxe~z(2O#reA_6z- z&+hxvB|r$+p;MhUwNr+V+bLB+Q7$qc2895kKwZD4Id1KMs1?5M@*q$wnm0D5RK%v^ zWW3Iq?wQEHa4Ft$AvsUH$S5!>E}O<^AiSFJ7@$e4yefq>(G#|);Zt?32sejw zl2s?6E7qt_6|o9wi*hwl4naoD1h zmoEc~%E%adNAtIvZoc#8o9Pp0BR7}mJ~IIt5>cUQc^(Rt!%`evCgx$`fOvbufg;7e z^!OfPhIv&bo15>s^YNvInw=U{LkQ3~=;3E9XY(1-{DA_cvio+H)4x4o2~36#k#W3` zBk2?|Q5iH9E+HJh$JduUeqb3CWtu_~L!oh7ZII^#R4WRt_ebX2r*0EB1-=So9*f1o}JiD4*U`9b(Bqi?(+>N%(sat3`N5Q0tn~{XK6Gtf4VO!c#XVExf6@^?8D#yV-7VY_8vn6 z6@tIOa$>&|z=5DQaa1K)oN$2Yh=YUxzh^~Gs8*n=s#Y=a;r%#;`q`hMfNeo>mS$e! zCeAZ43KX2vlgvyvz=Qcx{*d_=U1KwIu?qYW97y2781M@erCrZ;q+vPKF~Wm3`u_Xx zAN#1Fcy)a$aWzH)W3)iT1~QNjwR6$^txRyuDcFZ@v1o%Muo3Z;@f+)-M28K7CgxyT)ji*=HkIPh;ROEK3c0n zI^ZL=lC}tUEXLIAl0+stFi{s;% zkm4kGfJJVS8*ECkSO6KMIGHbw_pR#%if`xt848jWZ=wL5fN(rK?FTDbc4y>l+|1m= zgb>gp&bs)~s8C%ho;7G5Uu-z=Yf+m@uI5YL-EZyzzkm?|2iy!hdj7aZ9$3ED!azc z!eI~>c0d`4(2x;PpE@zsyH1xGb@Iw9uYA1023KB($bulTssIWyudI_p#X{9ZrebBH zdEJk5A}Ta`g;B-@eM!RvI3TtPP0?B|3=1KCV;F`_g=}$LHi0!rTe1=7Axk&P1XQF_ zL3VYnP?Swzx+kGxVoeb+Cd5S%PK=JCAmOVpIX+pK)VtH&v+P^yL9ZXC(3?`T63r0d zuwvy333Fg_|LvF9v`Y&xlt3`YL*{@{57}6HsjoNyK0M95M%Q<>t@zCIYp!4oUWVKT zid1|pe*q{i0T409YfuuZt@;>$65X>^IPI&)zzThF15f}0-+Y;D;h@RhqdU`&{X?1L@dj8)0}_N-_bo+T$l;cz{s~eiIaPKF9nER1A?n@Ng?s^ z-9RBKA`9ZEhzlBl1ytO2TSZ%~>XkH)Y(B@gA(cexLSV$2f_<+>GP-dg%)`uZ!Wb%Y zc$(GNkOUujQ4v(PRWv0M)fH8&KY}U_pXc*gpg<=3ibWcdj8DA0MrceNHbK$o;`jt? zfWo+!3BK0wzVx0}4u$`u{BqONV&G{6C>~phJZ3Y&&xou@B#|Gg8%E3lgJxQ~h6flN z8jN3>l91ux+HmHp;HjRn*%J3GKrzeG3o;Ed8~`Ge)}kX^LB+3p(N*rNiodF)Eq?nq z17Zt}LPAh9bOHrbm}vf>!kCai1v1vd;fkR#ztiEVh;wX+LZ5{%DX?M@7hD$^1uMH) z$7vlMBO@apfBflD8(e8v&~UJfis-PQHpZS@h}=hJ3;kFI=FBN$LNvs9%oh|vqBVmU6cj;g;VBB`nLbcoY!QAM$L*9x#GK`BT&ITqAEF?Wn&FiDx;bp9&quq&4X3_A` zcP5QJR;Mp&+Iz%7B+@!gL6j2#6Ym{!%uz=P3#f2)uPlUhv1%H$HqFH zTk*@aSZuE%z+nkr2Q6Lk?6po`xCxIvPy+!Hj3QYY#Ky=K0iUQYCSuPrrRsX^r&m3G z1Pd<`0vexoAQ(hmNBFMDLu&ppNGrmFR?=rs`a)=Y`N3~n%<-B+L?8x5&uKjpu`y~k zgTbb;#KX*n8*WA@yM1ivvdf0xLJ}v2JAFYZsycU#h_MJP9q_8F8>kx?SdP5+`R7n! zSfDE2ioz9kYOw6MIjTZ(+mTq1H}9<>h{evw7MlwMC&q>^uBwb0*B+=*m9G*Rvn5T* z9LJ7DM*i>`Ip7qq9Nk_l~ z*WPl=r8fbE=?hnTS$2h*%!~$tvtcWEM216_!}UYHoS8&+xm{S9zIghB-zZrN3_`;E zO`u2v1$M%?aF!o2Ch8mzb%?vvn-BKr&bu@9oj9E(h2f#->YCG;K88E;2Se}k|@ z@vuIx1m4|n!!LSAWH@6o>|1wV(Ldgq<#m=BsC{82sSfd;K!t~b3i?2y9t-MKez`TE z-gG4`IlR7L+G8B-r8epYqIi)TJRrQ%s}p&Mnpo>;tcz}cyKC#fgzJ@>d{j?UZVdB4kwsMmM66aP z!z2NU=G1UgK7f9*m-|LN%zZ3|z}rr#Ea3)}5CK&3MKBOdHKSP$^?6YbpecLxrgc8$1+^M6N^y=L1hdRj?gO{Sso?VXB!{ zb9sPh%jMa|n9bvC%8JO<3hRrpXH5 zv7D>s?j_EeY!mo z^&X1L(%e1V^PwUD9Sz>UAo*H3yp{928u;Z%q#a@&ghcQ3gX=f+-wP<9B7!2K0;l42 z*$+v@YwBLCaK$H|bl3F^4bhUcbG}irzNR>I*c%R-0BO5JyQVE@jxeaLCfc2611uXR zlC!hmkV`^Ep2a8;3JXe2tm|7R|9WI#gm?S@%Cw+hP7&0E&B3|1M^Cvxf+h@sfKCQbG{BI7R_yv1NLHJ^S;9Tffj{pmV4t8b!1tx7;c z*L9R?VHNfIH^0S|wD$9Dt@txL&xI>m9dG1dV zh+w_B&isaf0rw|ba;Z-+jqYf0GCi$fFp|qQG`UO;_b*Y4HzokN#kU{dZ17wjfQoYA zg=!Hf8zU;{OAksCpfD^}2pvDYUPrhVq-))*@*m-%JZ`v2

-D!Q)_#gkIv?ms)e< z1Kj1;-eP_5fBjCHf^TGMaIkZuIY2SLUpl_dM5?}|@X-q<{ptRGyUMD53BI<3C*T4< ze5}2GZ!+>s<}cF)+_zvb8V08y`KJ%ysjqAhL*W8gEJ~m7o+e+i9`{)?Rx_2DuQ;uH zW~#lvvjj!Se4NxeY(}He(ygR2vZ?U`(v-`xOG>;d%pvS?C9^1tY?hsHZGX+?C*PRp zTX%lnsM$^rMK>-`gIg>y40yH2kCOUj%hKt3+9tbApgCin2@i!$l$b!4Mv9u2!Ul;G zhe9OhMt;W-_w6;B^w{Y5sYdj)Yg6o=pWz4ng5z-Iwp*_COxlPyy6sA@B)pEG{WZUQ z+~pn$_p1iQ9D`!Wd?P`@L^8cYhaYY*)T1C8Vug>uLo8QsQ_o>QVkoRx6sqErvwDVld+PQE zi1J;K4XO@pk$*{wCbuoxDXnfR5WdRi300safI^-G-`QrI3+ajl`N^$Cc7bw_mQNr2h4`BpAZpL@2=o?U3nm>b!~-*PJUT ze)-O4?TQJbV&knk@dKQzu-R0-B=!lH850s`KDNho=RxTq8MG-TD1$NA!C4$WqoJGczk!4wYo(ka4()dwj)`%g3BBpN@)} ztva{r!cYJOa(JvCU;=@MGANKa^QVkbZ$_ZP$-I990WBjErdd;VcD^%jUdQtY;R=N| z<5j7pU-Hr)YC%sLP&AA=8n(frzDpBDY%ca;Ozbt3d{#I zFsBHg=g4^M63ezG??$5@(a3$fdo91FiD#ylFr_AIHNPG-TmpoTdkG3Y`1X33@Y=?= zwr2d_PV>7sZCzN1^t9LK{~(UK?VX&;T`2=kg@b7NFBl-x2+K$^>AZ9zy*xSZ9YNt$ zg@;0(rbRVhEJeD3LT%;H6r$pYGd{WF(y5uTI*tUDccJ9mM1g*z?&5*&rJ5MnK0wgKGHEzzsf-Y5NRi(Lth$%M12X0%Gi9w zP&gcH7>Yjr=iPeVd8W4c<_Z6Fxm;sI^lWS)^iem40O#Epu1%a4aX;vB&jgRXWS5b( z`2Q=ZK|#?a_woPAsU7LGEJtrirqD56lELZeI)Et2w_@VQ%Z$bjs3@}^H_Fp=P(0zG zaAAtJ+GZ8AWC23Bd#|1$&WUx^lu_ZJXl*nos(}J33W-z!KmcI@&ZVkWfI-4#Tjngm zCdf}JOtEI2oC>I5%@jc)!)XNkRhFf@5J9o=-#`&h_ql$aX8vI9++t&$q5ypKp-CMo z^)e-4#MCrXjhxeZ@X%9gNOM9FsYX0BQYINOE+ND<)FqN46_LnbkjQvI6sb#6Es4k> zNXQXQLqkx)gUgFVT;f}6ul=w2=bzRk)=dBT=X!d&zgf4v_S)AXP@$40!S6a0cFr*x z9~92Nk$!whH8BRoDQ-Bn4jg#qUOBm!V-T`icdun+e7LVx2Z|fQ055MNyMtcoPl-LZ zR#QvE4`eOR)e=!Vvk`R!SCP+Z_S+xt_5&y)71H&}G8F`+tIVE1cOk7UX)Qw!GWLx6 z;VMkfMG*=mf*+j_V>*l}Q9ytUP9{fYS4t&{@X8~LMf}YIGaYI*$^{M-@)LPz@eI+2 z#G@~ehaXBo!O;%m2@+f*6j+ZPpfIPPp-9~BWCS+C4R+wbmfV4Xs7H|SNX&6O56uGGx7Ilu$(x~r4fOmsO(Pc z&W%x3`1}V@yf5Jg4e}m*d-ZXQ2dS%9PdfY5%V|CY9H@%x#>Rjm*4dc(AlleJlg~jx zsifM1ShSqSk5CM9`Nf$@ina^fpUumjwX-~)FRxI7N9u~gnioh?NZxKfLg7?!^c#K@ ziW*SxH^?}ej0sos1S1v(g$u)|63A(o?l3g;)QCY2x5v4G36V4uP6bbg&$06Z21WSC zBNWI!gF+rZhWmm=7K%}a!cgEgMa5S5^#c#c!dks2%E0g__DA1wR$xptu^6bi6;#6FQrjShRSq=qMT(`WuWr3Kg ziNFQ0FE7slMaq2iC=`!`0XJ=E>f)Gff{TEb0>c^tc?cw-OA-Zll25 zh@on@KUPVER|H4%v*2lBKkjOQLOW?{1us+(eXO2y@VS$wX$*jzho*S4x>T11Aj3|C zLNQz3Gt1^sPfwMY{t_w}{hT@xoG+FqW>@lMO7QS93pvXBM+OITyny2Dl}{!30E#i0 z+mBE@rYMM`_41`HrXy&IH3`4w)TaOi*AzVpg)`A$6#kLiZOTBM0kadLZmNeI+K@3d zF*}si6n8i8HVe@LPK1KT?Y-+3pe*n)-2c4=iU&GDG3rooFN!jugS-|DgLESkEYzCJ z>#}0k7lH-9tyAH8VYAM8t4A^a9j(xk_~S0KA6{uADa{W)e*ECUa{fE1`l#KV`W;vQjDxUR+>}(O_Yg zr&cCEeFI1H!*}0%Pb(*w-!Lc`e92H;w__+imWBf9t_c(k%TFZwx_Nt>nD1#La_GHD ztQs@XZY`pAXfm43WPI<{H{Khb4SB9Z*9C+SRgCii4uyY=Jz)446mC44qB7D@>?mdi zf}1g#1Q6>zDH*7!or#gC_z@fSeuLtyU?)HOtb(TaDdw-J)r5*vObQiPkH>K|SLvxw zKU_40bkGKh)An}9bz)Yl2^6y{xiWJdWhOLolwk@KlhczQ>O7d_Fc)z%C+Ij(xR_RX zTnSXD%llD6(zl37wMvXxFM}fbnLB3$G9l>^HU83#@O`{nh9Lz757_Y7(MIGz^f5U) z6oQ2zU{K%;Scc(V8*fcHY$dr*u@m9F{{V^dtJ8sYy9Kc~!F~Eq#?y zQw__3iON{SN10fQOdvnG{SZ|lRTbt`2o>)L6;e(C6nmaK@9b%ypp80vz*U!OJqCmw z&=lAVpqQdwn+7%zBIaYIJhGy>ZI%VUN)~gfbn(>17hinF41ULPq&o>jUS*vR<^xUf z0*)pVU+z;V6$W@(mZE={hC&xpUTaL8h&JCA3M36hD&fg_j6Hqufa34vK!hTo^gwvf zDcl)~_%F-{pF$)&w6Ru&jE&}cM#5jBsUAnnXgBJ8JlUnNOhDFs1KO ziO!}<)1My$9K9padbj7Z=KK4bdn;x?&Qh0xt5s^9ZeAwO(3YF4Xo}D2vu9p~LV-x4 z;-&ZBeE-+CfBpD_2MNkUAL3Jx&%}PDeF}$ylPnbdy#We09|A?l zK4LmjP{4po%eg~g2`9ud9m<5g{P&i`Q%F#oWw31Z;X&_Tqd7{2g5!UM0$JOef?_KJ zMa*lCdB}Q-=>p{iE_$t{zx-+F7hsw}G=;|>%N0b;j`nJ=a6v?iEIAaaDvSzL#joO1 zd`vao9(;;d*-=)v;dt_!Pnz9bX(o=?#aDhYH?sl_6J=dRDgo)_+2tu_I0_{mF0sD2 zJhA`j5}EJP#rvn^vGQbC5=i(Vv!rv6Iut`~P$YS8B2tH<&8MK?RmTd`p(0SY?Y~zJ zer$3$tR)fMnz=H=arZIDJP=80n(F7&9ZfyA(LY95GA*NA8FGh-F>8e1z`#P1DUf{u zgOm&dg_#5!%B|&P&~O<)W%-nG&oC^YJQrVwG7BJWWY!?zvFYSA9Lc$AjMNl7j6r87wkV z(^D)9BEJm|6RJF@uPI_g$XV?5vnd` zr4=$z-0n~y5ej&S{s_nLFw%4e9EyO}4p8)sA_m1!R2j<~CNQB?^sNWORXN}xC*oX; z^WcA_0#yMNWD(0k2Z%_EI2MbzowTJWg310uzBE#PdS(SdSD-1#UNIl$ zK84oY85F6EX076yqB9isO-Brhdvze)Y3$@vT&w2ejsq^jgi~QYoqd2qflp~rI99id z`EV$Z&QSc`4n>bcp_-!V)}fPZ_ViduO>`*QWlM*5eP_9@RTu!1J9VvCgo5bf{rBH_ z^ZnHi9$fu+^|{B-IY?{MR|N`YN=a&dvOMlVFx`HoMw1|PaZ-a0T+01a*Wfx(RxvR$ zBc(MOhm4S^D3+#j<=kLycK={0e;zX>%#x~4@zD#!q>nljrYa1Iz=K{K3W#ijqRXIA zDz=Bh#i1}ylfll)VGhLsi)!zU4lEL(uvxRD5Ys~+Wx$~@9=0_f4n@dA8Wfy)Aw`g$ zo~734Mby`9iN>k(akP5I6GC!PtCGpvDEUD(3~t3b5n!GiR0*L z12z-6nZ*@Nb7MbP^8pm&ouM#EYYP2d@zV+gZa?BwL@2TXCJu$qcR$f7)RgeU9ZiG6 zU~}^kr%$30iA1_$a7_`Q;PLID@Tre@=A*k27?~4C(q*l?Zy1}w!29d6P{d101d3lO zfDv=Z)%h=QDg=s@s(Aa?UsoSoJ^1mS)q~XJ0Rv72L8;E3Z>_DfOKEdU^9 z>M%{EQZP6RZdDhNjjNiDKH>sw;d zHYjc-{t)}|_Q&VoR7gq1L7-r!6m20;oVCRwoX`rIroLQvO;CD;pRhuIfmtR$X7DQL z**{wvDZmH45lL5Qab}s=l>R2Uk%`FyEfSxWb|o;Og;#ApMOIT}p=dK7d#_!HbKoiS zk-?!e6vmSP<M0zGtoiWhqcYkG1wS5=8Wbv>pl~)i`V@#B(@^j-$KcRZCB|W? z$yRqXNnG(EO^3p>8(HZD1&IpzzMm4F+fGx|>NK%AJ?i_4F`SaDr3KZ5W&x&_{edpkV6y$M{KKc`EQ;)I`O|g7dN@(LscRboq5g zI%$ejEnbA;81pgxGoCP2_; z-9?|O#*l6Fh<4fFxog{YS^Zh4QVCE%SHf3yowM7ki6>w*^;0sqVCN24fTHo6%fb^+ zEXeKeHAoVR!r>7XodZjC!J#ls5v<43s6*jmP;8~{KMEYb6yj-;sd)RHWa0TV`^f^uG*E=?vLg`otnsjMbs?n5)0AGJe)L@2WV3xfhlMIUHUukB8GbFL`j(*M!k-V7}*P&i+^w-l>QcMt73j8%zV8_H~|1;gW&pXMPK# z!l4+BmSJoMC>r(^KPy^_I;x^>P`LEg#9GKAvCYH=Ra`u=EoHlIJYGYRhG;K_IvX+- zQqldk?n((2B)G93>`DI34|fJ45Qw;89UynH7N{^OYs|3;KvAF`lPWA(Jc?(*F^`}u z1_$#OmvWpGz=KR@VTCrkG9!jdk%D5#q39&8DLhZnCShojoF8=9Xqj-DEdB3si_U3! zRBezeYvv5LDC2!74wv`=h{6}%z97(S_3t3O&Z%%H#;dLku^Rx~GB z_iqNWH!c@zXlZNO;Y@hjeSC@u^n@yk`+}x$PxDQfVC}i63YLi>K=I_|FQX=uij8Bp znzYinwqDyM_96!jlDdR~+46aK7kQw-mssJ^JS60ViQ|Y|^T(Z;J1(cMm5io(n(5@# zV8g6J@^1=ooeDP}GVnfv%??mh zTk7@`?RCoBhA$;_D8ka>0NN?OTo4(GigX@5VO6_risF9hQjskFKvU3>U==-qt9c>y zS6n6dU^gmBU2Qa)>vX_k1s_E=7t%?WDs8mGVh*!VAUmOp%keCvz@`Wwi<9DM(y;x^ zLhj7tpn_sfsG$F{c$yu2iU`G!Nl0kM*`c0)0tEruj^b3b?i|tzMf&p=vfVx!71I!H z5;Y3b zgrdg=(bs0X?9Swl9F6UuSg5uxJ?oEu(1orn6ZP=Iu4F~WI?-bKAL~Kf96eBXC1%jXXgh)5@b z$tkQzapYA@2#N1AFD?}?JdVsJIz%fp2}y(XzW! zJfq?q1SSNEBW0g$HYT%{nlJ$rY$Yh+#~_!y6yLEilt!gR_6Z{&fu_Kv5GryPm(D!y z%tA?Pdld>+7?Y-eid2oJKygD`Jq1!xuZu{aw?{Sec2H!z!g=*OGv9gsvi@>ygTDf%wl3&n z*hR<&yEKFfgn5C-#zT*+8tTH_*DWjH9ome6j2jPULNx_Q2o$kNo;T@UMNiOQb$ZXV zbVK{<*rVCNj=idG8>?xnZnyx7vTPkEBFn)hNo}8<0}9epw5O0Hw?-hx;c=GEV4WED zqfiLVxk+g<`(dpJe1Wk;@rfx4%UzAm(VeG4pU6z~k&&kHuuos1p`D(HstWT?E64*eL-7+(w0fDlqNacf(-ikO6xPp1ppXuE6Ra@4;{2l# zHlDdopg_cYP*hcz5=|kNgH9A2rO_FNME0bm*REm#GCB5tDI9mT?^_h^R7QqEwg#+4NPV zQI9VD1Qb7|6-9&sCJ0Ft3i1@luRsxc92pen>|sY3_OCnoD4;-7)X@|Hiqhgpnb~ux ztqD5>A`_^J94zR32lp5E&vG&{QX(GJA`8h=B_5SM&b+fkMp( zsv>PZ_Gr_q%b&gK5;jhkT`zB#8yfDF)G|=uPmTZuJBfyNXBdpdS6^KWTZ=Mh%8g{W zC1X*e9gcWq1rFm{MOeB|eO zheOfs&Q`r?$U=f)n{3)!oe9tpAUGHl-CH3T4NU|h6fpqdK^x2?JVO;`I`&oi;DYax zewnnHCKsndLJ*5nmJ%j5htg085+NW>RE0r7?#j)Fw#Yk&_IP*{uU>Z9RkrEnC6^8n zdju#@0Y$b2gb0*{7zS}HM^F&}LVG-D$x&Csu^<7;aYUp*flr}3glRQ*8VYqOOx%3@ zGbqf3vut>U^Qg`FN?Ib*M;I|3v4ihJ8_`7cxP(RotmveYqGlFDkPtBo5;R5ZVRDKyq2f@u{V*zk;zi5{ zP+&e1C=P1$F)?w{H)r24G^bFYEH1fjXn3HzJFNSdWrH|Wg*2onl*=5outw}fj&`a5 zz%5rAmOw+X%>6DtJwte{*+l>-|DS!bC+W)=MiC$ha5T#KoX0r^FhHxM(!09b!u@)Epf$ z7T_Wc1yuOlv2{J#)_j!n6J6jjHrUCb6Z6G)6k9EzCE-gGFcOMR{cfH=%%lfwoU2{$xfh#-nBV;h=^?_?Q$ZFduJZrp^_L zs3G|AG{v@1a2An>OjJ|tJJ!?LY4mqjRvZbRlBk40V$F9}aVS(Y*l;M^rC9I$;b;*b zm0_Q}g9n467btA(m)j@AYKUGi5z|ORhN!e3<^n1?8!})L_dUPnKIwO zOo*K3a>4g3(w9JafDH~fp~}QtTB8}DcsX59;Yb)2mci=`1>#IdxY^2t`*r4OI7Jx? zQcn%r?$8RzT?33mqKBd%FubXcq+E*XHpKB-N6v6CQ7n2D3ck^qa=e)39uICo4XMj> zYmrOumZ$onGOpU9-G0db$fr(zs-^7*RDi{~9+aXh(wZVNF>&^*ul&@@8q(gqmr3|> zG%2i0E*l#?0xHye0L94E3R(hT=mi39b>4$n4m?hYMsqL%3`JQOgx(;}t%bX~>Rh2% zc1@A;F{K4_3W{DG9fzXdp-^AW2gt>Tx?!GOXK5tC{$^wdkI|*oZS@kmMt{F|A=~s$ zR#{Q6(7Xa~Fyc@g5ujKz5;B^C>j70nNekVrN-wIg)r2YJ+>DL~DuxixU%8O}Fh|oT z&OKL=Kp}nGZ8NdB9X>wh#Pm^54ZuWS@4(nqR|YC1ebpBSdi+XpTsA z6_F=#2gW523&o<=+7OtO7e%EcQ|Ca@)?C%700;X~D9n1)6^fotQ1Hj8X8VEhOg)UG z&sA(U*}l({o#vJH2Q}d&85C}@+N2&G(X5DV*>pN16)q8qadV40XbOa#8X7+Hxlxza z1J(3jf|^ydPNk-vW|GvLYisOJR!j{@B~1n5RFKm2G;PD9~h zoQnWOEke7Aho7g{)f&!7AxEfD0 z_!Oci6bi{vNXNvZ#`?On@j!R|wkyS@P^W^?>E$8uDVPa;b!Ap2#6$O*f*gvuS+W)d zEyP1fU{{Dd2FZ7`mM~95s%Z+T=5{FlgQTITH>~fIO9hLNErdwfgzY9p;%-#&#jTpc z9?C+oJ2oWCGSQ6CwG?o<`9Ks3C#Tk-@+t+!vP^a8H>J^%z3`A`fL(@$jEb#8y&7k2 zvn4RyxGYP>xvnW33TDnFok=cp;>k~*ylu2wn1F!Fb(g57AboY&kb0WEyU0qR9atm? z2#dL#?sZQKn-VKPsN80XqeNm;8WKnlQXm2$_CSCFr-C%MI2Hc~6fPF|%e6kAAW?1b z4NPRSVI%L7X2Piei*?UIc`BQCw4liMaxsrvWA`|ax<&_`f~*&m!GASo5f(XvjGQR>;b7F*VAd%4CC2#gL7cz4xGO_|to zB=AObB7JVDy#{Uh_~WmnFCCkRANlOF4h12p`kA;C3WY?Y)3mR-p}Pkrxc2sqU2;A% zr6>!{RP=NU6jas;0@x3k5|dip;8EttUrv;B=TZ0%1TY~`@H4_1bS=EH8qKs%5jF!# zB-w2Dv7andtynol)~7Hrjl8gtCfu=>W>%Hd}I@(gm5X2WaQ_KNZXYs zB@3Mt1u*VzK|&>K)cX73eQT{Z>7Q70D9F2HHN~76gz9zmD0pG`mg0G8R4Nq}<6~!i z;z-n0Lg=!SYBnffLZGmXj5Qt&I}LvG$<`4)14oGTc8vqYu}Z}aOq6z$)ZCSmBHcX4 zm7J{l5h~PFuoO&g*)xh|Vmbu^7=wfAdoIc*=(@m|sdEcIY-70q1SmYpc7yE6Ey6ia zm+X50s@1F_{?He6Digo`NA6Wal4_+Rq%$pZ#L!W$; z+r-E81|vc)U?E%#)k0;~(AJWFbi=@r<73GB%iET&DilfZVT!_`kR2Vmdj4XqnOF=tO@>_T1>LSDbGpntH!@PpOSx5k1pC32S&&7bFi+D8@(hZ= z1os6AuEqsWSebxAfwV%=m9`oXv4c~IpV1i#nMteqtFvG8on>D=xS5+~)s2YicE8DR zrw=}5V6-kfJ^5GqZ=pP~-WsoQMc{(ctF0yXsOH!GMS%)ruH&C#>1%T|Q`SSXnX-8? z%RRn%^7wJKI#fk*MAz_j2^5!&S6D&?6a`Taj5uBLI7dwYh2VghKw&J>!zNc8fe^|# z%Tj?gTL28{t?Ez+6`KhZM#KwI=JtTp;ujl;%5$9zPyKik(7mti|u zoBsz~%*V`WlBujW_q7a4r__ZxJ+5l5tqhC~*|(X2vxL)*vNUEiaDfE9BI{=@w?v*> z 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 LNkvXXu0mjfpLX0H literal 313399 zcmV)WK(4=uP)R#R3#;+lDy{0Z%H|Cwx8oCS1Pt_r(90tlq`voBT)@a+6p_7fCvg~ zK@tQ&H22xmp=;@)h&ptMSOi%LfOUXJ#>{GQ? zXY2@pkOrM=21+T?U#{~w?}HWI-_Y@edv!7x1T?EPB)gH5UzIay8T*h<+8$Hp1L&>4Jf&Q8Q7d|uo*{8y9v4E z?t01^rv6b$bX4DLKcoX+5*k+5R%p^;bIX{~C;(vA^$~g|drbB*7e-+Q((;U3>Ghkz zwNC_$X=rF=;ewL0(;Xv}#L_R@RizZG5X_U(EIIUkj&uw*CR;Bq1+unU{jC4?TuQQz zM0MR(&v19Y?>}c8`bOb>($9`9m(iEb3$@Mgz0XtRTmD>X_L%I2a&%v>QyBZldTfdN zI_W;QJ<8mUZEPoUKe4@PX8<5A80T^U<3{IFWRsVIoHN+#u=gmOYi@p$K1arsrwN6D zoHHK1{Rm(G+SgG^!RxQSj&V4}Z~xA(e8ZdIz_O3HIH17(ElMCT*?veMFeudnaq#}W86Li;{y8hV3@F}6lfc0& zv=(DTFecX2Nl3$hi;D|<$G3j~?|a|-@av!d4gBF(zlw1jj@YM%E{*MgKr5t^zDI6j z|H{6SxkzlH3cElHM_{mc?ita^r(H@Z z&Dm=B>`ag!-u2G+{`>wgfHfK6SPg}3wD>+LV_Rb!M1)}w$*u%MBn*#$nT7%|rrx$; z9WW$SiZIe?ns9pe6d(EUNASJh`w6`H)*JYTKlzVwaRHp2o#6|=^SdYo_{8^q0zdeJ zKY-md;R|2*U0j@8vO8x}T zw!oQ)FeCw?DJ4w1j60`yaQ~(Ic=Y%&9zS~A-A~JPY`)zO0Kwl;xQA-o8j>7TT z;(({$7h2t7rVVK3EdE1BgHaUWIPyV5Z(==*vD0#Wdw^mMxLozz?&C>Ee?DLHZ*t&a z=fnhhW(EN>pI)?Vl@ZDXF#pIr@Z(0T8oqj5#1#m?*T3T*Opy=k8r30-iiMle3cy zDbClYBIkMEBFRZZ!e+A(Eybfpn5GG@yz&a(^{#h=XuwB4@?m`Ehd+!jfB7r;`JexJ zeEQR$#^3xKe*@=d7x-`g+rNhg4<6vool~SVsHr802CA{J7Kv=b&xo1-p1faePYFOq zJcYa%O&w<+J+qo)Y@Wr`18&V6zBz)5Rm@8JH~(BpDWwQeqxD06S=pynoIlB7PD>=N zGvxyTKq_b-xp@uTH(R;M;WIOG$r#drJ9kd;m9KskZ@l#;Hp7TvOh@ep0>HHY31@1v z_v|RnL50(W88fr*j*@##4w%=WbSb3)#^klP0E|}6fK9(TDT$gd2)fTptxHOWe%d!C zL86*K=1k7OKwRd~=8m2--f~C1W8%;C&ioFKJvyH~w;bsVKHF}jl zT#*LdFw=ilTHc#11{Nt1QW}s_Le3ea)=#p%ssMuljLoiQJqRhG6~UG11eGT>2b_Ha zIALH2s}jAsR;QbQy*dAdS!|)18Jls$%P+r-QVJeDdZIQ&@(r|RE@}TxU@o$c6EF-3 zr4$T$@E<>Ug1dL_;m`f~Ka2CTbDW;s!Jqmwe+obGSN<{{J$i&h3E%b658!Y8t^X|^ zJa`NL;2-=$lu|H`^}rPcIuVH1rMPUVYCU#1MG6#hL#DPbXl$r)=92XTwt)pqfH@Fn zhPlPWwrWo*;$(`9d3_9KI$%-|l9t4+0VZswivA%>NMK@6voGc<#%k^$S3}I}lRDU+ zQx%1JzWrpu`1I1{cUG|fq2TlR4)JHQ$8P_Cg6lgzMkuiwFjWb^^ zZC!Rg>Kp_xuII1O1SoGU-rq3BBEnYVCjjXFWpDvFL4ZUt4P{X}A#0m-Q!|&A9+M41 zo-M&F+erStzY7?xZh9f`ng0dML@M>)^izIa(dT!an0CH`@jqbvCP)1ez9mvD^1$_SVzkx4(d4rq_?!R;&r+36TGwD3o{FHr*hd%g`k+&ue=q$Q@ zw!4e$T|#2>uVC3g9M_|>+veJgh~{kTH+#zDsCFzwgmD~^K-f+@m~GWnt>%E#Y%A?k zcSwPdG@ma`21P+d(H)aI&j`^E>`8gV(G1==9t!GUhUGK8SBU^R&bKCY=KG+=R%(;= z>_W5Eck0)@w)!R<+k>TKIcI}_|7u&EF{M2{h({>_{psLhkh^|86N+l*><8_&K-rln zD9xU#=E?S)&fg`FI-yDjTr;4|Kg}459c`BYjd7ZN+Q5af8=FM~l|>Ibab;{(OEd55 z9GLqb0DJ!9>cUP~;sO}m}Xd0i(H z^lMN;Yx2 zC4BUw-;IC%&;A8|>Yx5ooSmKF;iJdcj3Z7@PmxOjmlC87lM>ptg;2ns*{1@lgh=Nl z4svC@t$E<`xj5vE?W-oXx7lnkq=a3bTouNVARWuR4=$G(WTdp}H(-bvIC2D;#JnJzAz?`JFQtTG7-UZ+Q2K9wa_ zG!bhKPc|1^Sk*<`?8BoS3AwZcEzSO2v&>ChWP5?@Mk~4800X~Xq43`ym;FUFjS1${ z!&fAPA@1OtqO`8Pl@2xK(BbINjn3`cdn)5QQw2=BQ(Uu0>gbl#$#b(g!Dh2*Od}Dr zkP%Sp4m%Y%$d140Yf2Jf;=e{W;d55HSI)Yz%UFr*=#f_?KTP>@b8aCVFq#*A(~68Me50pY5sCb2}2q{ zq>}xRRMjtD$Kz(iIF1<7;0@#)4n(h0#*7@5+osEUnAo3`hzt$88iq#FIVPl0^#-C@ zE7I1zd-qUs!I!`M6}U%gwAt0p?9m{YC}Kj8B6yA-+Kjr8;rGJPAEkm3 zXZW-Ku3e09g0bCh#UEuDs&a44fa>|M{}nbEt4>IN(p!F zoQPvHvkGbnxiB6*eu9(|UVQNdp|QcYpq}YEi^oPdIFqJGn=jYul~Ry%!MNGr-n^01V^cLaXf_=MK!sIb*lm;r!ylLt$^E zxbH?e=-)R>xe4gA)Y(+WSQ9So-(J~O(6Vf_q#BBG7_ixFga8exO5ly-OuNaGtN-gk zFpWhHNk5q3A)dov$(?ke!=^u^u!57q5-tiB;T`24T`IzIc^&*GZkD^{s;dN z{?7mV@8HW{{tDjr-uL38AN?pk|M|}&=YmiF$fxo8>#yU{qsKTqI}DprGN%90}2(s^sipMbmD=+4&iM>$iRjKl3v`i_iYbX8{7< z^X_-!`@aAC@R9HOFy8Ubcj5gXcoz~z{NsP}kMY&7ehr(=Mi5w%5XqPz#&CUu^k-_w zSX{HrHV@D-4q6R4aW}&6rXm;-sN2rKldAx41VF5-_5|<^xO?|5xD@QRI}aUoJ?E^d z%gElE>CCk)q~@?_d%o>LfJEDe>Y|m@S$6yS~2wrkcU3}l>*Xhy>sJ9`Kl@?yN$S^{psD;^V}&d<-Q1lXC+%N)b4`D!|-c^)XLDfXQCraH}Q0~IcI(}Xk(FsE!T z)lqvG2BdKSbCGPnrS0qa`30VwJ;838kky>RF_9ABS{duGo60+~Gp@3CudvMSSh`YQ zbgrGf+5EHrnEQZ&L+%hl&C_9EjR5D7b@x_)yxD9pj-!H#mdeg)X^#d+ML-$?{Z)F* zYMZh?j<&!IAf;Mmf+ReZjI>Hl zhi?{3`cjf?P@W1-@0?&5M!fmv8^}AxE3dtZloEdSXMYx-_}-7>-}ztuO}zHnef;Kc zd;x#^Z~qtg+Sk5{mtKAmC!3A?Wz_Kogq#5I;aU2khwh;BxGNb05kScgmF&y9p=^T8 z*;EiHiQYt!3R#?1nAiD1u4Vo6d19jI*RkDhF-?<)k7e_|A7maD%cRZBiQi>sKbn5x zqifDr;sE?q;Kq6(=}GU4wf#|WHCGo}0WLDH>op4INtmVyPtMMT;iRN_2>_JR30Lmd zw2y!vv=T~?j&bPixqXU#zxoiwZA&}kWd9p2zETO}>;bcRhUca$b6VCDdX?W5)i|x4m9?zPM*kV& zb7G0zcH1q6am1LC@DH6E?*bK+0-T)OMIyp>d*1M`TEDM|W*A-yzloEAo|rilC}paE zNS`0$t6bnQ`-Ka50%&05k^mwM1_qd`v>qo|t4-7%$a%P@9ryJ@K{Z$hV4LX>h9L(? z>PcyU#cCyWFn#&umr*^h3%>fbui-!Y&;E0K=2t$8yLa#6=YRee@R`qk7WZF#5htf7 z0Dxg2RPE7H-8SEjYeb2NF{H8joeas%GDyjD={IPyBlg?b^}w>ZCg>ofemL`GuDRUv zl#!=Nf+`DROym(dO@^9&O>O%}F_BtDpJn5iA>CqG54~K08n@b;wK7c^yWND%II5Ox z8P0s0<$9WsDqxtz=(HwCO_2e)rjILQS~?%f=86pM+8Ucc&#Wrb2B$FRlEbI8iJbyy zG;q-Zr1fGEDD&qSSTbzyY5^gk547ZMBEn{~0hoiCgW$P_^kFw?hj}zQVq^BT^L!g{ z%|sL-O&VVGcV!%hm#eICXKo5t5Uv7`nH>ls1kFK9;Gm28O!zKfCnq9U7rgSy%Rpf~ zIXkaBk!oEw#~0`4V2F9G6o{`%XPhmRn9=9(aRr3ll64B8)P{|+P6`M}10g}o9HlT4 z70@t9dRcS6vsBV7=uA?sGB}0~sdiy81pE5PgL<&^>4PWTykeS3NRrAr7gyyBNxX(X z|8G8zU;M>i!kyDo+`V@f_wL^-WzJc?g80_W;OP@|0cG zSy=m9iswS$FqbypQm0QMB>Tthb5c`h4x>1y4oL`$B{o?QaVx7`i`uNCda|d!r&Ct# zn@_C=#~0bVwdaF6$wiYv@!#u(YEfW#9*Rj!3~tg-Go;TcEoloul2aWjkVGbR)w5;N zWm6BE^L4e!&o#7!AON$xS1f5n=;NjBgBE?5k7=Q?>1h~iqX3aATUwAH{Z48&g9ALR z)HM)IAB^ELHvJ>BnK<8HF_bOzsW;>`E#0agg|I~=s@ENOJvPRu{Rx6m z7ZfqvXJi-$j|h78l~-`F-Qm&WCo)&Wl0${yng}ih+nh0sqXt%Rbw9W&nXIg+0e?^} z(Kvs;s@Id=t5I#(%murNv7088DZ`=>d=u#8r_fk!efHTiE5eY=l)(_jcrt1jQW|ha z{Q~aXxr38CC(0ZR;DbQeO%njdIGlh8c>Q(pc>dBCzl32BKbEYw z3;OgqWZ&D&!oGD=B_i?7Fo5pf#o5^t?52!y7*+|Lz#jRn{o-~mbh`C&GuL%I^+L_> zq%<|6ez~33vdvx{^jUQ`zqTfoF*7n3@9*YhqalftKNF?5}$R;pDGk@p{!h%)d7&y}KWR$-_SiM9*yfy{@W( zs6U7U%5~qb6`8M0aypp9{VNWVRyEf>^^5$7z_S#_SN`y;s=7I%5H54tBU4PvJ+1=Ey0JkXOls)nMOhcza)_&h4LZo81T&#bZ38ntnH z=Z=R!an9m+#FE{UQN$^el17f8^+{MeBx`-pgHja955tJ8NuuuDy|Z$_S^Co`rdnyM z`7WjnPV3&nS1HVl(~}K`VZiytR#bb#08Dw$!6fu^$m+LzLx=h)7V2>T!n*zWIWF^4 zX!#!HkE@U7pf`ht#LJ=ERk=(lj3GgNofv0N1gIOa?U@Xpmd2&kbfBSu(JzVgw%^`- zH}>l*_IplxP3hEQs6Joqrf?wEJiTY_jDaqwe0;}zCr~B-qqqV=%{o_{nc4j*Z8wjo zQB7894H-hl)Pu4__HAufG&1dT!!Y0kz;?Hj937hCJ}y^`iK$w~et&MNkOKPAdJ%6D zvz84N$<^JOIEm|JFbrxAgxNMIFz(9=D}%iW8>N#7Q_DC+X4F*IqRO>RR5D}B6G}M) zK-g?XU>Hz}_z2{jkp_u$k@u~?wVmFkTYg5(|4S)S?~uSn?-~K{r7wLMT#6^QDZ$Z+ z!D6lnZP*1Z5z}y)5z7K`xS>iY+;lQ~)FVou&NxQIia3E5E}%p>IgyLj&YqlslBDAv z$DygGj@-02eW1yDa;`-bZmV#Dd%evVfzOj=3RT9)vpHQaM#`U}8|j{gM3|OM!OZS2 z#Nq$fX*No#Zg$Zx+^&AWL`3y=f+S+HS@at@RbsN}qi7Dl zKvdcUFrkBR?Tvg_7WA!qyi6Lu3YZrp)Bku)F6Q1=WuO3s3NCV%bx%Mm^tB_frCgZj z^zQ-@2*)J84~Y_JBqW_DaLq|%u0ZuJ9^+Z26{AaN6Ja^K``0_p0)a8gh8%Pdwjt_e zUQiHW-qS>SDVC{QU>6s>nn~#mc zwf5P4=99JWkCJX`g>40~&%M$JSi*EX7ycz1nnJZLKtHb3*=|2+f1AHUXddb|YH8)` z-sQtl(n`b1s93>m=uhL{f=|67V_0A%P6F(HS23tIE-7RL`#r^N>shi0EkL6{5acJX4U#4VQA)d zuf{JBEstmj)!mvuiwQs_bd^x=!7%!8P`GNS)3+t}Cvpblj{;NtAj`m0VR0E$zm$19i*y8VYVjO_$5mW{-BHKF|?2RS@H#l1YEIM8}Y^nMJxr0Io#NMi>=07I{)9gq6IR_y2K9 z>nH)nu{8uqX|yq!TrvtXPB(XuDPz0c`Tny0+NDrHWE$;gZ`y%&Ik*|oHb6ox^SFt^ zF%7dxzJz5qOR5Au(w5aBTyu_Yg67<05e;x@)&`jW4qQg^SL624kYp9q7@U>HYFiD+R+?j3m~9_O&ISOO=R%x0=_*4}H~AllLa6wHii z$^dkrAQTtK4ga+EM<7mIoutiX#L3BtXHDDboezvGkqRXyOOhRS;|JS;d=Q%#lK{Of z)O;hzH3ic&K@(O=4@obmQNSVjt8@pZka2G{anu?CY(GnS8=pODHw)&X*Tq8#j}N1j z2l{>-)_2*2LmE8yg@GTt2{3|{VI1^=xIGN!l4T>3a9n03Dv1$qTwI?CsoT`zMK?1u zf?rA1KthZ8IUBARqU)ZyjYF-{ zb`tfo=P;)_^@gdu-}=3)S!a@yWxr(0<|a!FOHpi0=JLbWN*?uq)QvSCN!=i(M)yvs z5D=yK+JgndPSv?H>vg}Tm2vG%sr6Ax$}cY`D$$A0@oIrbP`4Pf{-)hM1x<$dnZ3fczz(md&uf6&z?%X-WgSQ`Id$9$;8gfYs z&4B^YE=}qR68j1_KW@ADemxK5)67MAes}7t6i6qxN zAXzkxNlLa<&9%W0Gk^k-CK|?mb_R8+dX@>S32Qux1__MZyk8f!B2ix+Q^z;!ql_N1 zCPd_>0R{j~gknR_XJ131_p7$+@@TKm4Rv1lFWdcMb$*@hyZecIN{;j4{NM_RNs>? zUzmfrRPhW8$9;2y)uNth0)WKmWnr{iHW@AHS+7@-IIEg{6Il(ltuaC<+)|04j|*c6_>r%w&;hnHjQqycBH44PJZwb!>M#?6zA7-7R`{h5+DY zUx4;#wM#W~u=a;x{9yi$yG9;4$eSpY>938&_-EGZ;q5+--DJ03Cotz~X1>YZ&HA)Y zIGgXjvEg?F&&JlZuk9%{XGLXV!nnl$fRK^>8I$%_>Qbzn+y;JFALeSqeeXqSz7&Qz zP73KMx;HC>6{ajNny*2z%%IZRp7z?wYz_&;+*DB}qbQhIbk^HhGJOU+>(vW-{bH9;xsI^U-=Twp;_tCWJG6W^4 z;@u?gmKs(YCD>BbTV6pGBYR|%_t~AN3{KL*p(=K|rZZ;Mz7f(udYyZzbFWnxuxYE& zFfO_-TM&XN3atG#HIjNLA*1~McSE^M7HTxu4)%wd)eq4bEhK!J)w%}C$sHAId)_zw z*uqK|Ld6xu@tEw%0OwUnI)x2F6>=?~K|EUZ9N(mgWIo6H9R7MGidPl8k)K1Yv}tb` zM!fOnn|Sc>ZEz{-(^V^s!n`{;T4Re2%otgCOhm<2l&%z+jv8mv!A@!X3r(m~A%~SN z3XSDDU$ors_Rj#rZntat7-an_P~K3vin+_m$Q(Ad)V3(@IUnrRO$U^@=G+oe01hDL z9@5;YXW6dgl<%>fd1>c!eK3R>;!;VJi!QQKHJcZ1&O#l(sPJpFN;OCHd)h`-z!|sf zIcGb|_pRMa2)%n|yXngQk*`TnrcdEN8;{o&Bkc zB=6s_s{N}}pkC}Fq>P}6aX^WXv)ukH(T1r3#m6wUMTAL*Bv>&zkPBD;xPx2LI#%X@ zW)E3j3|sCTol2Pjzg@e&FL`a0PN^HgcAEV2f?)CvW{lOJS0=S&jLSOBAS!6X8xOC`ibFcbv%1;BGrrf2RmZHF94b1Z*afKl zFZ&Fgn8l>mEj4EZU|p*MOuf%2>F07;)p#-re=tW*H@z==w+1p6kqFjyG#S@X03^u- zjZZC*{$+*MX|%LSE_7OxJnILA`FFFRuCoIQ0Cwvvv!t1~u?^wG&qjZRvOWQagrHj) zuc9`Kl5k;5_+(*-)I=6=VeEF|_&E$cZNpk0X+b0oE#~tuuQC3`Xb)6U-oX$c<2K4} z%yq1+7hTZl&*C#-f-zrx8!0klc;3(T{v^xKkc8PpwSOJ^A@g6yb4*SEiIhPz7G~4_ zkX}4hnjQ#nWXuicH8ZIaPz5EDwYA2an<>RyLW9)>K(6C;FhC9xt1^b#TO7TDm`tcT z9h*tcq=J;Bf2cge1VIkofn!-AU@0j(AznT zmf%DU7tx@7PzPYsP=wzX?_Dwn-TiwAnP7wz%U2`4m97M{+Eu_rr^K*b1L=Yv5|AYM zpy__u_wtQ2-S&ZlGkXVA+-Q?3$RSrD&4sqBk^Tdl6)-yt;-gWf$*JwU+3Sqg=5sA^ z9h(s3FVrvWjL3GY>u$^5O39nrGRABxoH9uS7bkAKL>t#L`fRKx1n&eU>^ipGs z#eOYlaL~{gHjqD?g9=poXz3^qLlfTp}B3`j!_GnDA1;$Hp{gguX;uzQQH376JiEczhq3&p>YE~ut+AVQ(}X|;#0 z&<6#rilyNaOz%-!M?K3Xm=FU@lKwu1;94+2BN1J-FyJCJ#pH|o$J}oFmdIQQP#8io zZ&NV)Mvp3TQu`F8B%yu~UkgS-;-%zk1lBq^j}EwvQtPjxT~Q*na6)tOSI1(#rSSeW z3Z<4nh>2+EW_&bKpG?XPMB9N`02#;3<4t*x6G_}P$kd9|62b_EI(=|S-zE4WcF#nh zkpR8SLsNdEjBM{v}cTMuH0-|6)a43-3n%qT@80Yg0; z>$pls9ZVQuvBZ3JEjGyWViQXwJ6Gp2XA)Z7A#eOVlDqEIlC z_DbUqVeed}9eaN+<>?m-=0)uWnlt9+{VNvJ(A<>h)u?8bAk=VP|F}I^WDa055vC5d z$RUFGBADMxwa?3~rzvS>Qv#;#gx$0gX(ttI#u3AKifK2w+J7@_s((d;z1ulx!(A=J zl-l)B#9$b62|&0>ycV@DwT?L&s7#+x>zrRu#9^YPe>D$_Yl>X8Zkb2d&maNru$H{P zsokq!$zg&J9b@eot~rKb-N7NXwp2yzP`+PRmMk0zY_A}t-|DJ#xv>6T- z;%5}jXotV<>*LcYZQEA>6xEhPbO}KU=uFPDIHdXg4?KUizX9gBsr%Fo(xH~2whEq0 zokF>WYc{tQhG!$nWR<*)u{g2TxZOl85xIJJ(B*39g~R09((bEfabA`wb6Uo%)h)oW zIeM1nGr2endYO(K+C&KqadJ&0rnX@i0WeAeh6EI7{PvJA4kLY2y04?Ml!S^+yLc&3XM%@!}JP22r;5Hs&DYNG(`BR zk|tn(heht7JJDuX*s11$=W6tH$&6@~%pX^hAJQnbgbsxTlwZoqni6E;$N#QNr^YNGkMrV2}0L5U1He)o7O#9iN5wuQtQE|7Dp;n9dwD+F}I> zGOryLh9tK!r$b|j_4!s<7WH5*j6Cg}Ac%x8f|0=t>?WBLv5t_2EDl)upC*8_cg=`} z{flFjL|%a*>UM7wPafT2f49JDK0&T6()9q)=9}|-rd1P2AU@_?7=vYE<{v<9(rdpf?xXdpDvQjmk+NRNJyWq~Y zx%xgi5OWPPP_=aSd*4JIC39vz*aLXg0hcq@{C?aC-8v?X+L!hBro!c(BYj+WQm-Yu z2PvE(UnfFAmMBFW5A>=P!Dv3bog0I}1T1y!Mf`(}|3VHrT{n+P*l~c{OBUDeRFoed{zl+p7oL%mF@M5#mL`8 zRqPwKag1C+rzVXu7{f5&#TQ>hVaD0nxqc^URSOqTv2@WZQqZ?>IcB)MXQ}z!0K<$q zuMtFhmRF=^lY|zZrPYpKg`fo%ecXGleS1Ywl`m$Vb-L_ITlQC`2VF-$?VCZbP#$|w7tGukL!ov z&H?&Ss&zlQ_P^|?1xVhsPn27BYJ-B5`(%^;PnI`jZ8LZ3Z3liUjG z;3^WqTpDQKy;U-8{eNyJ8!k;9dRSxh%)urj_J+D5pE2sluJoIox$s%e9`H961qY=9KyO2K0eQ=z%>KO|3Sj`>rr78>9&ig(Yl0 zS#mi&-)j!*>rGTi+WVfWX%?urALlIB8KiJMa=nDHB^qr;RMfK&OAj~XRs$RTzZ&O8 z`?2S&Zte8`R#+a_!bZZHxwBgi+N=Z#Nw;~Y_)X}? zw|-d#6g@}4);HpI2%;@O^!Q%*U_6h7HNHPz{-_9dk{vRag2#^@V;BdVoNQ3C-hq)q z^nm^RR#TaQus57$U)k%h)obU-y%TZA-gys9%JPM%^lmNoTCNNuet29Lt)6|KM+Say z^%UO-U@6p1qzRU|lI8G#RkYSX|6^X)?yQwHhd=`m^RV#6(2kjNZL2y(Dp(-KNCPrd zt*@C4rBwr&he^Rk_tKaIsAP$b8`98ZB^F;Gi6^~#_bztR4moGJvw}POf|riMm(dfu zOQ)JHTQeQ=EDJ4`?H)t3=9&n*m~mdnKw7sL19zDX8iwSl%HxSJzo^Tc?N|FeGjT*K zUu^0sz7*e#Wn3%_cYdeYPychjB)+fo?BRxb1>^;((hxT zd4l;n@@&F55@;MzxL}&}g0i5dZSyvKs}+fZCO_zN&CH&0_n5)j^NqdyxN@n6H4r5~ zCT^bN_IAp7(Tt`%%N}}DrD!A!nYU?lj+;HZ^n20?3(PY{Az9uG=gi8)+IkIO(Sq>K zOV=^_8ikz+wzR?0*cG2vu9+TI-vN(I{(>OEuLoX&7p)DJd7~nsg2O2P75j|_P%3EOD3VxF|kD+6KXn&0N$9v8kr^a zw>7!XNx;d4SxK-&(!^wCGXDBDGouiP{1vtKML0ECcEJ5-du@I9l?8Kiy2eho=H`Je z%_3-|%@XLFnqF?Qt@W-k?6tmGL&YwYGa&3>O+lUhml9#xO?czYH?iAJO+J&R9qY-8 zK)C3EnLJ6y#p2jnQ|DJE&&*BM<9!-4qYte%Yk(({W4*(oEW@K?uc6nPcWI+*CIbB= zc=QUTnz7gJ%)oR}2e2q>8dP&ijW;|O4Pl>YX?)DQSOXu^iaff{M0+LEC9gFk{X3*Y zS#BNyJgy(OP-3DdTP&rVwn>gv>GUQeCBTzb5os8lq5VC!U*<*lO3#aa@!E>MV{H*B zaM(jh+nOp+J|l3+D9jjA!Z;4zzIZ3MD#hnZW!9UZeY3L9w4qYO?3v4gnC1!2)}A+Q z*uE@-eP*?9zT@*%64zhC6E&WFUwypt_1+L(wzk*_upSlAULs{gf%RfG1$YM zqV>=y8BRK*=@OGzbWO=j~=8BihvZq5*ee=BXE{o3yQDJ+?GnHkeGsQ`EB$?O(N2?L3e zKP5@1x83brOJxL!!M%IVW^Ft@!LiFnjsPbdew&YLG0g4(oKq%le%x4bHKjTtk)lYz z2-)*jsz&7s8$!iTe=`cQRPUOQXKp#n>RY%N3uLbR_jRkIK!?^2+9 z6qjPB=bb2CrL-Ev0GMzyOkggwM6<)(=b`uRGLJ+B5_J#&4TOOtb8Jcp<2d5tLNp-n ze)qfZ_|ap0^{ZdQ$>|A38i7F^bggS8FC8h35f&~OXxO{S;#|dZ=b!}^M!#pW5VX7o zu2uVDxjK*to3sHSiNSCH@AGM5=y(KJFf-0IS`qm*W3>?+L9?6F1f2e5|X8eAszH05a$uLBoYcvn?CyDBsOZWW&4aQnahzm2X#WhV0Q<#KRd(&FA)6%>h9Q6_1Ls4SY}(WA%MUR=1@ zqv$P|T#8@H*YAgV5U4im;5|Q=seDE*7p!bQQ)rvqq+Y=Va|Y)<=JZ~-^pJU8jg+w6 zZL!^LMYxSXAew59JRW-mOdKx2KFxA9K;ddmEjLO;gz|DJ!Rd}G(ZdoW2nKo1DcQVx zMiUHea*!mb&ac~4pK?confs4OIY6|Yzp*+&f!YUu3a)veD|4vd9@*d8l+;~`j++kW z{k5LC8uidN)+wP#f9v1L>xa+lH{F)w{esi6XE!ntH%Jt-Rhyc=GUc0QD5Vs+)*EU* z3w{-EyzvHz2;;a>6I*d-$HML~u)yKAAI*GyXgPH58jWE6GypS_wEB88wgZB^e&^01 zeKQb6q+z{!|8&u7(U<*SAJ4Pj_xt9$-#ghCt>@Y(U#wTi>crA)5?T_8W1TYB+Ka%! z{3aMRrSQJK-dtJXrW7`LS4q-&r-v|2s!+AF$Cq~)OSOeNoZ7oBdn>ReQ+HR3RF2<= zCd};B1$OJNt(7b2v5s%V$|_@AYiG?fd+nVN)xjQ9;SwQ}-l7^b6_<9kr{Cqe6NFxu zux?csGk?i?L}39dQLz12+q2A{?ar!^9r1YsH8L+X6qdmUMQAKkx^<}GH*(kVd1#}& zxoos3%%*$r)8g3*{+m{!05Ah!ZM28YKF0@|XJ_5l&|}iEaQmD|{d+*4tIT3M zk>OFzUYT=NdYh2*)I=|APBxOgbIuDu9^n$_WWEo9(Kr z*s2HJ7eti86trzNEqnSn=Ya$a>PTw!m*6H&VJ|g9XL#VjlBc%Mbt5BJSk7Kvyh_m4 zUg&?(<9DNcv9~rXIY;rk$DTaF(Ml0VzoGf=zp0xkPQy601ZKoJ_Dv^pO@SbiaftU z{XpuoYtD`Ehsm(>7@0?u3+&D#Ci8WGa=y0RSYJEpN?T@MwC$iL5zn4qd}NGR=vr@{ zY?W;dy~X4uYnnkW^lkGWTRq4V8HB5RxLTrb9{NxhP4D14uW=`zf9D9~B#6#zNX zPD0Df2!8&TWJWgj+}FjnN{_#iz1Qm-`fDjKQWsEIeMeGMV7BMFCXd<~8BGLsrV5WC zMpv?(9{Ieq7yh0DnCk+HMSps5aOJ?;cb#|4oA~nnkrJH>p5(LHQ?rB6?c}AOXt}hm zr%|r5a8EPhDY-lpv<`+pO<+5oS^#t|RCjO^@N(|CfI(P((R?yj(!<-)}P!T?ox5rLTJ z7~rU(nfBa)&}G-O1Fi{6!HKh{d6W995Gy%hp1;xp3fq{ae&7<7-Xu;bc^-MQp8*xA z%LF|aoJtDyob8^ELTX+R(3pZ$vwkS{*S8`yU#pjYT#hd0?0C&;`nKWly-BZ8QoNw6{0E0i?}{x-^W+KAc~ z#(E;UUYm`%gX%h%bFus$RTjO9mS*`im~7hN-gu)Eeeb*eS>0b;qozD{7>DYM(OKVl z-}ZsSr&(G+VSU02X}JdNT}GpzLFfQ`vUq`S(&kn(w! z!R%?yK=S)q$Z6$f*nq((IV)Z354M!za}l+^f0W9hYh`Bb0`=iT^)M5wg$iP*W@yo* z9k&-a)O_fh)c$YrT{Uwm<0lC+=$UXNVn#LtGkdD_0fXlR=>bs2@_rqyd@snq?Xt*2 z5li%~FwLc17<1?J6sM=B5Su^n=<#FZysgQYntHer7HjLNoG3mB+&uMSz>Ub$Gk53g z=xTMIdp5FXxXG?w))wwL(t!2KO4pcK?-RJ0QkU~4*Y&Wv=81~dZMNb3Uov)HSy#J3 zb5Sv#p)_XTp9pD4HMMgN+KR<1mP5f<l$qo)1A?0-kC_ zAT96YY5>Km8j%1lG9*uIMG2eD2{xMn7Z*EM&os_a`v2$w?L>oaSvvv9{!4u%*B>k0 z%A(98V0yH1u|clV5Szx2T|1K~`I=2d9P+9fS{gTpv2K+LDsOkKdc>0eqz2T6UUfdJ z?>&vgN!6#PWo#+UvuAQ0LmbPYecbb$eRET6C*EeczfZIGx;_t*zd#81;7)=)?YmkKW9SRInN-H4IJE#mh?nN17Gi;-C@{*1} zP+D7#&`uTJVvJNqRU5S$1lxD+3Z%W@wj;Gav;IZe?`D+QGo=)iBJJF{a{_>HzP<4C zL8bcP^`e7fxHxmUQK4mT5j9EkkpPOwM7_U?j6`RHn!lew3CMZElP6CA0(QFzTv$@G z`rGnuB+Oo7483>>Yd%5Vi?tV4KECvYSyD7M&_L){x;|s8cF!9u%n?-7{)j&e z(A_3sYaP~`c4c~X^2+D^2F%XS^%*5nzVCTCe+$Q8p(Ohn>Xw)nApTwJt)DlM6sh`W z8BwnErD`^Kg|HtCkY)the~;A_Q~<7RJ7ZoD__&8PbT$zZLqdn=s%);z-q#`Ap=5n^ z2IF7tPZqVHnt`h|;iqxlSB}@E(Im6EWY;QGaG8?c&RP^ibuPNFE)o&4hOJUkU|LFZ z&>U)Te4o7S`N3=ZFT07=zan7-b3rKurza#tv0_x zB27qD{9u`R2Vk6EWTXU~Y)&x_1Gc-2oOhZ;%s>j`+jm12UjR0kc%*B*kKDXhi!8iM<%HWF=|4}am94_mwZ^Yh3}}%`G@_womzZNe{MoCtW3{m{^7^+kr1>PRfWZ27 z0Fnmo8zQ)rw15hpmfaN{HwoZx!&(6)Av#>`j6lgh4``np}wF~?*} z7WQo#0&CcX9|jar^usKzkGk0zT=zXCP-11C1ftTAGq1}o7+V2b{lP4spAEL2%YE2f zkD-ls1(U9Ct-!1o;iO!Yi6&*Fu~GOt1Oo{H0Wc$tgmD?8-UJt&vegb4v zanmG#Ypwz(k_=>)@Vyd<=dm90CW)t1J&&JGG3jA>K-PKY?U+2~W0rjnjH6u4baAos zGg5--;CfwQR3D%olRv(?Dj1;VS1439SO@nDxC~6Bj^ye6u-CbM$XKTf&Ky)KC75G( zYOA?MvCGVCuLI(Qo=d3-8LS^s8lG!0;@}J?5RlU7S6wr*0IC!~)xJU8@Rp7t|7KDd zh5-aZF4YM?7LT^zy1y?gQA=YWI2XAmL`*lcE7u*MM?juLLt*Bp)LZjvf?U(8#uBd? zPX#DJIwwjP2EsH=*zI=6Ib#?Gj1bL=jVEgL_VK#Q`r#2QBy}?b7uziWV9@c-mxBQe zl$_=a!-zYlr`T?H$mJY28(QB#VHTzvOHFL$>Oy_k?0N<)bVQund=R9Ftjt`m*(wVR z`>8qxM$|qdpvG7eS6u796Lh_P?%z-*873exK&gus_Z43Jra(~Du+9EXvR?Czcy!TC zJ#I#9FLqa{;ajLgDO2w|Um%fEIZl`_G=z_fm@A zs+t)7*^tCU9EIt^a=UIRMd)|*X~E(5lk}oB$U3ckqwt*d$uW{Wrr+5;Btsf7O<5u` zpHd(KW0_8UoU@p?~s!F2RtpmpJ!j@%%sjIkxX^b`5p#Q znGzi%?O)!u?BCwGL6a$ zdxUEZa?ENUDENuQU`=4z^WsSbhEdP=>uxAU_LRgK@Q z92IG(IWf8G9l^m~>+DubdvI=Ug8O?T_JNV}1gL~SAe3UF^pY_jTSP)y&$Ma|T8GwU z!{M@I2&TV~kTuNu-e$|a%`$haTu86;uAwFzKQvL&T8Vc|#C}}HES6GdU9T8X!OUW* z40NwLtOuKa6}Fo;ZiGSD9nDrZ==9Dh?wp+9$=Ml7 z&Z`^B#Z8?D&KU$?Gma=Ku>+BDn&N^R%G5hIY9SDf>mb*(mmq-EE;CBWUOy2q3?p_t ziOGhO1B_!zFA_ut9d4MewU!lv&IF`XfrEh(@6l5R6Sg;Jc_>nQcrKxbQJpt0wy?}{ z6P-9a4V5A&GZ&kpIGgNo1-e$TER?1`iW-aRIlgSy`e)-gCS7i)7Zp()MV;r;qIvPo z>V|Bmh?P^2NaJ&Bb@%&R)oWA9u&9x}@AN+1qsbB&Q3-gWB6dS1c$82*ob6&L-+C?6 zZxt*3d0HzqYGWTji4BnNZ2}?NJ~sllx3y*I_0D9IK2z&la22G_PGyM6-D9h182kc0 zpeWIf(pUFVc67O4_@>uqicuyw8tX2aGXg4ePk!0b-YQH&UjeD{Z%;mSg~M<3PyT4D zFp0RS)~0$}et;PwQOw4F6LMy60CFdh(G#pC@WA&wj-j<5GgX^3?!gUh~)7CcaI`^zXj`6We zSr%K{{fW9wnxxOB246W+lcGSFLBzs zv%UGG@60KVFxAKm3H1^hmm*dtZ`lV~u@u@T%~-cq*bidA_H}}6hXhcII3g_Dw0-ZfUr}mplj+}r9*)s7+vO816ALhM`A4D@t`8= zKna6Mo%Zm`TAS&i1r98sj7G1hj)7L@Eq1I2`WnXF!M8NizWE?K{R3m^_#WE|TAk=< z-bB*`VA89r+t2XbUgl?Y+d7)5hzOJhJUQQCJ3Yjd0isdn(}gr@$QlDpbaP?2U&lO} z&H6?vKsCUi(@AEk+9>ODj|XV|0}VJmIl(ZFC{qSo7(O2Y2GFeM-e*?6;2BZ1vu)GX zJbgUoy^&(`FuNR#!vQ*sbNARp^{Zru%4A(x%oTE~E(CYP_wfgzWS2k2* zJ`QG38KO&@bFkt4hW1O=%OW%qCAkC&1w>|_Us>w~(@u)F+I?aY@4m9H8EL9NABQre zXkxf_F{aBAgmz3IEAImRe)q?yJ2(xPNa4wIgd0|ifwRtSqldt$ z!-{#)*3i;w7c&*5sVS3(#?;x$CEw`89pePLIB?hP$bz{Y@XB2JyP0J zQOx9G?{{8np_Sgx!>NK0)iS$!cwbp(ikH1fD@Q^X<1(`aY<}eLQVMp{PWw}{K6WEm zp0bU(TlhGdbSqt$nLI8n5%7ljHDR$2G=hIi$0w#Is`%p_=1H;f8z?lBR9#a?GCz6B zcy#dy!!T4672OE5`=&3=TF}b%<<~9DH9t9E5(l^Hw2&!|26oTZoU=l$82Lx1a+3MT2c z07Z0oaxDdg)VZW8RyDd8O*9oH)b8e4VM>?u=E}l|qivJ+u2PY!qtOuQqpE0B&QE76 zKdl2Z56Mh=G0v1TOv`VHZVYfzN*F1jP*sbE8nMPMObcOkk!r>y+>?;=v?8%Ro!PLl zE?6gH->~-A8Gg4zZ%(z*CLSXk>{iTuA%vzevQirTwYI_BGMo4=mggXvI=Z+^IEwzT7kWqr$p-%=de7bLdr~Dy~w@WMa7`hRul6lM_r+ z#xzZSeT7&I{|N6Q)!={u8bs*0H4Q}!f3i(h)D~`s+%`(iS>mC|vei9%RiKrYApdc> zjWTCWL{O<9NxV8B+40<#Sx{1I9*9nSan&lZlBW$!%%6=8t@XR7St?-E3w`Clx_9B# zcb|=<(7L8y@IYkd)U1o7xmLiT%$R1j-NmIumvs3OcM}&pqUo_=SXu!?9Y;eSYa1al z;*ZO~w7Ix7r&^1RHbz$x;|OOZLxb zYAemU-(0D8ebmzUZor5EHPDFn_N4`5{rY>Ko8xVj{s}cVg2k518MQrAJ?m@ajcjGh z4X|e|1d({(tE4;I2Z5nfP+{g&2^VxF$jl$-Itr%N8Mbz1Z&j-z`0?%E2o1daP_905 zP#!a8J-=&u1G{b0I&;`UL?>MTKh^JUo>{6tRx)8&+X8zct?hQ}87nUX!Z%yeqAl91 z?*Y1@c9#@8x5t#G5BlmDognL>?szq?s16VDF);giRZnsQD7>CoiSgu5C+k;r6zy3U zZWh}nq>h>YJYUF)|_Ab#HDX7feKAHC;zI_?YS+hEl58aproUuh`0xqRq z`eXa2*gjfr*47VU4kTv4NX-l}Jd^ei+sn$nuuy3@s8j83VWc z5n|4mJ=6GwSpRBYdST!8y`9_Caq6(Rd;lmVR{*AGRvs_&P2^xH#WMz*vvwD8*cWCB zWtMd3c87|>zgD-TV8W&84gsz-sTow=Z}BJB+3ICjCa-7ZEFDQV*28o6*VQE93v_+8 z9qa)V)c~WH zZf^!Jo7Jmj>n)vXnHi}VpPKbT&C~JO(Zg+SL4{<@IcEE6;<8olEX;bZ#&R%L&r>f3 zpwdj*dD@+L4oh?UwM&XOSik=yr)(z7I)Pub$1XXTIp6wZ(zG$9=B=Q@w4^t+AHv{n zZt455tDC=oFW-0eRijBbsI+FEIWKh6M`*}=o{a6=>nRPebmM*7d2axX?s7Jwb*4;2 zI5|0yORR|el0s8~Tk0*=7Glz|I1X#+k9xVz97K)M?NF)1H5xbC=4?_dTP7ID1Q4sA zmkJ-xCxyOR>0Rc>V7XAUgQ7|)?U^d9J`^k3ho@62z%Yp+N+ZvD!MY)5f5b~84MGw9 zElv@UT*f0Hq3)M{Bv8zyA`X`>Ew2=*m%1UC&!W_v1Cl%ot}&QB7|aY&qjJ+{MxwSz zE_5p5B*%Iq<+}QJ|Ayc|h^(wT-3bvNh9Q8%n&Fmo{mehcqErmFL?Om6ae7YH`q=VJ zgsX4y(r9y{uwp7gHHX0Igx1JY!Wa!wJ{ZEpp_GN*h(JWRiqR zsLvc)Jp1BifM?Y{FTq6odF6VkuLUqQvi*z>mTOGR?Do_oZ!-D4ms&fv^&FJomoQHu zy}%Rkwa#ZZRV3Z9I>joesA0fY1(Q93Ox*#9c}22df~)$wX3!)+@1S7&o^4-M5Fs{j zk_)-!D6G@H{)f+YqwI4qJ;coMtjZ$Acqs0(V|R48ChRuN9BpweeK7Udo(H2w1yKdQ z-|B*<@!@^fCd4@)5XOd4n@ahH%E4%Q@mydS{EW0GY(Ji-O|w6q`>B@$rp6E6T$D9} zPx?ovu6=gnCQpKQMYIgjcS3;b+^^vBem|??AWbFhwUo@O?>i8$FU?Ik#}qePhHJxl zW;eafV%nvgW$;5x!(AA#Xdfmq0=rbH(`DwGxWS6v9pf4i=OCcM!IanO4-SA7w}9># zj6~IGxEYVzp>D3dMIoKbb(Pc&lsdc5?1;|iP>@FbnCpE|-;8D7zMfx1R<{-JKj&fZ z-AdT#u_dP61?rZXlg6iA4hIv*=}({axSnb;yW^0+sJFcmB3AnLjq~` zUGt+TsLvJ?Bc)^Y49^4vyPJeDzsmh;QhppwLGdmSVszW_^~OH?29EURf=#{Y%gSX(k_Bx<;v^24W%-OmBE-*A zqnwN1(0l_Dc{*7RYCwu(HC1;Cgx$m~h$=4rtV5NyqVpjoQr@D)sV$QrA8w;M^HQ9}Xc>4RXQ!{gc+1k5ggVN&6 znDQhw3#z+#-{hrrN6uc9016}Np(YOIKdxgiAmk3QDM)gPyl6rAR13r+!KWn5n$<4c zmpa-rSwS5kuDodl8Uky64S_*7nj~6=z%Awn?H;xZ>EwN-4qUun)iEr*OSM0~h(1(O z##P5!iq0L&+^}jd=D}19ZnGqERo5#F5-_1*@s`iS_PkwC8K>FPr0W^*I{}&X+5*xf zZ)$czU<$S*PUFa}I9&gy^B0D%>q9mdSkg%I-64G^rG(Aq1ZhZk^5hK6rTU}Tc!)5M zH?=Oz6JEvk+=t0-yY{|mU&6zcMDDp_y-u$_+u7%~MF=RQHybb3J3NT`Jh>U*X_Ou% zUY61jmTEd$tup@Ui`0H$+qHXzI&|xJ*H!?<8Vo&t{^rX%J4-8sf1g?N=F7S&xPTD^ zji$%}Xm3DVL;LGv*)vL7h6wt}KDIDGDdB~C_b^R6Y%jKcfC0F4x9r184+#t6Gw*w7 z8UQfmg5B;6LrS>+(u>&bGR~en5huSW8HP7SwHoKR9`CLRWKWmoed`0vn+OCyg zmQYnqrqiP}FwvxsdaEf;OVI>k)zr#hV>Gco%&JsiUIL(t?NUm9b}7`M-@-1RQ*RF_ z<$;9YCA{>~eUwu0@X;fH6|9kh@tKc})oJ~$f3NH(N_tOBJ8!Wn14;&xWS-)+_W#Z#K$nUUkLM{d3q%;Jr;Jlu`Hb$h25vAm1 zJN`fK-g?*LJ4X~QMNJ(c?X_n%yab?JR`+e&@ChECm{RgYKE}p?Dt(!K`KpA5ue-O_ zYu5g07TOz(>8ES1Ut8Bw!vetfjzxW$q%YAN@=Q81H%mN&O=Qq9s(nHehcER4rd{wF zX{JGNvrZ4-iDrb=Ut$;rjGGO{voq|byl~XLP7&wU!TY6Mk0bH!F&NveUA)FfGeCAT zrGiKR_4@OhC?*L!C(%8J(o)GNU3{(=3b(b&_tNn`Ou7kxH1=Z0ft!*xu286A-RG(( z5x6Y|W-3UcYU(jCVczNOVP8rfkuU;)B{kww@aXMFdV8x;&Tg7At!Ga^aV(%<4f#6( zGvmR7hcd>b>S371=cr1bZqcdsY0079zqpirh0z6Q)uy zgwRpb7OsU6-)@J4$-^`%s0sO8%}*A|)>lPGfEROplv42E!P{V#u@k8rIx`(qFmS*e zaF3zHP;1i=Sh#hi2`0pNJ2ycJNfXt%c`NjvmUDHrCaUJB8)|;-7LF@jb90C9RV{6# z^|}4N+rS3`+v$8hq6nUS4}hgPq$U7Mw~(;FvvZo7cSsn95z{mYA))~C zoG4T+##&AWKqQe|ve-RUZ5*;5hxcy-Fp&}?tCuwyl4vXJVy>H%ns2kz?2J;9UQ$L; z<^3F-p_?xKOP_A8*xBl#+0AAIK$!C6TAT_dj*np3OXFzY_iN?@(`t}2i$sKR7}ZQr zH53eE%(359KuEs7Br#Xs6?LqTJRri;Ere0)m}^rnD?vP-n;ctUQNnU8g_j#CL955S zHoyBzrCs}Ji03((%@Z}_e7eP&UE?UFs7Bj-w;Jkml}@km_@&w8D&^Kq9+u9SsHWQ` z(G@}k@hbxb^tMQE`Y1)JM}lr@t6sv0C@L5*O2~T?qLV{ za;S(BPEJp;*_`0)$r-l09Vh{b$?$=Xy53DvO(p=`0h)%?Xgy+YOp|Hs#@=s@DE-25 z04`h@Q;8Z10WMcDZ&jz@wY6&JqUqmEQ`M&zgbG8!s+rNe@6_&1 zW7-fG&sOIUdpny0W@P=&VUnGHw!J8QG84$E!*Gx8D1ou)o;pY7E73fS`SD@xhus-t z{fb%?p~1RmQ{79Fl^wJ!#{r5?X$M_778mZ`;Q`dmdZXo%5sSox#iGpcgnwgFKbL|+H54vS z8D(NH5z;U?L+}I9HHY*W=6Ye|oRM?!@IZS#np@^ckJgkW96;`3q_eYh6)7Z^QY1XC zA@B-RO36VzX$*M()I=b0P`5f7l2FA_5S@W~MR#!ebJaW%GFRUgdwxIL4!_&8#*~7P zvZy6UCeg{Swrt<^KMy_Z_iH94nm}~9>^2Wwu0W~syTGJoTq80+B0>&*G_x2n?96>s$lL3KxAmJ{88xG7Z%hkPUV(#2|1-@BS@unnd5N&y?(pF4w_*MPLmIHd zr0-SM-|IpI_RLCelB(>ZT10<(tWD^HEgrQ)@{(qM6KBmVCMdtELoY9L=KK`w>}r~^ zyb#vcTnZjPevBuNpCFfl%`hUVs#^~H%{1RIP-Y~gW}7ASZ#=wYNM3tH_d&q9nX&>7 z7~;9^61r2k{i=SGP@ZKe2t<`Dz#x+R6vo^dmGu?MFySbmZKUDX&X)uQ&A?FDfQ*qE+zo%9}G_V+lI|Hcr_VNK(_VpGwE#q7 za@my~rri!h8XY{D1Y1fGpm${0=yi(pO(khqu8bB?6s}3Rl4>s`Qlsu0)7GuBHwwR0 z!FmBIu3AJpYbuPked$s4r|^g|o@W+aFGforKYoHoj~*+%oIAD4_~#n3Qi{)??FnXJ z7$g^l{9Wbs8aAx{9#E|g08Rq5S?B!51gI;NwIoq%L{WCgWg)uH4>THYC#s_^S{jCi zt{%7j54Q!a*Lxg+CH>D!RP`C*x4QtKl71pPJqNdQz4H8Qpl|gedp4vtHoKBxfS{Q$ zM#E4|#$2u2{2_mtFXv6I=spSX&|;Ee&ITk*iUtnrY3t-zn1LcbM?)I)`f*^IatkKx zOeN70Iul6=+uaT)BXD|piYaHmPk|(9!R3(BiIODL z)hcs|w{3F;rcwV{N)%cz+} zjfPRs+`#9={3H@m(nzq9{ckhH#YUskYzpx~^Kj;3IUjxQ+>pG9nVu5_R{_{O^`M%=*3Z zxm88hE=ee5VP3VNGD}{OVHl8-NaPF)*OIewaGkL$HMn7(8B-0Ts3FvK)b1E%!_R9QS$ST$v_KRxhc^HEqb42xs6 z?mem?!m~Vt^;-3D=!|PVQ|#KRm3I)<<-j63nG#xJtkE4Qmjo`%_-50;BQ#HIWn6BN zWryUP++=phrSit6IRRngC^Dz^+LE43aWs$J^FqDhvIcgw2fSm$)YB>Sq)J~7I=U3k zY_02^iGbvAHTPjaO#Gv{lzCKxO=)oWqF2_w z%+wNS2I9g-oCwoYkkWv09FenJ;wFGW9Q^Lwxuf4@2NZtC3|h^{$;ke?Bvi%{&dmzAVeC0Cy!yv#*SfG@ z2?K?;>e_M_7lcXRa?L3Xt;zu=}g&W zfC$c72i|=m4?{IuDVQ4*)%Phf_5hd;WvV)Pk&`W9VfV$u>i1GQ-13^yjz=ELW6J1W z{aWKKWR><3k?MkxDzZoOX=^T6lO$rf%-efiGtudgldIMm{-GbFg^*+QpeAh5m^c(!~_(&8k!!9PEH4=vqi5p?%Rf z+g@*_s;F*_ua;6I=3XyRgXSKXri>>~&fGc82<@C{nDqDX;UkRWpc*;Sn_Wd!OtVnr zp43DHjN^zqrzbeyZE>;PiA@{8IErIs$&6%s{D$@@L87G^2tgGzwSDe^pkrFA`6w>! z)yiY0(kCKp#sQ@;rfEXfJ4Yxbx!mjq1%Z8qnURKs&1M6}4xA5Hf6q;Gwa(d74U<2- z^h~t^%!L<_@&Hya(KEl<Lr&Fi*sX#iSt76;)!Z+JfC~R|%pMVXN43MG%HTax^@7asdJ`Zceb- zY_QwyBxStr$=ZA+uU5Zl2gl6@r$m^tgnt({t&53}d#B*|qNb?D65Q6gCjne*>K2** zJ)(E`%7h=+PAH(5a(;Rz!_5f=37Wj>+I7zUzSd}3aBV&{a(B^ylCx;U2v81vWZj)l z6+jfKpoWMYY&4pcYwSB0d`odt&L6e%++1HC5u?O%i_|n~*r}+hZvR-C-Mdx%U2=pX z!sUg`>UD}LQa*Wp%CXzNjR0tf%`NbkUa4OF%hqhAo>km7GsI5Im2_I#B2-Tn#wdQGR6Xy$O;c>g#R=^Vfi7*`y*gunkGDa_{hUJX|s{5 z=u1I4Lh?PPQ0%0R-#@x!a~%VW8%dP*0n|&{)J@OK*oEj7BDr=NnVU7202pFS=X9W&9IbK)4dGk{o``gG6NJV8;}94sfe~#d@`s{B$VZKC$GsD(xY59|8-%Sejg*C5cPdUJXw_% zbM&&zj&{)#Y#a~`otbr+?0b@QoUjY)bS(?3Y0vIgu%3&VWY6zdW zc~w6I9VgeT4OU#+z zbHUNswnm{46AC)?V!c86aYI_;%lsjvSCz@`fAaeM>DEl@l$jY5%Z^G zBy%XcO;Ev>^;Z^4XfK9wz@0mHu)Vmz`F4xVFgCQ!@x-DwV>$?gUkOtp&5`jm%96NW zyIln+#RRbVCO?ETUzu>OgIGq5tl`j^6ioyx+s7DW0}|-Cp4q;(hyY}(V(?JA4mfiYhirQJJ1O8uu*f2jdNY`z+7bmO z)@G0>m7nh6|0^IQ*gPHTX+;Up)cwcp+_lG>J-gOR_w!<(CwsfjaTSVUb)LQ-qyQ3F z+er^jc|OrOmwwO8bG`rMQIJ=X4s&lriz77lFx|TMm?E#Wzd; zuL33(C6FX@)V&opWkM-{rT7{Sf&3__Md!1}f zaC&lzC&L-0-43+bU>KjSb;kG(rG%5s26ykAB2SYfBuX`w+IC}fzFex)LSZ1OO1!va z`3#Fn#9c{y<)oP>wjX%g24N3#~A?qg~g9xXIW z(F`_EoxJE|cLMcYtE+a-zdVY^uI;%!W{&!5OrliJIZ*xVZEOK+I9<53o7Lu2S^LUM zS9OFuv=MfBn4y=)4}SML7ac99i>?mFQV+bWQ`(@aWNFoSmH`PX(uU zPeGg-_3BN;%l1pOJqy6G)U8jRoFV6o&A6$X9B_gcd$b=*tE~`662>bRm5zLVZ>A85 zlo~y;%^Y;>L8^d$_TB;2^xB#OhJ(PwvXaA}#e7dz)hFBj;Cdg1`PuY5%AHhEmC|A& zG#SrqY+90P6k=%In%BC0OfBc=E0UONdinU7zrVa`b2%rmtWT;nQ3_*C)`_<8%j&!? zO>cz*LQ1wTY>dT0R@18hJbJl(VvnDdNMU=hn}cO#FYIU7mlc~OZay*jz=7Dzggn>4 zUA4^5X#F(>?TjTX^DeAZRw_O10_NK<1~N?`xtzXVRc-Q304U;u3e6Q&YvF=vJAol~ z)PV+ME-v};l_%w}K8df!AZ@Xv3Z8a5?6zA?I+W_dxxW9PssS))h>3`v4k-~v)fLIF zXgd^on)IS429T#orE58iS3>7UyyAyxFLQDfp+Ber3XupjbsY&NnzjJMa3%l0GT?oQ zb7D##@6*0{TCKxliIEAl&IftdtUh~ha^*1W#pz6D5Zd@X8H_` zdYjy6p{~~6p`~I{OPbAL&bSG4Y|HI4n#%#)%6kZF9{S$0{AF8v<>DjzFyB|tf-$#N z^cJl4dt>vqU!Aegq8NR=jY4QNKhLT>H&Gr!Au3Tc8>A5@qBKita%;V4r4p04i9GVe zvQn6u_^?vdwU#otwGlp}vX6SKmJS#qO`nacrLnzI>7eokM06y1Teta;=Q0rK|Dv>c zXt8~9?7MM4d4HI@>iNbOga{PCEh)(BuYze2((G@EKq8P|SAH3jjx2~w)YLi~Gsltp z%LEK&a%a@4Q3>r|Ynw$dy{6r0H8LUylmO&-xxU8S?XH&f>Tj%En}!^Pomb`x2S~^r zt;as+T_WA86BF0H!4lvRG;P+SnfkX=Il~zV3d(lKkFZs$jb=4E772meM2>t(qp{-r7e?<99?o?cE-D zbVYmh9GI-rc?!?4&vz3#t>4=XsrG__ihbdX?N|gz9YW*UI;*g9rDA-@`%%M|W0eVz zL~Cr{w1A619x}b&)=U7XU0CaypQXdgGGEd98rY>{_A1r!(;A%7vtw<|r5A5gf5QCb z_5-M15lEH0KulO>rNus3duqGw@(eW;9g#zeIsF;DH}qRowYS`}ifV)jpr{9W=(l#n z=?BMeJ7bzJv^yEy{vrc`Q0ttw{fURDes)>uYK;14>&8n3oQKL0&V8Ft1z4#!)U*nzEPY5QGHVx3)c~cErJ)7;GYQ+HZL(&2bTSxmYh!nnF`_}lHo2{ee>ORKx=e{i` zQ3{aknD%_r&j277_0)Z~Ec1Ej{eCrBexXU@`AX`?6nhg6zrn<>*ub4#GC%l4}sCl|F z(IuE@2zVo^T4-i9OGW9_nJ>dZ0V()oZIcNoWXY%`S|eGawkZQ7S{;}EXL;|rRNo7>8wVDa4{(-64lwfEr=!i*^@Tnyjmy1eslpq_ghG`#Yk=< zP`O>tQ>qo3YDb6SdNk_wxORkQW8(QR_LTi5x_VGrpcpH+@d?Qqh6FA;#*{Ep1@D$7 zy>DuXh^75_AIyw>U5k`zwGW|X0@m(snpGX%587CS+$i31i%>5UnR@dd>j&&un$x#` zuv%x2B~=SyAabaEHyZ6^Vx{`1VYHd+kFTz$^UWhO%MvBOi-e2NgG&2YXuG#{sjgGM z+S7qm!Qt>ekJL1+B(oFKHB+}42l2pG*`c|N7>0}Ao z7$q_V`(H!tZDSV!HWfuE@Kz#bdtL!Ry+}`fBno@CXzmOmP)aCzmqK&1JWt$0)OSa zH4L=){xXx8tNt4ypaO)74=O?JC8M(8(IS(v6wl65NN>aqEVh&dV7Fr4E-~kdNB~P@ zVe?6-eVhYYsWfmGMwo-cO&&s%3_MiO5c8veJ3wwl%jJ}Cg>s847I;9tKi(K2JPekA8C$(mvZYzEdvPj3b7WFinzJ=JfOg z(>7z;P0y;6KEIa%(q+Dj<#$H+nDnTH!`(!7xOS>OBj+z+vl(R{7U1O02{z*>jGkTI zRw+uZ9XAuwG);@$k#m;In@s9*tw)qG2nE?0e<`({l;XTpa>6VZQ*F09|6VSpk_)5U zEMazH(vVb}A15}_D5V549}w#Zxu^hh0*mC7q4P|H!oV<&7&arO-43kUvg9q)+l9qH zi4P}0%BaL{uWLORlGFh7lmy_F5aI`iP6B2E7-Z0#9Ku2<;};DW2#l}**zI;2@+#NR z$9m%Wrodb|0)Wzr42@5cG3do)aP#b5e&sJSxEwasy|#4r!nD{ep74DwL$hsrGX& zEht>jLjsO<#60fl<4WU0;TnRg=;Cjx1STLvb?LEgNqS;8ZzkL*5Gng)PT&Yg!w}y< z$ve0GKEs&QFzE!`yLS(1AiTZZLY!L(#HG%cj?);d%%{xLKnY^s zE>5I<(io_>?X~;Jq*uGhtYJfu=_)ZcIWz1c`A^#{);`KY_>wkcZdj$#ln5s$8{|A; zyWL8SL?DU)BRLH-XHHwM>NjBfg1nB(^GKO@WX2JMcg~P)58nuD%k)7mSgACuVfd@1?#((UjKwAij{~+oE$CadV9)r0G4$j4rkV8GxwDU~ zCxvSw#q05A1u564;j%JDbYmOfn9^+grnG}Mr0*)D*~B!`q`$9M8Sw2+LM_3EBb?US zC2Ch1CS8omM4nDBy*n$P*E2ytFmu$zQ^srw1Ie8!X&mvMcfT8FXJ>fp!2_I(C&z;{ z-VZ2x-*MC5erY_JjnS@*zQ3=6)2Y@oqya=EHyi5brWH*oDT5nuaysDj^wc#9mUdbq zBs6D=-#6?DHd~k=9){oNU{xrT+bp5s$>+s zdK}8Ma+XWJTCXINDwE7hDJYZl9fO)(N^x^hLINe|99B9~0nC7$3wBe+IBr0cFiks% z)0>QK7z9Y<%;M)USg0sKX^;^Vxue8|4L_#{`ye=&8t%}|WsJp~pq){$o!`{FBged& zr}9)_iX2k2j7&S#cXY2MN=Q0}-hMYVHlYednxRDcMn#pg3f=;fZ8Q}+rpRQBeAkF0 zHEZ|`El*9W&Tg4$zqCxhzNcVW-DYH6E1>I{cPe18v81Nm79<_g#~S@4>*%PlGxAtn zL=GWKY&aDj85eM^je%zMT~0MkNL%N%LbFXRW05th6n}4PZkof~#B}pzoOh;+$e>7^ zTY|a2urjUWbpTM6U4JxoD4hv_U0QNSO{~C|8M?e~akXr)nL(wm61>%iVZh1h23%Ow zy0@GwL!>!q`|WtEe?<7qnJ@YL){;;dU`{9*swy8)GUIG_=1ljgmf2x-rW#MG?TM&j zY>GuhsCrVbsIE$8QB|hF#&7E>03l&ON`!GUVsm;2<2a&9WeMP__OyO77xAI7L`)|6 z!0vKTKw%-qy04gufQ};IgW`E%`92Ok5?~ZlmBXbnJ2%Yg@Ck_eeT-R63wX3XRghvp zC8_TP0FXHt>I?{=U~kh2MjUS`;3A--lmg_0yLV1841-*st_)^K2{cHkan4ym6sxIQ z>M4c&-jI?Dpkp#A-k6dw@Y2kWm|fVzy<-xk2vXhTLBfE=tTgzbPEYTEIiuu)gu&}c z>SrMeFa_&Ld@M?yeD6l+6GMIhMpocqH|WlNiKM<@8nX=-=G^Eq`Sb1NYRqcBq8gbM z`_b$vpP&80!2+0ApAs1!|hz;UuwhaTv?qSjtcW7pk7j6$=cC$YG>Pow_>0L06js%zV&AXwQQa0%%^J1ncPHO3u9j> zyU|Azj#|I2TS60<0gA}9+d}KnOx_N^eCF0t}&^voxqE+HsioS#TI1wS|jIV#=4L!Vs_1k6F z3$Ofs*$CG_OLWO}P;pdm>KL^W5UUq{Vvwr%P5oU20b@MbVBCxth9pU%iUMf{I1_k) z1kWUOnkyurk{P?*PHzLPeKv``(QGjR#3)&8*;)4#1uX{T5gkIv9a2>-?HS0bp4X-d zu}k8z6IdCDI!7wek+aEOw=m2ES802{g+N(W$ksVbXFL%`R3<3anxYI%qFfC8m@_MZ zz#Aj%S<`$cXRdZHNh$LkCQLGy0xn{{srF{27Y3Sq{w*6Wxu6oI{Kg5qhz9B9m+ym! z@c7{)TwGiPfTs&&ekKM?rPrdhH1Lrs{qnTzs30S1G@|;wC}>MbHC6V#?OTXHRN?_9 znMV$q6;KQ$Aj!>f4%B@^cF*BLXh8rqWI~tG84iEjxw7ZOWpb6D12gfGefG>L)`*&& zRSuuc`)VJ{es1=sfe{Xr-7+v+)F>CEVZ<;DC_Eu^fmIPX)MXOBe??2b98o-I-_k5) zJ)%F72?q#5bsQ`EyWMqz2dMiVFfFM2Kixuc9}4f>iYKfS_E|p|(21#sL2jf2m9@8Otx;M`gil49Y|H2q%$u#PPmHJTu< z8_j-pi-W~V+$BOm+q3G}=AZ>|jr1LQ6bTBm`r{1h1&)GZf+zu_IuLG7HlmdviK1X* zeko*IS0YrHkrLyD`!9eClhhHw>`a@+`WBSqX>vS@?s7D#W9R?K5{D zFpUHN6qWMjJJTu}MPpqf3rDq5R83)XD*Y}Z;fGydODYNk$#+2jrkv~fXQL%aGz~fl zg|kOvh<}Ixx|@@Q~wqPVrh`w%td`)f;O`_ z(XUc<&KY@{kW^}(rYT6a2!Kr5HldBhomb~^P+FWo!>Fn9jsAp95cX*VMl~cjfFK2$ z9<}HUDybwbo0%aRw=@hG2hr?IyGfo+Bqp_x6cCynRY;ZPhf(v8gXwy{1waHwk5Z#M z2tYEHTHp2UOuF(Uq21!74D8B;TrzUWD4YSK*FiJS;s5n)@obL584h!6LW?}0wAaC{ zg@v|G*Twdje`2QV0kg0Cdl*pHY6=6K3&ug(u0qb`{M9hrve1HmjsAtBz`bODwN(3( zf(uGG8AohR2E1_Z9>#IRg9mTp?BcANhR|*Z>3yBB+f>aQUFNW~6jvdd1KJ^r^EAq3 z{>6*G++Ke>{jupe^jX+}fst+@*eO-D@SL&sONBBf5d#qOf^)tqDUaibaWe|2wnH5x zY1;!WlalXp<^X0$vs5Z*O9al!=oFDt5jCa*Jksxt*#!ax1q%i2zavu(K@B4K+(9$Q z>p^SK376)U!4MHlX-vKf!stv(mrJe=g+xVS(mS)7EOvP-)uiP@+W8ADbE zUCN|eN9NQdV)AfcC;$qXU3G06{~Vng1R;jVqA=Q1=b9na{@HvJB@Dv=8VJZ4MFH(F z$~h~;jCcQ`{6@??EtlsL{qpD!sn{E`c9dz z{~Z?ko2n>Nrp5Vqh9v+6L+;ENQo=ZnBIR&~FeiJbI;T0)^+R0=ylXMQ>a!qfKuU6X z@{kh7VQ|%;`935C6;^E;MD3qT!8iz^f9t_pFkqh2;HkEy6WZ5Y)sP^U6QxhisjH90 zLW)a}Gus zDwr~RGCH@}Hy#<}Q4vN|38oX5OCP>{7vPk6|Tq~j4l zHVDWs?Ak2}6HdNH#lVJ5Y-;zrwIz!jdKDab$>*oOuD;^P{aj%WT;?(_BAqsw#eA5&XcQ<@#jwFGT4r?W2D zUNYvX5^Ndi$W~?9u6bEr4n9{ z+SrFdA_1~U>%y!wDuY_OV*53>^ueV9x3z zVLVGsin?sCCqJsm@FZL|U!qK?fKmmpDQ8TRoa4<#oWL7k?fce%eT;H4 z^MW)zsRqUNOoEtxx$B5yjV%tLZAqB#DD@&>qAc2?Z&(PYf<6 zqlLvZdV^~~{F2E45G?_ngBbtL98=MNd(Xb9ZJ>${X65pCAH4Mt%mw2(NGLW#oxu$o z)8VcKmy~M*3L9wLl&p*@=Zx)khl`5~Ow)vM954Am|(jO!cLW#O$x1`C^FTmtqEY7 z08w$5kdRE~K`>d(3nO(Aw%Z9`|N0vk-+X}Ww1Z(&)9kv>jcV4m+X=x zKKWYMGZsN%;G)zwSi-BNSf@q$AFxHD1b17mv zO7$X3=HlSlBAyn)sMCV$l^LLz_q1vrY!h3nt~Lyl?kGuP?*~QxIcITr8wNepgG*4> zq#rcj(2D+?vCsQA@AqM|K?a-WZ;(kLx|WTqXQ_#$$i@b<&UdS8i2j@Gio%n#$(Xq<#es?(G9iKKI``S3QG zVXExteeLH(%_|@y$Oams(Ngf7DYBn*4I}(P-&NvN(C^n$VXw9qB5?!(1ib}3oVIom8Co~LR zFHZrxGoFGgw)$gOsnITzKC7w4+lljp{lyrK|E&_T(F&n?wI*hE$GBk_U?yiUXHg%7d8k!pk z9URvIgpy@nS92mjC`c^ktfF_w5Cf`A6LUC;#&4Rk`e~p&S3t7{z7|~PnzQ~vbw3%H zG#`}cUCdVRw*(@B5XDrje?<8Tc_vBd|r(1d#)6MhHt~i5ch+xn9{9k7Rs2i z0Kx5chiSLNw40Fg1TK>RCIy*kOt9>>vX|<(hEYtlDT!vOXnvXPG=W*>H4RDUyCXRG zdftrB;wU;DMe?Wz+nid8s>u*GOwJipiUNd+tm9$z!BYZ;0ZwoWqi~yKXBEnc z4u{op5m=qkjx8rqfMcE}Jbv;-_F1Wpl_^NcX(VjM6FHD20cx@^F@OL-2{2i>39Fh> z*VYn2F@V7iW+BPS#%khFXySJ44u~k~cu~I(z(Vc#4yee89sJo+vg{6J{!9{;#0-f- znq>y%_(wDMv(+$lyn``iJZ=+)Z?TjJ$W-@o5EuxgXIH(?&HQ0^aS)Kj7KK9M#UVFH zm(VWzc1iXtFvVrl`GA%yH1P= zxyem60VPAPeiv;;X4~@u#)cI0hHzMG8!o#q!pUaD#l?VJCht!)L0c)I&qgJ@{~eNI z?GA&6a5^(Lx>5lUX-FzTbAR}r=CFqd+lwtO&d(7ug~|#jxC{Oq`*I#JDiE}B7>J7b zMxb};B=zf%EXVSF=BoYU!r;M7CoQP)76WOuglq&rgN+(`U6|br+wK(wu8F|N8p%^| zvE5?YZZYk)xY%w(_<0KcUgEEnGpiX(9LY_qM03AqW^sc_ zL1BVP2}Wel9y;m=v-bJ0vhRxH%RZSE?(uV#FxPO%TI-y`HSdcrYmZDq1)+yO$iM_u zP_Z^OX4hUwl)7&oKYjvcU`!h!dH_r!9)62 z8e-`{Mh<5r%)T>DW%#88upG}QqE;^mF7a>mdUv2sBzwv|9jkFJW zObraA5`d6yM+L=#P*{l3KJTszAZ&G6T>i~XoN-u z+VY+RooH%&SBo1xEO>JE1iNVhRMPLSW!sku$}7$1AV|%zB{Ps==RlKV#m8KbDn6M# zr%i|g2#Dw5kK4Q1-KOFF>*wrFBJnlO~qBp?7glJg`l7bIVVR` zC{-$x8!1&=V7&|kj+B_TEr!26vLKvNuJ=mFJp&=%zoQkq>Npar{umQbQ99X%WsT)# z4;zXo%MZdzoxVOCes8;TW94a^P@dmn3`2`+cB;fYR|^^QN=X1@1Th8#DT&|1pa{P@ z=E0eJ1+VPqk1aYw30(BgFpyyCYgTO=w7(p*Vh$#_ni8WHB08tBKWD^B=S0DUOIb?P zGYO~=&weH4B{#|Cy8GPcak=+Q&A#}`&gTr^FEteP?D{$g5pZykQUOB~#j#Kri7J3U zBe?|RT}fr2K!(JhfT>hG)X*1sm-IhQT92r4!WL>^nhv2d-vz}{X2RxJX#6^8EG4Ox zu7hd=hRuK%?%o3>!sEw}HM6A9M)BQYOw4MY2!c!yO-f;ui){nSqNAS`^S3i#N@^-1 z23C~-L1SxLleqlxd{ky{|9}W6oP_Jk!zlhr5s1vU6+=L?bS0V+{abNVttt)?Mx8cC zU=DT8w%j^)jtu;VH8*XIRvPKBgZZ$Rtdkv)KnuVHxlt$dSLl}k) zPPP}gIJ*Ff_=KFFp9_!)>teu%3Q&mg=&bD>L`cR-IC}?I$V+!oRf>Q-l zJ_cJan1EuVuHzr{4wX_54ouU*Mf?oX0F*5BbQngwaPKZoPfoDi?ZgiO;w!?Cblg^v ztMw^KxUvak-VFjQ3_nl?=a&#bQy98`i)Ma>hFHqnn}el1AL&4Q7OZFHLtyCI_OWYv z%wA72KZM}0%%=6Qy=y)y;F6}&T>#GZGTSu{eq)GV(O z8d*GApiGRja*plA&Y6~34JzBV`ik!ZsCwJ>x!LlKQCGYZ!JTx%USp+wOlCN0F-l~J zc1B{ph8dpp&lq9?PdYc&rwQ5fme_iGLa8~8adE!RvuP?|-O>&aW$bpW3Eu1s>GKx8 zjD%!Px~3Ut5s1xXIj1GI_STW=)T=vO$JZ@;X{MZ48VN*$CLEJC3Kh{Z=-C#)mLz&c zbMTl+rWiVefm>vMG&QWWV z=o`;~ozZQ;i3TOPMi~<{cHNw3QniWAGzsl;>(6FSwEefBvsS7yzEuxZ{KC^S7}B%D z6rc0vbfXY056E*~7W_J@t{pGmdN1m)=Cl_>-AGl_T$zS3O_4mvwHIO{AP2)~7^MsWN;662@l>Rf&rts8Ef;5>S-(?-dXszqio{lK7{ZPV7Kc z8$IcC6jacXeP&NiHb}I={g?0KJ3jCM3@PFD*I&gu-tjs<^5GBR-o3jhh4JDG_wdq7 z_kAwMam2~V2FCE289Vhv&sqBZ=+R?5dh{3o@Z`xEe)Bhf8*e^%8(;g{*Rh)>{NC?< z5pO?wEYee!dG$3laN+A9qBDU&T%|b6^$@i&a=M2GKrnHzsp~!>z--rk`y>6+NeQy@Gbl zK}4hja1Dn}i3<$o+$#H~pp@E|N*n4zkb;6Je2Qzf05#XP>@C&-pI!M>orW8_Px@_# zbSm!E2Wo^B*|>n_?1M!M@xlxDFpLR#%DA}Lxh7;A?v@erQK~fM2Ftv@%3V|Q_c&im zI~*~Of2~Vi%PI*C=3ilxB#%^~M^B?OY-TV-yOB%A+1Z(+*pdq}<)#ixy43d3kf(j7 z3mZ^ub&?^?BuDP3Wdw~~DpF9vW;eIEUt=UTqcxOuKDo@5;4UR+vew#G88Y*H$Y$Sv9kEUM?s z40CKFN*+aEnl8&-A;c}s?ypH6Sx~Zcu9&LaHzPMu9>th>fux4E1MVh>>Wm3yWY(KC z7a#(S7NQauBj|Mv47?H~VnF6cF3H+#M%=x3A1~d15#RmM595RHe;;0a@kM;scYPS& z@xk|F7zVuX!adwMJ;nX|FSjo-wB z2M_V~+Yj+ezx)}z`PSQb^yra05>_n*L9`u&REn4{h%im2wNR#^6kC{{UL_Amn(d+h zQR_|WFyJ?dY6z<25eQ61HX)q2SKd?UUjm5L(XVniKgO;}Rfao`NkInzBQWv?o6Sa2 zWM>0>$@dlmF#)5uZR0i5Pt{E692h8bumGfFrZ-d}&9n_lzjI*_51JS&G5{Tw(mcss zpz~S)8d@r6iMEpWqdIZs;yG8nFFZ>&=B$HMgsGU>zZ5?M-5+e{v4Dm_uk4W9O&5_! zRHG0rR|7z9vS=^HbIei;12hw8GuPW(O`9|qyae;;zBIE{GL3?s|3pp$jH$7Q^vXI} zUYv0=;tt)x+2eDhA!ECnU>+fbLk3W`5aG472L^%XXMO32?S{n;?H3wV&5+KcKl0&E zv8!F!JVw>#r7^~KVT~bwpP&H6E!`>+G~9*!pu~YC(%VNlrf8M=#$j9}D{1FKE=QFM zZoGDmd!CCjtUFQrVFVIp#L6!N#B`e!IH55}Mx$MtYhs)~sz@^kw!$E7owhjSgFp7qJRV{{1rh&1| zCgHW$Ud78V-Nz4n|M%jZ?|cV7@$rx0yFUCOy!6t2y!`UZR|X0hfz4ZW(9zDiy z{mvKguYdK|@Z~Rm6`%R+=kVrR5Afz&Z)3NcWS_^3x`AOv@FC!!YAF(>@Ogd4&U>uK zF2HMm=8PH1Pt}g4`fg=76#idnhm93XC=P5<#{&Uh?&QZNvCDJDDC)j94qFQ;9R-XL zTaqdEJr6RvrQj-&XLZe`fG5$|r6DATRY-fgZ1xo^dV5nHol+HTu?9hC zy+#^w2<)RP)5M$RArW&tFxJ@hed*yL>acwHKem`CPM7_xC%4MRNBy2 zBai}#y|a~&>fbaA4^#lNkn(M}=`#tIDp(&a+=MF{L8(_iSG*ZxwL29q)J@ zue|)yL2LA^3K8MeS6{)aufBr!zyH1Xu|M}`000jkKEl_&{tbNQv!BCn|IY8?7k=qi z@TD((1z-BoANn~aKr~ARJc%@cmgRZSn=UCioiH;85h3e(CrTd9Y8nfW1*=lfT7)xZ z{uRpqOd}a&Yx$i`+Yo4~GNa6lk|$-{0-^^^P-G^1A`zB`1Ra-1sg_l7UQDUtl7FC8 z5)FW6pCuXqO$BZea$&LGSwc+LM5u#mVv-Bm)R8l;B{)c_Hf=D#HlR=zr+Vg^#+}jJ zFCmV~$^8k$@m@hZ^jwr`%4*2H0>(6`L$K0)1Lwv9L@U4wJP0stXbxvJ)E%iS>7utx zTLcLmZu5?VF#~k=g71_W?Ka^S5Z@27gpZ_qg+n&dgws1G$R%UiZR=G44PUZ89M6=@ zqX^Th| ztu2`;sZSKSoA4Y>a`?==f9o?908yzZyu`QV+=zck7)s;OyLMT{70W(Qv4m;YY|A%) zNHLqU;pQ7{Yf)0&clGLKS!dID0Va_Nf!*!4c<|tDO))C~K(aNa3Or#_sc20ok)~M= z(ij6J6<_z!!uv)|P(=wmW$;uT9p}zucBt#+NNQ^?Ey!q`FJ~acKMw#pnmz8^+2A`r z^g(>|yFQFhfBJ{;y&wM=-uvy}hL@jn0C8x!fB!|?zyBh>^Ftp506cp17=Q4kFXMB+ z_Urhy&;JH~;TL`xzx7*Rz}fkQO2EQE602#;XjWne5U!nonMD*3gtH$3hXV_e@KY&9 zZ^$)5@}m77frSYq?Rl~Nn}{%OHmY^Vnz_`>v$a@iWYsw!vq`6IalbH5>R?)6UPMT*$>q$V#jry2xjPV9`CLKh*cCz(jR(`_j^u&NTM6zaV;XU?Il;wn0me?Fb7DQT*ei#YoAA460EJaI z41;I<6MNTy%|<@kO_L;3(dUWNtRr?<=e33>6iC;L96?IxlSZx>!fgsKe(8OABSj8qWv0$#l+hlFAc2~?e08H6qSdc*&aRG4 zL$&YHk?kH*gcd6xOyR;W0Bp&kv`IVSCI#(wI{?7A8NdY3<|;W;pg<)dneb8+_^L!} zGQat1BqFd$X$2@MDU>WTZuuZ21+VUrVj2n6BDiJ&XhB2NMpRQsjlnk%QMDDrIN+0? z_!xfVPyP`8^q>A=eB{F)!t1ZS>L#ggG`kP0#y5hCxi`mC|HK_HZ* zV91hA0lx19I7PLf0VV;!ITsJnte5IBoJsm?y8qpOl@(Mhrp5Pkq*-2-5L9(gtzoEu zom=Gtu;&P#)l?D+IUQeW`@~r_1yecexWERWbWRF^vj`C$?P7DRG>?hgWbeFXt&q@| zo(h(m{~1JN^}VSRSHij?%$I3qxMaZY{#C7$+|oK?QhzP!i`LI!zHYmi;1f!pI+^Bx z2&5*D&1S@A95Lh(I6ud3H|e8wGEAx}s>$e=sybX~CycSNKN+5AlUh;y7j13J5r;8R zv+>$qLDFaMQP0mem+Wz6xA8Pxv-I{vMeLo998@lUsr3yI5a*7uHw-2Kpr$T}z>$=2 z_xoqh40_9_C2n#l!qf2z;S)zay-9R18@N8h67=)XV%B3Uk#!gln3s8E|g) zi2q6m`>X)M=Q;q2yLa#41MmNK{J9_f5&Y<%`4Rl!r@jw&@7}rTS^uUh?|jGWc;`D_ z$B%sahw!)lH~$Vk_iMk7fBAF2gn#@`{yBc*w|*Dr=jU*TN=gnYjE=eECPTinEQd+1 zI2SabKT{HrV45>e3r>(LYGtiTc%_%b&p&u+T`4A(pY0XIeC}gg3E|MWqwFWhU(o}^i2?s$% z`ebLe`ZrXwpaGtOqGn6iXlV6;t;(mV`Jkcu?hj1@#_aA5(-2uSVS-qQ9qd3D4`l#3 zD;+3)whPPctvqn;(2Nj8-~)ze#n9p3D-3o+_?JO!Fy?bbY$W+UxsQc-9 z>G|j!RP+oqAARJ*pK?SX-pk$!0C9R5hY=^IC*q-;3ob6sF->`)0!qoNa;7K?Ut^^! z&oyKpqyto^C0A&%)D2V>w(%f;UJr_)O`uA$E@2OYJ2BZ|^~0#|jKhcm#%`LF=-1Iz zEehAoq)iCo7V*|np!$2*@04i?z*;=gK5JoH0=kBy)_!q~xSNo|Gf>Ii5wo@%x?$tL zq%}_!?4OkRXkJ3 z^r`Q|>#x6d*+l|rqGiV*6=Aq`cu;1+>D7zWc zt`w&fv=$&lEe6scmtGNplkOj?;gDd23+7Zf84~WE-ow2YUO-9-j~_qA!-tOmuN#e4 z*0f;EMN+!kH3TbX<HKPg1%#m#v8^ZLD&Z_$G|QQ1h{XtHd3{9W45!)W5A3B7}?Q zDxD{){fIe>A}zFhAO^hc8K>pkYLoypeh+qIr@FZ~ppd{m1A=mnldgM$=1?Lzi_;`w z(B?ZOQDg}Nnc1w)VN9CGhW2a?El&u!3(9KYFugvmQbHYK8sm~>s;MR)E6g~5a)!qb z9|R$=(k=muhkNeJTo<5|Z|2z201E3@3k>yY=W+CfmN@iWnV|0Eh>0UhFlG@M1>0h4 z8|IiIkJcFRh*}r6E!{x2A%QBP!Z)tk&O&Ns))N}9P-h0lQ9#F(H33cF@~&!Q11}X~ z8!<8>VPn!+Z4iB&*SWt){4TD-8lYJ9rwAX}9N0WXm1M6kdjUoLt4rU609#!0tUJM} z%M(qJ+N{-6Ken}Qhpc8XzYdxxD6p#b0?_+9(gx#3@4*9m$lkaa<{@FkOS^V^W|1a_LzxXS7^5iUl2=cRIAR?tf z^Nv*ioeDxUl$h(85vM@;C(?7$=px;77VpU+g*!2r6H*#*vN-`I!nta9y&rl$oy7-0 zk=1E%Mh>b#5a?q#Jyml{(Z`FdORm)wAx-BX8S`f`mspZ7QD!I&t!-GVb;}y_;M!*u z0tq3aLZ3FXhn))>Vl-*^sEF$K8c|};b}#Z`HEF;=VXZBt0mKsRB01AovX$z2HTq%Z znbz!10Z^D`vGSY9FM21LP&Kcv6EIM#bf^TnHcc7eta+d0S;<_TaB+T)w;#MEw8 zjZT*^hx1a+Y5s|Y<6cEeF&|?E6#BsoHTlv;36iSQS<;D&3+JPQs32R-fH2-DwKqSB z{4kW+q|BW-5f?ll{AMteWcn=gou#Q|_K+^1hOnCdNL(nJhk8?_Av$ZOhM)m(=4^~l zZ1l_UhbBU#f=orc?0-zt2T9vHJ{43v<91&QdW65nQXf?+f2{RpH)Sm({|s8OAAv8W0K z1fq?Q40H}695m#r$(Vr(m!OIy{P8>$4BWr}0{+~O{we(1fBoOW_kHr?c~yA8(* zqVk{#!vNr=`!C}D{rlijaC&-*)6-LY=tCdCcYMbOap%q{KJh&t!-v1~J8<{z9lZ0M z@3__RK7RZJzw()1#eeg6e-i)vU;IlveDoM|4MlYx*S`%+q$QxJ8Vh(mpoD}Z`4~t* zMoRGaW!@T84}JiXx~fxkgISLL*t5b3X`AkFNZq=*`!v1&OKT#fe($ z2?j_(cy;c>acLUIft1 zrgNV9h@z~ZSpyXFbuxnDz!3ysXw<{8%dOKKb;bikM<`A~K+rWGhXLcbQKGN*$Rdtm zr*}>;q=fVH3nlQPwaBXaESeySK#+^0ifSFO@S#`$4Ugyu@$Fpj$}2D9C;sw}L%w&d$#o z+}f0-sJp*#U@Y@`!oCLsckbT73oqQm3-|8gdq43#_`v($=i#oeX&C9v&qv0u{@SnO zAN_y-8Gh=Y{tx)V@BTi_q%j8+6;!ll5hS@yNNz}`3Pz9`aKc2O!Au}g$WeNRq-VEi zQY)pM3(OtOtm42YK0?9g%Y_Nq{|R;*gts=myCy%h#DpxsH)=d+s83rIq0N

hu}!RISk25%8Z}Ljl0O>#hE)01Bpx@V);DdSK;n8)Kl9a59c^HbTya1)z{WP#s#T zIu%#~iaGJE=7BXp0kBKQ>~m}Xj8H^PfcZx(%MwudT&hab##A`_?^gvB0J@qJtJkii zwghBBQ`i){GiKQkhfXsxS&|^aIE+FJESwGkIIN9v7;*1~J4k87lShxmA1G(!X@@CK z;KGmB$jf9WsafAKf|I==6dpXkoX zRm)eu`Zav%%YTSp_{D#P-~OH7#V`NLXYl*K|0O(l`=Kg-bU#ovX%RJw1}&{PItOrl zhv(*jJ_)nWxo$Gi63INh{PIh9{k2!|sqgm&7J-gI*Fb->6aY>Sg5a|#Ma&_t z(bJFp8gbK5n1&5UPK_x zSpi8Rm6GK62)FQ#qX;6#200L?$d|5C2&Hk}Hw}ek_)I7{W4qmQ1VmdSC0crK;catP$Hn{kI9G}yGDYUu^C2V^N8`&fHD3$57!11OrZ}3z$&1K zH{O}a$Nr(KF&U&)^DaN zW6bG@LCF)uBWH220w-)vHaOj!;9c)}C*J+;cVXOYaC&-*yLV18j3Y|PIDc}E^Rp*7 zJ3q(8_5$1O1*W{i_F}@t`2}{nEwYA5m%Nkjwv(6yrXt{rMU`91-eib z@t5&8|LwnqANs)`=q<{X%IAOmH}FgU>a+OFXZ|&Q_18X+-~HX+#~W|HHJdlX>1snx z6;Skxt<{Q+t6S#lZ|4EwCj)F=@qlS8|t zLj=NLRSy(y+*IIEEUcBBev)Zatgi%Y#*LfB0i)fOeq9$(V8#=Rd;v4JUz}gy&fPos z*mr*vr*}@Vy*S4}gvXDc;LBhB3f_F{EdaoFyT#jYzKJ|#w4oOJz`}C`DDq4Sk@VNH z(MCo4b^~b+iLijcj6vqw-!Pgq1gAW%w(%(W%)!hdWgnm@CQTwRd_A-&GHMs{s`|Ys z6R|@tv}ADNEw5b;C`6{wHXI0`)9(ym7SLfEh zbEcyHJmK0E!!!!eyVbCN}j*LpbU*Vdy7E2L6BlB7#Y0Z`cak~?OAG*HduqWwi-$(fNENcFNX*KYtMNNKd;YO*@1$07{b1XtURWEz)xJp)1LhTT+adoqoZ#LLVz z7J6#h4eL)klWpPf%pt{+Q~`F{;P=&7DD8H;#RuO1KD_sR@5ML1{tY~M>rLFddk3$) z@-ogZw)o-~zlgWreu&*}i-&JLz%)%W|Ffs5q38&ehX4u=>Mf9_1SMtgg)t5THYX=Y z0Jggw@|3GaPtmPpDkKmIpfC*wGke&QUq2hP6>-5VKKIHR3Iz-TW{Tag;Gau>izZ(& z`<-oo3Kjsk|AYd1M3cfWEbp^eEFuHUsYyI#Hy!F5^-*SQHXGc%dk3We7Z=-_e2N0Y z4cw&VW8y?sn6Wt-aqr$8BpUGW(Iafnw-|>(AVLW3;)I1XQM#*-1RzxpEH^PYF%eeZi8zVkyL!h7HQ zUcCJBOE^6}1!u-?cY)n*hrHWiH%*xI9{~S-%9Cg#cDwr7ZYODO^>6upLY^{m(a&-g zDFMJIKK?QM_>ccZeD_Dct8c0~u#{5pr7wL6zx>Oe!B79p&*IlU_iymV8*eD+toJ}L zQ#?CoAru@>s`YitTfB9eI zr+?=EhqJQ_2MQ@A4CCmU3fD|Dks+$NKmrZPBLi}oK$LLr?p&@S zmc^`5G?`Tz2tyK$=#+ENg6P>86ObkJ9HjtBgqf<hLApEc^iQ0FE`kwdR(EUiA}6pQ46t@y|vU5IGupdZo9&slG5#X z8I{dpL5;jXjmmO?OL-NAzA#j?u91t-c8l&|10!w3N#zk&Es|6(YM~%>Qgd_cb~}{9 z_^yw97_YzfD!%v!U&32&zKJ`hCwTq!*FZ%0@|V7hH{W^-=NA`v{Px>Tl#86j;%BU1 z*v;C#?~X6+d0iy7>Q9;w41h2s5|3JKK10-4$?`npjEry>JKLVD;ZMwrOf^Ep+O60P zt{G_OzSUY_{@uP3k$*$T^uuEHB;R-qbg}!)8<11A7A64@k*o7v17ht2U}L{ja9)#{ zxN5T*lH~MfwlgRwEX@4_kRF? z;m7_wKK&8HHJbJko#{@4txGUVRlOrzd#$@G<`I;v6|o7zbe}`5o@B2q2uNo>IVCWLN-!-M%V7>M9qCEz3efv0mh{AtXWZxY=RA zox69CDB`KS;*-)*DX({a8_7M(0 z$q{&D)X|xZQfd~PCO(kQy-J%pB z#xLK00pI@K_uxYx`XD~?(GTN0KKKFLzyE?uAzZR+7;?@i0Z3#847mXi*|ie#ojki! zE!(926ZX8NCC?fW^1=%*;KzRK$MBi2*D_wlQr`y77m=YAf){oB9e zK+i5}GZTbej%L!asN|s`Zc%L?Ocz9&1u=jPA{crkve_5pZw}0!v;-AM){FT3iPk!&mue=7|_{JOf|NPyb#Q*ER`Fr@?-~A%Qlrdl&N1ykUhQ_Bt zCUC%J+-N4&0WZDuGG2T2Rk=_xiO}@5uYDblA3nk6^aPycevU*VPEH2gy>}NW4S4eS zv6wYr=TS|PB_pK)BaI;QCGiAo2ELRLmV(R~pyFZhR$-;Z)I_gIecW2Mm?T1q^g47B zHF<(+ILyg0sOpUvwIr4+y=IeLgss|rI!!_>lXg%Y`E$vli6UsqaEZ!Dq$Uf!xRN%dAH3x`Zz^xwZ_aSdl3n%A5`KI2#8xx&=iz`tUr<7q z3^hX5$u5#PBa1>-X{+5v#Veoq>= zE#CTbJ25%qvXabiA^?M>-@xU7q5_(TB&+(-)tjsb&91yX7*LEDXarM1-c@yFbUvbi zT$*&pQ#nzce)miDJ0VXB69EN7Pdx$ufA;=6-m>dD4+Os*&bjxcia?=&Dim@iKqL~( z1ehgeQY0PQ)9#jSNo`B5w$?b-b6=<`5LB>r@GHFcZa87bZ+&a6Z-Gn#6x_CKwQ25r z*LEO7SX`RN?(@#Y-V1i)>Z`86l~-PY^UgaLw(>yi5E0NeQ%svF+NMPa9U>7z+krx0 zrlM=nbr~cC0UfQlHJUcNI2e$$XxmOw$~s9WWgwxO;I-FYhadm3AH!8wU46#wQyxTw z0|yS^8{hl}zW2TFN1?BD%a42JU)CbRCgXf9PALm@LP+JLP2Z5@X_HVUsFC*P6u~GUugd_Y zi)ceDeI3&4=bn2GZh!Ne@a`XfC$78h+Fm*CvpF7m_!0ceU;TCb{^$P?ZA&SR!f2Zt zn`6ERfPfBHRj7T9?b~-?>$a`v+E!vQEDj$&jMec3mG@Aw1ZypZg8=~XKn}l#b90~= zu)MMgBA~83V7Ryj-uy{~J#bUwmb zP;dr!+XEEkW?M28dQbfKtgN_BhH{ZIDRq#CrcI!vfBn+|3IWZ?fFMP0BctaWnVhYo zzD~x*fWkSJpw=ve*oi2aW4h4E=0rrGAi-hSSlDX4LnokKzc2YM&j|($5VWsW9TOuF zF>uP%wyj%n$;B7pl1nebrI%fTi}vlo&YjyaKQ{+Lz;w#4j}ThKz!NZtD}&2KN5n25 zhRjT8#zxoXKuV3LXqvVF6By0;jN{m`>zKQE4MrAM95Qz%t^_-NA3I!z^_?=mj zAfF*DEH2`f8*jwxUiTWj@`jhD_tAA7{_qQ5#;1P!vv~TMXPL2zTHJ~OMUWIfA}heI zg7Y4OdJgmR^SnvLh{<$}Q>T_$tVsGv@7r1n?<+)#n2aZYF{mpK=PDTElJ%cTd`qD& zWtG(JWt?yn6ydyscRsau+N7gNU^VvzN!){`Q}X8w_?#5EkBwwagxDd(Ac3VZvo^+v zD=a1u0hH@OHk9%@XISn&t*L!*F&l@b#KQLN+pxH}h?SKU96fqGft0R`GR}GbDaUn) zfm78nvu275(ZJ+j(iu>pFSE3?*2YV5}S7R$IAdH$|zof?J2tHgwfyi zC!;h2l|zk0$->fsM#NFkOG}HWe1)cIvivVL0TVLm*1l{6==7<0TIYOBy=gFG&SHqO z15l}A0Rb{MnUI zN*M9l@#U|48TZ_M7gknR3y_dA2NX;w zt5Ckj@9W?FdK3(XZ3?hUR<*qEl!Z|DNjcsWtU}g4lgAJxnpO-4XiSEHPMu3Ct4tcC z2~Z3Ofzk#Ri9iApC{e8?K1q zcHKbf(;d$}{{p^z=T~v>efI+}yjlAS&Ux7*42&}XIavcZmcj~vZ`(;?69Ay=*yS^s zOfYR)xk>~@BUw8Rb8~a3stVpa)O9Vuwnc~rUDL8oL5x@(jd9|{Nvy1{qLU1xrfC3c zQF#w-m*weK(0!z+{F5Xs1b=O|I&fM7iXxkQd z-F-K{{hjaN?796NdlM~)uF(PPIj8jTP`fU^#5$1KWt zGRAm3McV|nXgh98-H5s_pbZ`ONyokqBL%bw#B(f0IAK0iha%mJFwNe1I9s8rozz8Q z@Z!B=wE!7YR)DR#iminNItP%H>Q<&r`_nm>GV82!kSK-B2#ckzrN@mh#yZ9*1UMBf zTgM>d=R%4zQ~7SIX~P20lM7zvMG9<}BdvpkR3bgaXar(BW3qa)u{NvoF3Q*sPU)CuJF@nIx*jY^vng8WsF2DSy-fL^?K;%Pf z6wGcuOkHLg(^!HfEA=|WS~U)(9(|cE>6}ZbxUMUqo%CyzfKT;Lr@vDcN=(6>F=XXf-3&GBCKc3*S)h&=TORLQw`{>{Zo3U{d+Xa? z62tV&Gtc1jfB1Pkao_-)_Y5NH8dX)Ju50+JhW8Z)^#D~>qpE9El}`Y~c_-iVyvbUZ z(@l+$fI=tWPK*aD@fuQXISJnx1LrGLRRz}m$-%D-f?HVQxX5AGA;cC<6Qu5gNm?sy z%h^lQW}0N^r{gKwmSZHwqY2us!(=?c>SzVi3C|@MLK`|X6K3|BHl&ot7@5He9anAU z;nfe!HI{XCC~^%B>l~{Viy>;AMO9U4Bd#jeqiEY!1n8J$RAxnsGuAb;N02$-eTB+< zR8rF7!GeQ)t?j3}2g!KgU}Tj5XDy4Wt|ds6@@6i5qT@aVzk2y57Xmuq8EoVT%8;Y_|>7f4BpLPRj zV?yW|$!B`Z2KbuGueh1YK*#kEe9!k;_(JvbjGPR#gt)$zNgB28nQl_b3y_YGOFEf+ z3R>)%y!4llcP`XhL87IIAHR zSuFh^C0?KvaH&i;t;5vJvIBZMde>$H(3*~envd!OVI|VplCaXso2Cra20EBl=z^#5q-5|CA9SR;?5yWYFF&B&v#MfQ`3hAB%xRstp z8r{?zr#cQa3SZ}n4p&{wW-F>fm>dR2g(Q=isYK);A|il!dD*t{owkuL^2$Agu-*mVu7~*xB6A?)k#s(1nIs z9$u8h5=CMOIX1(?8bWes$$QTF#FnwVH-U)LShfE{;JL*kJZ%I-TO=$&;8&8pKEpn8pnVTvMiPJG3#vl7+Vl=p@Jz?3y9N?&Ymp zw_;&o0V~TZ7>&j}$DB=D7|D18SWZFXTeSvAmIdcjR}CcfuHyOZ4N}b}NP$Y5vtD}c z2!gl``LmPp1f!Le#PlVXI&n4$(BG#|cW_)tKlwqA%~-v)fa0oK2yy+)iSpzcuo8Ql zULlwgiwuYjPR5qJexRVo6c ztuj0^A`rEvqYOZt6to6Y5O{zRC{_7Wmk;9%k-|2GnjA8qFvh@pmuSy31r+`FQ?$qC zM-l1$k^qIt&#(S_=N&5N;Jk&Uh@(diVQF!QpZckv#CzWJ4veR(XqqvMXibn*S*0lB zEk?0=m;vLBC{wnYxzUBtXB|3@h;Bv9s-3n;&9d#eH$=dy>yoQuer_Ibd;2@_mOI{Z zR=N@~M*QFh599ZK@AvTF!w)9l;A{p0J(fa2g8&FuxI0qC!^BFIAR_~Z1Uf8(RdsRBX!5jE3BUMs+ao<~(=LjfvuQ7!`&ue$OIyyxBT!L7I4dRBp(M1=2t@4NWJFMa{b z%ge%02wPECs4M>6*A;4IFT9r;5yDhR%5POwFfP}eFveg|*Fx!Y=93x{*0|EFOzr>$ zgUZf?(6K8}n4$~}c^*Ogy_A=h=gO{z9D_qr;LKTe-=s8A65uN9UUCgs2b&B(3ECI8 zfYTyafdiklnscNmAYanCn=XO=m6cVTI=O;+P~+TlcVg!`=VE?tAR>R90Yn6JZHMt_ zit%KG@py{yc!J4fiq+LsJah0_Jonu5SYBSi^6Dy9MdL*JV+D-SHIGu9 z6j9!T6`+qtBdo5h0K_cH4+|)G6olTNW&F>E-s#N%XgtaQLfjb&5ZEFTN#+u0=z9t% zyf0!U^6PoQLW)naE(3}jMKR)-l&*I>7&%%4hjD&!RxxtXZg}6PGt*ELBuByZh zqAn2886d!V(X^<1Vw7}qch+at$>jO0tWgx#uvW|MD=21m7AV#Vp^?i#*5vP(jWRPB zZ1`E9Q<|;BBPd|iU~#KLQ-*aDDQcQAkF}(MpkptoN9N~+xOqYs(X~RTzfDv&q%^{<)1L>laJZee9a;yLCU zAsxW7G3%8SjDQEbD;B+ccbQ?(Mel5bs&oQI^e0Z4il^3Lj0E+3b)ikcVQGPl;%8px z8XEx$_Iar#(FYWk403e3R;$eZZi*71pMNpYV>Qo-?43cnoJ#ez`F@t^y=r-CAfolg>$-tW*XI1-+E!rB$hHS@1z6 z+|a(F1dMflN0!}0M9B25^l1Q(cV-N^uv#w&Q&|UfX%sOP%1ZHKp8t*%Qf5w&GS{61 zTPHvwOb4YPQ66u*cJ0Ck-~R!;@r`dfD@N+rv19nZe(!g2@BR0pt}4y~sTep^3KlAL zfm94Gyod7@eC1F%pEN80N-kgLywtIAQd zGE3rolGlQFRZss&gX7cV6(AaxvpuwuaEzSxO2SYmKrWdFWqr7A6UTk1ltB>flve8b zj8?}OjYnwP1}9IR!gSK2?Lq<|FTC&qtTDLy$}92Q^M|m1{}VWV{5WC^68m95U_dgc z>lAkwiL(cFo`s<29APpZB^O^*F9Xrb11M(SCH>4SqUe9lYs;%M!cd7A)7aupN`p+S z9)bjM8pM#(f>M4oj~^MRc@l8okx7HtK{G?vnD37<=d^VQxWrT`k38V%7Y{tYN>rp;EKUs)@qne|^ zsgujN@iHWBiBN(G(U@qO z25rNxjxtTO$4rQujT*maAdZk%>L-7jjE^>WbroEqzb)G<11w0Rw@;V#^$b4L7>!YGoTTG)MhDO zqr%-Mn*+1olxSIi*$8KVXbrpsXREuED8dnVA+%Qf{T9*Xc$qTbvJAlNI*#HGUIGw3 z7s?A&6kU@J!DTg5Cvi2_8pL2=ZA1*pR2r7Ej)bE}j^J1S)vw}#2Oq#s{KQAF=YlCLVvP`RalGV)iZCjtP3#h4%R2y_ z0W4GcwXYNQQ;-HgvcEV+D|JpmLX@>xoaGHli*0GC7IkkxvwWQy*c5*Z&N%w|{QbD9n7 zZ>FY&x=o_=MFw1otVt~iMe)TT-T3+bq#U{eu;mL?))Qbc7}j|68()u0F5QRK<&%;z z5V$T3Y1WBS;hC+VD3tG$#G@Ny7TomfUaOzh!2Hxc?4Wj^~$mB?Apb938rjiyFEsO7S z5S1+8;PTD-yS3Rm)3#NRp|2IhA&ikeNMKvLl@jwt2$`z3c>xpb#~W3Q9Fss#N%Q%^mGkA3uGc-5<3y~h2z-Z4KnkN3Xkeb~NzJHGa{ zuVFeFBU+B;Cl?bNl2Q$01d+H~WkaaI0fOaNaMtq%m*QQ(29Gks$;xA*U=8Bq(aZIqqL;oSc(D2(n&_V4xaiF+jP6%mOhf-_RJnA!M{*Ifa< z#z+8uGCH~I%JGoP2B@LTEWHZPsq4lXz*bmb{?pknc=fo!ZK7LrP5You)Yc1FoUZ6$R~Ik!^2DhHiVZbGv% zfoAUMj2ViwVCD&C1G)-bA+NMofCK{w! zf{D5LdA#@C@5Q@+{5@w(b&p0PeE##lk57I2lQ??hNb-eib_NID`2;d5rNHYN-m#xu znG5fIqHV3o!Iivl+1$ScRcv(P7j8!e4+`e;_gH^|TdUtT_i`|iIVD=RCwhH*MVyfJqST7Ck*k5AZYp&bsLOHMG?+4^R-3%xW2v5`bkSeg>3+dljS@A{azA^e6%`z!WOQd9}=i*GzI@ z>v%%?3fPf$!%z=w}f@CyaAiuM* z%jMa6&0d^0pI_#&D2Q=@V3+*&x-MAch-`g6PBqd&b>@c(tfV%XxgWkK)4LX>ML1LI zM$(i{2P)QUoHJ_8(*O$j2vih}1Cs)pu7$Bc?MfU^QF3ZRtYqpZpICXAVGy!x!RxNl_Z z18v*jkw+fE(@#H*^Dj96j0?Y=zx#Y_-@YABKm9bu<58ZIlpa^EV5F_(9`#;qf6Y_o zk^zTU&eoxANz_B5^`68E=Ve7Ue9Af`Iq14Y+fI{cUFh^RF)rGv6#Q!? z<{(HABZ<+b7*34j1hg~Y=DreOW79+f0}Tyc&2xwWF8DoH#0H9i1w*1Z+oiQ@X=xEx zUvo9CzUFF-#}hpL^fOplU4@mT0TTIl=RK&XD`blP&JbC-j5Gb$|4VmrYi-J!v(}~5 zZyn3};5vuAQkVJA&D9`lKx78P801tDA`l`%0ux58oVAm!?~8>5k+Cngu07hW#(q}fYHhc7>a17Ty!W#C0u!OhCn@0BMq|aTG@~# zgQIQR9$;uRRzg^bwkhH!3Tr5YfNfj1<0C)uQM~HbSDo?mJ@CN&_~fTPiRYd>$ds++ zLQBRN+2k3?SJeQO_c(FlBn}@wj8mtU(KHQ0;KDx~d2TTn4kY+WBIBLI;=&^4=H^7` zjc1c)2x!`2J*ZI&>oFXNLZkO0q~th-Q33=FAnukdp2g|8DmJIl{}DN(sh?uWqcNwn z!RYw(X#;oG!b@6XoRQk*xCn{%Pt-L5R_IzHM8j?^PNM^y+y{)7pLVbmVIyFTM+_`R z6kP;~AYIqB=nMlNXDw`J5Ui1nn7}a#8c^sE3=ji9PLy)qIvJHEyZ63(aOB8geDtG# z4!7NQ+Zm0M>#lnR>bk~fKl?j4dGcg>W}W8cvdv0}<+_+z9yZtrFr4Y+oGkg`9yG>q z6Pdh$=(+Nipcmd`k@#po|l^_t@(a=Q2Kv%Hz&ZRf_VyX4br$Xa0!Gu z>$7Zj!9qX+*NG(NEg3RY>{m!xJ?ies!c$_*B-e5VF~(ZSyt4^F^CN1D=I2&dNJ0E0 zAgAq8wi`1PMn2EMrj?U5-Z1zdjaJciEiS%jAO6Z;{tGz&{5|;mAN(PX96g5l`6YPo z5JFDxEnEEM+?uSB;Qw^e(i0)>Fe5dDka>Lo@ZLJ)%#Ol}K?VUtNvi{@hG&TzS~EI` z&M*U`uw+7rXr@e6r^QRht5H8%1~{6Q^urwLekpHAZ=0*>ne;oSt|`_0()a4M*W~MB z&A`z4Kbt;VPG;GDiBgK5JVkH7|8X4LuUcxFQ$|DEVEdMBxb)(S;0#x#HPiwW5IX}n znVeuBB+DpkL~|}_-6adZOF$tp6AcSjY3xLDd30IiO4*5>JI}$#KKe1-bmPru^h_ZH zeC=y@;&Z?IIh;IsGAUL%pW_?2a1x=ehwxQ}{g3U(qmMj_$z;mR0hv^zqH6;$RJzgF zyrn1|uZs~&8=Dl+=`34&^UoS91XJeCN2SEXbAoyObAJj+N5bBMIOlg7>mcL{Xfj2#&911}b(P z5EVc(XrahfatwgA{C6bF?m^4Jo;GrzTC`v>AaS`F5s_1hmPpcR1z1=En2u*uG)RH% z4v-B!A_SSU6!F}1&*Go`v;Q}qdFC0s?|tt(BiGm!S6qn?eBi_Q?cez{jvqhH`zUEp zs{AaZ$t$Wdm@o!j?lBPY1A1Oxoy+WKseG!&oG$!n8#t5oSU+2=gZagczP~O<31vt7 z%WGc8sQj&XoiD@VJ~J)6qdOIyk}=jlkutKnI#c?rPbM_MNE&Mf$U1H{;9u{L^9Qjozl8U__gy$|*DieW z)1SdpPd|&fxp@F4r=o%la%OCb66VEoZhFkH+37juJl=bZMk8Ey`DK`&pU0z*K8Dx@ z_`!gM*o;|YX!}gL@(-1FO$dx%?KOHj$4qp00nX&Rg0g20rK}7Qg0-m_iuyUrYiCUh zLXxjOlcN|A9a|-I5eZ9 zpV@IbFtL66cKrE2_vi7-8*V(K#~F{u_}uS)7GL_(7dR-;yX+@U*%<7$sGY~$+&q5p z@FTeQ-uuxuEe3-*v~9p-JOu%%d^HjKB2!ek(D_L8t4Y8`tq&~&-82zRJB2Y*96zpX z0^hFzlCN6neN8`eLQBsLhZqb8qS)wRot5X(R8(M4SMXJp(m{uFLk#Kx=H}+HusEOO zrRV1tM08C9>X`m!k*AueaqlGlLKf2$em0V=99K^?jV z2$EG`O~vO6fN6QIb%C>tx-PpmS}QJ@&Y+X~G3b)@AJipcD7kT)ronIh)^FnE$>aEQ zf9@}wQ9YMSFTE5W{?JG8Tc7+*oIG(t;s-SNI8B(u#A()m7P)iY!xOVWUN$)&D~=g4 zEML#}X(bhuM}SIO^Lu?OmS3B_Q?oINvCfjBF$Db=d7bAf=-Gl^leQ>puG!$+Fe>h+ zIw|@7HcWw>*%H*S>buHCsL)8zC6NTX@;R;E8w`xJwXU^gneSd}Muis^I;2sq!ZMf3 z`%+h3dU%qUF&K9LT3#bU2du6xW9!y!c-?DWg%Be?{h8mv!DnB<{QP`c7wf7*u$Gle z(G}}};b&Z@&3eyJ#Gv$=loTgWTDPjoL(9YSoIyX+MFy7F#?vvPCn^z+FrDs46#^PC)C)& z;yfzv5CW^;IV;niH1eETuSo3gXMF*2cXR=v&A^~-y5!EtKNVc4GG+lL+7?|10w(_A z8H0%vCywLOpZOHN`OU90a1dPyRXbGGOAWwWT-<_#2cN?O4?Kjfi?FW7>S)T=QdOtV z0Y#A>;>|Nk%AjrtAVQ!BYtsxw0v5zpyRSR|2kRBAT1oj%Jpt8iu~y=B3^1Oy7*AWV zE-W;7tP36cn5h8PRtve=r0%H9jHbr|h2pG14or0p-m^YN(8m!nPKj z2?!>@m=4B-v}Q507QuW4r$~~z9*FSy&;JJ;I&=ts<*)qTaKRa*mR@wxMfiyie-xkm z)F*KA#Bl&Q0NDsz!vT90NP_nw)u`0c2B%08Y21W?CkCRCATbdUQL;vq(KV9Nmw`qx zUuSQ!@VzVOOP{2EDnH5SwdHpxzeB}2DKDw=<k-&Oi%%+@Y}U^UIKZv@XAy73S`{zIcy9Vq|(6sN>m1WE= zEaKK%Z^URa!oUCbzlWnoPoN&u=u%`egC#DW#{FYRGQif`ie&Fi0*c6cV|iN&;;O2` zGtV3Z4A3+jH`WDa7M=Gn`dkVqtaU8c8kN4tEy3XxQVvKu$7lqi;km~Mi}FB8z(Xk= z?dv|0(L7}s&B>PFF@qGSw5I>c7ScgMd2pAFlG9a@Ea&oAv(f697K<;>tJx_9%-$T4 zEIcRxS%R&mSm^U-_=_S20YGC&q|4NY9Z0k{QtrOG8u&9& z+PxF`Vgud=C4m%N7saw48oFj`njW7IL=wC1;Jm|NILF{T7MVrJmJG#!t^+y~SxBlK zqwUzbw*sbPS_9G`<#-1QXd4AA@*ZNM?^&C}CjjIthg#xM$RO9nkRZUpu*PsO#N6Bf zmGebB3u^@g3P6;!bW-chq&^5;hb{?S0i2-(fp?OznKlzhC zi7T$S^0Z^=;)^fF2S4y3eDagOfs?0BAjAq?sL+N6gPP-L2mur;5CsrA00RIO3<3ZL zLlzYI`Jw?V2^$arjagHRIE6N_hxTC+dmquXj8!C4*Xlml=*jGxIUdP(*ZQ?nVSr5Y-4u8@l+>_Vi;@V}w zWt<2_?G=EL-VCICku1x5nw zQX508(&RdE9LQD-79E(9=~G;1=JyR6c4P2gtVV0VTpq~j#7ck3e@$p{&io=Syl`(S z-en{znxy7~f{BpoFR%zro6JTY}taJ`0z(?^G&yG z^pw5hnP;BCzy3GBih~EA7Hhl8niAfl^1S)Fq{F&sRtjP1Xv&8BbUmqyoUyCWtc0}t6}C(OU-L#P=HhDG*+f* zI~aKunskU^EZ4U^p(_RLF-8GNvZh$6)@cXCb!uD=VjX48#r=1n&)O zAQ6L#peBH`%RK5>!FBBV^1fyk!+B^$N@pFSYB2ywz{(gF(;}xVF|RXHDh+8(6d+=x zeFzYNIf#g%jFrUX%BGTbEyKW#8!fHRB@Le4)v`q>c%ecvJFS6~HeOhh#J>!Tn$bOQ zQM!}mOvO*>OW6a`Jfqp&AfF*Eb4BobdGxo9tSXsaCM&i!U$cL&vsX*EDELX3E5lh; ztS4(hfm78N7MJjvSHB8RJ^2*&KYoC}WQDDh`5_I54A_ec>#eZWA*5SQ_BIrG{uFyP zNf9`8@e+7klvza3DzU`cA3k(l@)Kz=UHV-PP32^G;Sdp0fKhfGL;(=}j?#zfZKVZN zO9L+IVn!QkJ7|43M23$w;>prIS(KcuqbXa>krDQIARUZ>@b-*ul(yr$ZmKS z$Q$uG@0OYqQfyO0NE8W|&1CGxH8y_ugAmGl?6EevjnY^U#P5wssbL|ste^-k4~$~` zL=r1d>oIg)Qo!umj)kcjjgx3lrh>Lo$fuOw)_>MoeDDJw!mDn5^%*_oA8t4%SyNhABcqgflk9h-83jL}x*K zx~3TCgk%k{uuP8YF-dQN6PYam&gAd&SmDKnpbLO#nM9d)lE$4=+>ikUSvX6Htz+xa zNq#$TE5=6AvoNBYU;yI*DfeuQW2-0dW?9$s(7;TYDdMpc3oA+iD@j{r3Ryt4*5M&} zgqX{nL+c+{7I^Bq4korK9a$@tjz&|2ZnY2yiUJaN9nk8hDK15VIK7pODoOTo*5JV7 zPvY^%pTOV!yMGUFdefUuJGQR8@@l;KO>f1o{o235WHP~UIKbT8octT6GU)Sja~KSV zs0Tc^>dL`Z6^yCa@FT}|J2}K?MLL4#k#(#Mz-wp(LP#Kp+f(CfB7;+F5(}kcNymvb z79DcnqLDexp+0G(6ni%%Rw21wlnEh`xt6Ro5~kIp>^T52B2brND7dmGXL{&*r`j}n zze%-H8<}$Q0Mxh?H>2KWkHyxlM3A{=`dPiNo-tNz(!JmErzkDd3QuMi_KYC3O}Xz3 zEJ2KOZe!MZ)_a+upi%AAMp1ktT^DAY zN1Gfa@kzUlL%Ux>FV4b>qCv%_>0nimIfwt6Q{Me zrq?lorUgu_cReC?=NRc^2}ZQ&0h=}VOO%0iLgo4Q^ulRD%-J2Y0ZCN+zTRO4F)N$m zoMRui$UIXBEcHuSHkOMS<*Jf72vaLd7Mc@XCjcX-hHAcf3dC%gnJ@s}`=0mV^{;!w z89nR64?l!o|F^$}BS#MBJb4kOD!~L>v>K4<;p+;`w8QZeCoyeXI9G#UBn!%l#IKaz zwrts@KvWrI6eFVHw6Z9mp=7su(c?z4KA5(GC0RHCbm)o&O#yKpmz<^HWd0dZ>g4zh zl>_(ALd5W4y)O)%1eXOc13qOn7r};%sMAOx0~jaID^+*Z1*#mJ5x+Je!dP8YjYxF! z=^P?ls`DTVnKcNZ19gp@ixz$_^W3PSf>v8DtQT*Ni@$X_XnX)^qVtoTN0<=yEpVKk0Gr+to$60nMB><>?EVpe zmPU0SDr5uL75iErkgz&u9|$S?MPO)ah4}*jOB@Oi!6}^6=>+rh9@kuR6~6zyd+_k_ z{gU`O-u(yz}y=RlHMOg9Op>$mVR7E>(K$ z0xxzJ2G$#{`9Pfh+ofVv(w?>ty0hhIQc9Y(ISWid+#OnVP*oRF56Q+NLP^%am{!_{ zB+El;iioA^j6+%1hnEQgSzdU(7gq;KSgGanqHrIi_9wNFS$dv`H7ZiY2*d6di7H}3 z*;tSTtdWgD)Bj>NJP00eyzsGs$AE>LmD)U6x|^tjHR75f0%FJV`%XZxjiA^f7}3Cp zNYs0j4bH&A+X}|>Gd%yoVf_7H{D&YI{MftRaoVx={`b8PPd#-I|L7n8GK}eRhL3=> zvJ^tU8ZD1WXy3!+3Zf=v3(-4NbrzChWmeYu84PMHF3e*%H-z^dbMtf9va|(DOG_9G z2I3N`;jQlpJL#AoRuzUr)j{D(sw$AhS}b|ZD$`fBcooO1L?o3|V;C*!vquinXZYDK zOV9c(o?TA{Ffk_wBnCp;O@Jk7!8Dx)8n;I?YXSk3TrdQsezX0~;8HdonO>@~kzeCj zCra^wGqaCBQ~a)S=XDwPT|)%i1PFo98JG5A6uE_6E|82<;V=k}h&kUrJhK zqHT-L&M}NB+_rb9e~s1B0Lev5RYH|SeYuWx)1Ef_?CcnwVJQe%VpqN;%3~36lPYiJ z`SJjJIiGanqr47Ja(yE+OKbbz$OM)83*KWe81N(!OOHLcG5O9zkSv3OmB=N=yMC$h zwkrgyiVP1e78U|t``XvxeeZq$87W{s{P08g_{V=8M~)py;!#f0q*N-qswx(<(gI#i zt$oh&XuB5ErUi_Fbsj{#7;CDhjs>M4jpbr`>J!$q!DQF5$&vzn0bTBX}u`*&H*(9BTp};W`S`07>9oir*ik8@bP#!sorB5(`H7SlHAx&dZ z4F{0)Y1B$d$KMjNIUB+(`(5jg6Abq5gg{R3-aCT8xG7an9$DIULBJStH{>i~_C9nov{ zBbkNSN$6A;5}SbDx=%cFEs`i*Q#@DiK3=9k3KGUDows*SNDoWSN?M;-Cps5pGp*~I zMT(?I65oAHv~na3Qlu16sjNdQaTJ;w+9+$GzfT(p+RU*NS6+D)KKhZ5o{?6-6Hh#W zkN=+^$C0B7CR|DjO)|oj=3sjm#}qPPV7-L{yAW7Z1`H^S0T~O?ssW-zOCzWCfe0aX zG8QB+!zz%D!kp;>$?dvjX$iZ}+lAq9fVK^I@$gZcICYW@(-;vDVJsI9j*;M`AB@H_ zfHz1hi(CvO#l(!vDhcE>+=)&l@O8r{0UyNdfFZ*{u@vCFg?AM~h-gO>Sl7ZpT$6@3 zk;-Ytfd+btvZINq7?j58P}%?)bV5K^q#(6UlnoON5AnN4$`+I{fQeS9a#?AFjnPzP zlCj4aE(nOp_oqvd)r=Ua3Bl)x5_`gH%F|Cjg$DLtR~9})%1SZ<{&n%A-r8;e1RY~#T#5pNr1~oHO2)w&mdiKJhPcfb2R5LI$-F%>3EOihujz(vv=D01+mY&)=~M)1NE z5~V(iF-k2Jk|LT&uN&i%8%%?&BN5<8q_<`M%!V8GMWBpKR*Q_Nl(yN#VxHX))5XM+ zD6gev@a*QAIz0^o@re>9UBf=}3^>=h9`b@%oIfo*1@O_I)@mT8zAgZxQ1zR=#>P#c z15c1D081`S$lmxdNP&3835s+kldoC2rm}e2ANj%SuSuvV38oOI{}D;_e`Y);*Y=NWsac8sGGtQh0wgVyH6|d1l*D>=HQKHNVuX>RTnNP0w0EF3a+sqOUm^g; zSK@BqDxy|yM4=QQb0SG93q1oZvCi%=W7(%nF`^>^MB>|mOH~t@q}vgM1#2N$AIaF@ zg_UDYggufw$Zj3(hY;8sAH{8@8#&T^)wxUQy~py-={v_VP8BR&ImTle`*1Kea zo1!ck#kygPfpdHwiu`=4c&ZFk}x!iE4vOe|I@W6CJSbJ+E+XN1yJ%2|Vo05OZ!xnEiAq5`O5)27lZ zefsU2F_RHCOINIXE=Ihwr;>md6rE;@FB+r;{&Ya*hdIs|oD{*Z85}ZbfRR$vL@B@8 z8n(cr3RuO6P8O9MLlLr$M7Or=c-!%!-*y5jLWg=Vz(+pvQCxcIWv9L6!-o&!U;XP} z#d9w_mskkzbAh9(szq7RdyNxptP{_Fhav8_l6XZOLN4Sq$+2v0|hB-?Q&73 zXe_)l*s^5_A9&x7<6ZA~D_(iSE3ke0wsoIj&jshF0L4K%zTf}SU-$?fee`jB;fr6v zzyI7H;^}9fP5lDHi;j^_WwJ5KW-YDdU9RN}tH6Tb8N67Pm zD+rTp3f{?lXF5D|9jMcSMwA&XLtIu|hq(gyR11SQuPDMwQGzQYfKJ4~C^9gj*as|IOd|A8_!QgJ@#}4BvAuW3C0{cmp>vBBl%JBD$B$>h$ zpzAahBU2f$E^uZ52#6hz57P<*gBFdqQZJ=L464K#Q{ON&6lry2mn`<=V>le39t;2w+O|RH0w!_7^H7*3?<~kFSD0foBxV)P0HA=X^4Pv(JL<{{STDw^ z5hEW40YS3E_&rk;C`m+u#7#yLEg*HLmBzi1G5z(`iwlu62*u zw_k!;OzD5I_+6jRZg@@(C^NEv^|^f`^q~6BlI4LsAazr(Yu2LcTDC$`G_TT$N*NXD zv_#^KKErI5zOT(>>Pg#xhyy=2V|Uhr3@>-E$V)gNXRzgrfNBMfi-06hC%% zNQx*CTZ3)R;^4C64gXXyp~b+G3MRWmLKo1rEnVst%;MTPU2Ia{v-|_ zd*54y0u~S-1tQE=->@6>hop z2K@Bj_^Wv9Ti$d=@gI6DE-c{Yn{L3(H{F2uzWZJH^^gA+e&@5FN7J^j#->WXd`5zO`WOuG z6y39*-GW5R!gy7(v>fjwZbugWQ^NzvHT=jUkKq^o+y4Ro@?ZY%xcH)rHtoANzTx%w zg}?oC___c37tprSCq`mdgkiAG=XnMT6QlyH<&y6p4qPP?-2iFB94k+mr9$3(lqu59 zg9xw{&~u##i_l7-9tXzi>S~LXO~*6%KIHo_1;8kbGnP&0B-Y4)^D{!h7z0RbU}h^y z@>Z)GYan(iEJ@jwk+PgZ0JTv-8er@gUFa}cUB%9wJFv90i07Yw9!=9>%a*MGEJ7F3 zoMD!LffS4?xk`aD%W)^lU|0(b=Z4t2WgC{ZEMfch9oTu!PMmx0F6_NAcM91kJ=#_O!aE=g)?q^KdP{F&4b z3fSeRWo9zS`jsn)b33NB1UA1X;~qw&w|}Ij?gmHNT4%0Zx;|z%Wq!YJxt9T+q{3JI z6Ba6#{IjYmzJo|YZRTt1VsDIg4CVJs&WoSD5S6+S>m9-@tdAeoKj)u{Lw)tV2b*mh zl9-7IPzONkb%={2NLle#GBw(e12J2c+iuE?jy9yqp)p3h>Q%Sl9dAEq=RwS>?yfMVC6>6QJm3_q}pElgV zU=bhw;JfkjKl|5l!TGy?sP4G>>MQW~|K8uho8I&q{KhAK2jBSC_t-rlgIaC%03>i= z!DXl;ap1MF4ufjI61$NXVOFuFB&cMJMccM;h8I!-QbDp1*)2lgG%_o?1{|T!Mucic z1leQ^j0Bcy@G8VYc6kg*8*?h7M$-$VkhbkYlifW~@JkYmZgT({1*o7Okpm&?Zm?RV z%u34zqawJ>fH6k0L87=AkalQQsRRt}y89mdkAL_7iC_8O{$JR(ZR@6e_P+PM8+YGx zAO3&8_HhoB6^0_wH?qE|)V_g{Y!77tIMp)(;`Wg-C`ak*PcJLese2YdqWkk&#O^zi z)5-dq_BeHG$X%r6oQ($QWDaIc*0ZD?$_?=Ia3+zIXG5fr?$;O&WHix0^X4@Lm<>@% z_pUK&V(BE!5Pd+30WiY4NTE2@EP3{Y=P`F;2=6Nx@3AtTz&p=CIYwBD7*rK1=MbY2 zCM`fK^>TM`1{J#ww5sr-6yO;{hxz$=eE1_D#%peS4MH2SZO1lrLG)4(L7j!Ks@~Yp zR9R~boFjBoj`c}3a%5hzOEv4eKzQN3dlz;aMCM5W>(yR{W zlWew=%AAbeP1-mVn1`V0ScWY+X&Mcvx%7ylUsgVR^C zJd>lUD!lXUZ^3I{^D2Dy|M~;`yWjd9?0@2^+1^;rIW;AK)MS!+(rl`k($E7z_p*_E%k1`1zmvukhqkPvgs9`5M?d6|g7$ zV~l0-BiYf3fDjGfMEuZNPV?0^*ZVFufe>T5-<}PlYLYPrk*nrFN`XwoabCZo1nU6> z3jqg;o)kR#_H-axS$>f8uMm7K`kk~35yv9yjB7|E3a859x+Bt zn*dON^9(dbqZ2rH=Wbkj>E-y>|LR}iw%5EGmt1x+rqc-s5!U57#+hL1Rh>_=?HZ$W3BjaB7IK>*WE2j_!$@)-cv1A==a;-^6NJ^`!WR?cI*L0Jf zI_0dzN$G#d%HhCB8AzL1<|r4_-m9B`Ne8}uv;bb3Pp{PwAs`_EW|)*^03FPv4Q2@yAn4CgNUk7$Ye!KYuI9BYgbEj3z((MTt)R?261qaQqVlER5{Q9 zsC9>6k8-W2NE!KAP@;)aXfE`2xJKc6=aVC5ZTqC>rV_I2Z`mW zphAx%`WUgxnI1$D6rvQU;?zn<>{ej8=>S76eHOw(m02L5h=2R>-^7I%UWmW-H-Bo= zzS_BS2Y&W%{uCbDe*n)u_X4z1Y+B3(U^wXu1%zC70G!?_nKT@pMgYWRfDC-O9f9nE z%WHiML6#%~D_K~^6&VXNOQj9$&f>aF5^N42QkD8E?MhVsEdh%PLNpEnUe1N&M&;T? zk}jOjCUc9QCum@zDPosq3_2@{nZ|M^1J7e-I~L}a;NZ|q11NT!4Iy*Z8^?thyMU$% zur{Pj2)+yl%#r~n;7YNS;`&6-#c~$LbTY!P{K~K3{`>F8x4-=jeCNC0#xMML|1B=Q zcpt}a#17~>bmHR4Y`u~5SX7qTUe0!735Q6W2~>d@7U&c>Qc#9xvfY!T^NDb z^#@M%W{O@q)F_A(Fk3Jen(_|tn&IX3gfO$!o;R)%3K$??sQ5(x=x=(Zajx+M8v%;6 z5v^maCpr|2%8h|!c;p2~8-Mnm1%rz^qas$5keCL+^xI}Me} z|BoLatas?7RKInZ2CLRMkAb4iV?;>cFM|m^Pmn0$7z!24qP%!l1ZJwXL@El_waLxF zdMFWZz2j}T>86`b`z$|rZ(rah2G^F9#-8Q0INNc zEWX5xn7Uq!4z#GBqqJB;=+Pi0tc|gF^P66ezx6YJ?MDJ8%H!gFd+}fY^k2o>-~2lK z)~A0L-}vs`=(-5+Yu0fv4jox^fzY-Q)274fXpFepVQFy@5rp~S5Ze~Epc^-6TZ41X z*^Rbo5xRhSp@xzCeeYcgcD5unD`JOgXk~NcKrwG3I_elS7`7Zkq@qrMSk=R<$rb;xcIanp6YL6FxtpcV)Q##^xp z0|%sP@~xA;h5-@#y#J4X{{O`lS6p^ljfz*l>K6Ru1xAA0xuH_(E)=MFv3oMvetidFK zswzq+8=UOp$wC*mtOU%1T~(1HOL|KR<*Y*tkapCriV;2u!3Z+#1=Og+SkW+$^0u=e zUrgxKsGtHVB1EPG(-_NxbiJ)~J;p%4OAva+WYiTQ{v$>0sd3850BEX=<{Rtz%$mE% z8)tfDhOFtO)0S_nrvYJrQcbM?#>~qbun#3wsL#FTyJv93b#5>NyqQCMkWT1UO)27X z5DNN8P&nm&E-gwnHD$nx-#KQfV=f5BMWce0&+a;nooF+VkQ7mMeZ&xP*=3jE9q)K& zzYo?qPMkP_PkiDxas1?ooZBw0j=FGh)K!JLs^O%76xSe8cZZBC=<`UnhSZSgnig#c zl2)pgw@pAw+oc>++qZAU&-~PXiJd!loMkV*^y3xRU5#t5xe|}<-;eL!bvK@V<~fk@ zm;}Nk04*6bfiP_&x=5I|ofJT`h-Bfc$IiX`&@~-K<0(d~V{Dz92du-%mDN-U)__DX zhv%k+hx0~)@3?&UgaE`E(Slhju%+w0XTdH=%BBSMl86!w=2$QZ0ZC1e7f34#i3G#| z6B(RR3~)vR6b(8=Or{fbF~EsLbQd}RImj}J%^Am^F>DQY#;{Ax=Mo~Z@+k51lDKWZAl>^Xv zELuXHR76caW1SU?nH5esK83{XBIlEBOUehzc}d(p0E9^}P|12}ca14P5PgT3*%jGS zDN3p|`6j&ajc?#}Rccu=sLgwX+_uChMXat)5W1GbMI*ZhqXo#ae7VYrW#Jw&)xx2TS(eZW5KvhV#-D5e)JmsNhyNWqz~xksm;eXVS0;r z8Ht*PdvBx7?gD+87iR9P*$qJvUvHBuHvbg+C}Wc--Y zoiW8Xix0?zpah~$zpbYZT<4t);M1n(B0-gB<^EU?>;OfO)$AMGKf~cX>Uw~cl~qiq zlcc-Qzats)+II%LIB7pnuG@hk@0@ifatuWys+Y=A1Ys~#pF+PgKR<^LeelEBwr$&{ zk35-7@X1eo0?!bxktQaDG;zRGpZLdDF3aBsV;jG2gS6+#|d-mYL2Ohw~k351CCr-dgyoky*TLD^? zbMRFKQ&q6u!+4KLs49onCzx~%V%Gw~UUacW?Yso<@pY`R@Yce5hpufgoi?2M zI}rbUJB71=^AluAUbx9co8&8ij7gm zYy+(Ztf`L&o_G?!{7?Tk{LlaBmo^2Fiwg_*`Jek4JaGTRIDGgRoB@nyBDpk}B1O@O zP(d=$10mK9u`nET>?{l!1{TD$b|lN2mJCVJj7D%VX{zumGGIwX*?89=gCr3Rn4Bpf zE(i9tTR@9PiFK?$0e}qOLo^Xa0k~Sb#0BjrMix>u%1_-HfL*cBQR;07h%W#U zXE-s_nB6y$@T&ceGSC(|>rsp-t+7KJET|+mly(|5t_%yv#Yk9L8Dq!JbFlNAbMb>m zAI4X{awo38;d<1A3QgO`w0iIk6vS>!R@@zSR zjV}5UK3%lY>8$jZawzT0QmZYJrJ@{RZjwax>q3<3ph%xbqExIYRTM3CCqlnc=xo(c zb$@70hCA+fD_-%6>reX(U-{}+@Xc?3i-nr36*q@yP8712Wz4UWVo7Rt3Xeqs!FUHNu>;X?s2+(GZ+&Kd zl~{FJozeuvXh0^w5Xq%^v&%IkOp+z<6@Ab^MCc`%Epl7~z8yMNZD=e>0aP0U= zY}qo8ZQHivkw+fIk;6xD@ue4IJQ;&x4dZ&IZaRmt79lj~+77Dz5mgipIVd%)zeW7f zY@z_yR$(@yX*&=Ibyb6i(6%k>zL2W=_FE@4356c1;JdEN>Ru(cqTkfar5VORtv034 z(nfCb!QnWQDG+-gmk##iYxJB@A~o6-u)wwK7!x<-OtI*$|4M*i3?o`_DS1< zk#K2a)KtghVoT;$dQwPLXKo+Mqec}NnLX1;ZH*_1c-?DXjccyD@`vo7m*?2NeLLRr zmbc)FE3UxZci)AlpMDx4uy{@sca-}kHkEUo^&dzx5g=081nquA%+UW$LZBbd`3Y;KN+oGL}(X(Kby-@5bZ4Xh9LcvOn=adVqK9B8Te|vLYnddEV5=M! zFf}|P08yI^B8Jv8I3@9?`_W~}m9ZyzgFO3dMT%HnUPg=^>UtlBb3+_Ddjx|~!%Q7D;^P_U*~mGKP zs%Vng?ge9VpkJ#1L(@dN0AJO3_q*SN#l^)<@BQS-llaspKZ%uV(mqLIXYSOchw`(FY8bYOX;20%vy z2Ve|f+5rR4KyNf2nWd)R-Y=4?K>;M~>%O9RVQYP`L{ApvIsc0L~$F1X}~n0oDV?Sv2hwU1(vw zMdcVIQ;b01O~6`=5q2nYeuun3e^QB}@^ zWX~WFr{=~83LPwU$od_`F?p8PXBQ1%SiUTT1~6?}v-#s{)d~bMJYJG3=PX%-^$+UB zbnuYm%%8J8tYHm{7$cgg#dtjC^{(yEN+B-B6BbbSk+5HyF#^3-G{V6NS)EQ3RULnV&mrzRupS{BNDHn>u)w<@uoxlP1uQ>R@SU?Mo|u^6bi3 zJ7JYg0w9w?2eu88SG`7mBCvQ>%IeU%9+{m`F!J`d-+>!ndDEu%Ohot(pZ^bdZ2$fw zP*qnohI2z+fCmFq-lL0M^8b0OZV+K61c0b&A+LoJ^P|w9Wasj*)0;>XV_6Ul1kBCP z;fCv9@iO+(%XfJ1asBl-;Npuf;rNP&AHu2S75EU~eU)ll_^Lu1BWwt$Vh0~Rd<o90 zWwUnc1}>TGWGAm;y=rSh~S-r@g7SHiwGfNI+;rC8N9s@n%?|Rzh+)5Kmx5CdOn*Qj`4bmQ%L`U9nHv111!uPrHO}Q-dQ1 zy6K^!Aso*mQOedJwr%R{KA=!nM)u$FKcEyzQAF2cFvP!6Vu;=#qznqUbBlW?1vYC3 z>AD1hR&71YamgE;TEaPEd)@Qsg^kG~RO=nHphW$hN;eDplVYDKrzdlYq(F-ZL=$_r zll-a1Br8|rC(1YqWh1m$SPI4z7HZe7UAX$a^pZ`Zjv@4WM{>%4QZG{-Cl$4am@ZAG}o3gu^t*3k&mDSlC>I>yP_bT3W(uUi(^HcKPM_?svb7$Deot)9D0V;MxII*THukd{u!0 zyRcjp(8U^E;HrHaQX5O@xTur#p93e#|@WWg{Pl8h&R3UO}OEzOYrq?d>`Nb*0=D^x4#vyy7gu$ z&c=E3Er9488X%>l2LR(XB9H@o1rrJLHQ-2s-zcI6Fa%-M5ZXzHty?0t&MjdwsW534 zF>M-5nkLJ}7vulMXCJ{`_uP%wy!zD}_Jg$sANt@saQx&EJo)sqBE~ip*BmRjM&mJU z38z*jSQ$;x8iz?6(MDdQ+R+%(X#=!Y)-OV3fq@$`tLlJySi@7q>S%=8X+}vz<(veo zvP;Xt!OpQhhcm3Nz{;EfU1(WlRqmZL>UiPS)Iv2zICZ>5Go4^Mo*;x)SUe6Y3>X?zh{=iav#v~cD4tf zgJGGI3aS7w9#1ftG<*S*pM%#IQMZx3ltsCCui;9dQoUM?xQ(EW7NP7jI5F>@46tuiVVu1w7Cgdy!)0DFui0Qqy zZkz>CZ$K0m>H#DIkTYAJB1`X1tZ@P~uQ_mq$j}8z6D`3+5r8R7MHjLhckGy* zxc&CGV(;F)o3_mhFTQ}k_xJuDzWtqVp_MuRn-G>8DKA8fcD*+6#s^CJ0&{Zr9B|)6A>kx#kh#{bg9^?Wj zGW!#q1^EirMHm<1qJtw2BqYn(3q-6=n!#z5dIm@gsD_POXE5&^28&C$;;L)$hFfpJ z?|rGk)^pFt0|yS`$gyR->CJD&lLrps+N-X_!qOs$2;Ty@o@sAAtxi7^7B34ZYK1K7QLH_qL@gb*WIA`D`L zi4m3vMt+ahGJxtj{NC^XJ}$ZFBAj>Lc^me@mZe3!>uqnum%sdFR8@uHaG3th&CQ`2 z3}EUZB6$QDP+cRE6Z6in=o7Jc*|Y;f15C#jD=Q;RCM_ZeUEAT<@#8pr_y`Q~ygYU4 z1hY}zi;KiyIG9K6Jy;T(^*Wkvg7J6+v@JqtF&R(LOd4U9vT~|5T-nk)*8Zri!F=q{ zOs8nu7DSwx!rB|6Op1W<>KH4hPNB9HrqeMDIk+l-s6`AdVvzL+3}!T0QRM|<6t{@U z;*BM*N}w4xwlV_phZxb>uzU%CLJV00Rv?{|I0epZYa|qaudJCei4jdkE3c!bWcl}vWh|-Yw?t>W4}&ZXM-a^0*>-4R-|056?HX6ISFNf z`q>vT<^j=(-hs6Ohb%`ugYPmwpKi!hvL;3bl2B4ft~&=Ak@)dQF_-2`qp+ZiRzqED zzDA<53ht%{w`t@^@6#Qo_v5S%9YiJ}Uv6VnT9aFcy0EOyWNn_r7-exHBd(9o&186} z`eky3bOn>qA;b<>U3Craxc!dP-uJ)w=l=qCe)UdN)c^~NLsV6rDN-VgMq@nt?2CBz z*%$EOgAd`l>#oB)-ti7xdF7QbfE3=zW5R&*zPmO{_@+sS9G|1I)5x#N!oy=sziWuF zw6ypqo}nlYYb`Fm_+sqYvj-16@Br?+|9%`lewP zk1XSeK)%3-u}3X_!+XeJA=ZX3M! z7(@oqNkB@1Rok}ZmtL0vga8X%x%YrJxpn*$65w9N=DEeog*Fd}MZqJ^>S#-TG< zCO15ag_Fv^Uy30}lv!O^4CjWh)?z#!A;u^{;0~b+>C&BOoaoxiAj<%-SXo`g>S)9O zLNd;H$u6N%evHiEl+QG4NE(pDIHnjCWDr5c|BJ_3^P2CAKZ#J3q8wdYO%BXVelQi0 z0r4gWrN&vnhU8(n0wDX=4467LLS=5){!#TX#?dWe~o_0i+Hhe@}X zg8x#z2w_!{#(a^^b+(80eH6|6+#k8TZ4PLaijk%mNQw1e)D>f3hN29}6xr=E%R>V+ zOEpYmEZ+Y1w_|B(Y14bY_uhN)>CgN&wr<;jL0z+d*-L7fw9(F;T$!{i*Q2fnNN8T3DQL|I?E+==Vq`D~)*fK>%M4}(ek3MML8H!}KW#@< zRk-Po-n=_TJ;xTW-Oj7he>2phIh0_{yVo9=>f^@JIqGyRL(CE{Sg?p(MjW zd>TVGke>_Qz*L(D2@}{g)L|`#i^rB!62wS!P(nNUhk_L{Jsp6wZAE1mhyenS5I7m8hX@)pY6T@T9~J`1 zVnkUNKivaAch?1>_LKqPr{a6&4%W^mqbvih+wh!AO(M&yt)@p@L%D5q7&4Mxr&6@a z93jhKGsVLIDT&WO(BeAaMRz}w|J1=uk=56XTqg)X)Td$)nKkqiCNm5;Kw0a(Bd#DC zx|VbJrTA9{84Me*3q72gi?{z&qddPVC&d15GpKeq}TzLy-gkmN~H^ zDkBlv4C-=)TE-?Oxr5SY{uCeEwr#^}Zo3T^?c0ZY?zsm~J@q8U<8i9k%6(TMR1p*c zeC0twGO|J?uAq?Ubms#+G01S9gRGvNer21(Xx8y4rG_%%0s? zd~^}F-u5cI;?nc6+z^f)ISM$7JKlB&9(nZ9#B5GOz$mm>?OL2S?_n#4Ck`CIIoq}{ zJ?|@k2n$P#7!E2NTV6r59&fn)tr(40aq!uLIJ&Zeox65nI2>YiWd)BvwjXT>xaP{M zk|u)!C#~QbW59Ge#TUQ$MO<>pC7Wh1?cTi`mtJ})e(-~b6Wh=<4V<-Tyn*d3;0%mo z(8X0(H70>zP0brs^l5!4wGP(mEy%C3A6dhBH8slRqHp2$4hLXK{y}PcU?AV!0fP(k zwP*fC022a6V_-7sFr5S}&Uw7>>@t4s-+Ua04!sD2fW~?dJYYHyu_3Fhxsi%wyrE|y z!auHq&124{jKawJidA?Q7UtorN-}Ju;MYvnpl*pIX}wyj%7Wtxos+#e8YGErS$h&L zN#}}rT`IstFUzw)Vy9cPW?UW;R4Gh=OIAMh$BQyR^)fA8OnZ1yp`FH{Owe>YZxN=%~hBgEz7#8UElq`#kdxkvU?8NfLqM1lMZW6N<{24wAhw`8Ez8KEl}pp0hiA7Z9p|s`Kc)N7n^ZRY2Ei71s_iR-AGww69C?6#?WuoQv?z zp`)A@UZ#iZT#5i7pXu5T4?p}UYzNE_=FyC&_=B&157Y4&qva6}?B9>;ue%C2-@utR zt;mZn%q_sX3e@p>GoDQF{Gnrb>dB`OLxY{$x8u6&uEmzUyAV3S`vJCZTf+QY4KfDX z4;;YdS6+o}gCWM#DOQ$OF`Y2bDutfB_lc!)&I1T|^wCH0^{;;&?|kPwH*9ZX46eKG zIvhCgglL2KRDrkZIOUbR1T{y87z~0naIr-U2GMq0%nTOBv4~V4PGh#S8;0&lK(E|T zu}$Tpl-8t~H=|l787SzIp8~w)Yih{IN3dXkE%N~8Js>5gFj>I-&|ztQfR&Xg%Xb?m zEUkyN961uSM7=RM{W7ca0jK-i4|^B}aKczrm6wnOi>0M4JT7G4Vr>V<1{nY+E_(H7 zoIH6FD=RCgoMU|vLqIfK5H5m$Qh%>7y8g&H(*8TM_Gs>H}q1XNK;`FJHa<>02gy;hb0M4${;Rk8=GFw=I0^G z^*hRYNDEEA7bC!iX;z>O9h$a zW9!zfm`uh{eg0y|$kfn2WpHYMR4^>HLuxD?K$||wpSr_Zi_0&+9DDcf#X}E0ga;pd z5XX)kV@-!jQiMGN2-PB~svrynmxlILfRF6n^4=l3fXY`uB#`HE;G{U4bJUwhB>ilh z<0_XiqIM3~UU?<1zT!&UbML*_y0D0?3ybKw7R|K5onQJxJoo&IxcP=_0RS;{ummjD z15_Y{w#BgaxcR1=aLbL?<9AM-0(Aked+n>Rb;}}GaI_ZnpoVi5s!C$CM7go5DvV=9 zN0AH9RaIhR)Fr{7f@I1u!}5)9d;>S#bQ5;%+O=VOpL_1Pxa_ja@X&(~p>th2XLb>S zgP2{1&N^7fg%GWgltZJ{IVi7@rcebgS&Esl%0?KH+yj54V61162JvT8%$4bXC!Yg( zgDMuD(u1n0H>Zvr!pg~It`B2~GeHa_e$NAKhiE!stpvD44T!XemSAS+U;~JBdEb4k z*^C>{*|nzcW`{8h++tuxv-X_+z;mJ!jSya!Iix@Xy+dL*001#!JQ|~E8d&ELBd>Qt zzKf7qv2RdnW%;|%c_Q6&sP82xo-qTD;uPsKYoFhH?*U@j&C+^}g_>k}!qOF@H)}+Q zB(*J#B^dRM=fB> zSpIzI0_Uw)E=gx=@MQ|f?1H#JI6EkWh^ijo_S@fry3PVd>mC2@H+~b}`~F>skj`bpV79e(*zBSXjV#JVJ<_1Yb%R z4;fWz<}tzoxGYX1F*Z6qR1Ib0Dfp-V7!HTH`R1E($t9QIuDkBSM_Dd7au zWy>}^{p3?Pa^wh(9X*b@`60G%UBU>2y%%1HSKWGZVN@G*ZHps^Uc`818TGDn;Ech= z7w^N;`~VA!^Qc^f3-;_vjM|nhOR(1AJKwtpFcF6iAI7Our||T_gBb1HiIvqAjK&k} z+Oab|zh=j1Q9S2WQIgq~BS(+m>tFvm-v9phZ} zO|cZ7?TbJl$D=P&_KaGvLE?-CUT=1VGg zKO-0*Sk#!b0qt}MU;tYiuE)~xT3@pK)>z4$U>yuwR-@FWm3lh~kWgl7qHM_48br(- zYHMrk9*_gM<*+vE8UgH%b4jLv0fp@hjP+=mskpEz)_o8L!&+jdIT{HrWsnraT~^mM z+HsTjF(S>5WurF%iP8vM$2cWGjaQC`yo zlaO;*^+JriTI%3plo*Lvq@p7GKU2pjQsq|??WTiVekVYclYumJP491|`*nlnZ~-G$ zWnqPU?!|6_bf5%voZ(96UKgY+wx6h0wtvXG|@m9jwfWGBDgh%xZ+d zAx&f)tZ@k?D*RvLZc-*d5#$;Aw))_rxFey^l+>V^L1#>vnwnqFKShV6g#aHFWQj8P zB8<13E-FEn-m%*&78(>KW1+rvb$8rw{SCP8y3GZk_CNkO{@rhU0^`XfF;JE)A_!K2 zr)>U~cuc5pj8%DO5$>uhmUBOP{5bBs^K00%XCL17wmVQ&71|N|XgMEWfm7~Jo-h0_ zr`oQa$OflfyZ=-l+qZAWTi^OtTz&P`xbMFE@c848i;Q>Gv!u(vy)YR1y1EARJtC6v z!Sz*=EV^au%!mBQC__;`}V%juC98TUV4FS>8`nPk2M_(mc z4fSFAmUia03%X)HrWW7{Xh;x! z&O%YZ0OG*BDCLboN$8Yck9u!>J|aRioB_egciESx>VSGAWUrP1>7!>rW0jtnIY?QQ z;$$TwvC&E(qhZ50qiRhMNqK9{eCS{d!CKAYXvNoS3w6wHCMdIG04f{vpdR4%+wb6X z!cC4(eBzThaNtQ{8Aa67Z=YTE%*Z1UO4m66J-6)|rTAgC_lt>DaSp4|ea~jYl7S6!+Y74_y|8ZN&0;!Vf?)mLAG#~*)UjX>ObY=~C*wKf;+QU*kULlt6Ln|>+p5qKq76r2~NtW!TA8VAC&+zm8d(KcP)yk5Cji$W6 zR8__D)&=8Uo+=uxt7b86Tbx)vg*I^PPj;QHcVqY=DbYStaVCd%hHGSKY7qdqB7s+} zTBYGr`BsT$kAc5u2su@!xB&0*>he%DfRsL^DJwd#gKqq?F+>Tf)c2W@Q2`V=-8!vl zjYlVem%88s?B`~mEMTMK3V|_V+O&Wn3@R@k7snO#&cxz;E(T%v%6&`S$d#v+uG+%e+%tFOilH@tF9pO(iXk351; zfBLsMvqvSeN!v_XLM$;3(v;v#h~&Cf47G$1fwrB(dy9pIdFTJnp>nt2pPJ zb2#{`%SFDli3Cybl4l)^4cc%8JC+(VfA2qm!#Rg*uDJ&L_U*%i4?c+d?z<18(P#}r zp{#^%h_k>VvlYxx#8`2#rf1l_#{rAIDt36>GjM`bUc3N=Z0|3fT9Iil7&3Nfn5$wJB2X@ zbM+8&m4z7&&=_DcnE){&v@ITc^ik|Se>ZmR+=j!4j$;4g`*Gogdoed0;LxE%ICkt9 z_Uze%`|rOWPdxDiuDo*N;&JDlcOEXf=psD&=%a~^P?n-}foK~mvy!dan6xO?0~Kd_ zY&4c%UJ_1O2VmN;d^wRw6u^N>COSPsW-0zCj&=7R7Cz#=Q;k0&wuEigQ?brsabb~z zkqhG)kajHNuM9AqOt8ALA|J5;1w=$R3F1r|O^NjGOF+D|=Sz13U~{15p-NkcrO<`g z^feOnS56qnmY!wJ18qPKM9g$gkGc`+%H*OUay=D?TKiA$N71&C;w7fIh5{CSSdRWS zuz{=XsfDSB7K9=xG8RGrJZE=Q-X@n%3>;k8g(&`bE(^@hZ_);3qz!fBhkloc1;`9L z16*My8)}byfeIF+xDg$# zI!<*mQ_6M8SrMfA6DDPHl=OGDq?nzke1$i?>CLB=*#6`vKZSz_55l^N85424WX3C3 z3)Rd3Ba>P0%LwBm92Xa$BcQHp%+Jr`-h1!ID_{9a+;r0`vAnv9X4>(!nBGcO8Ih*o z&Dw1xCE6d;|EU}c3k!Jlt6zlSY;xRkJn4zdt&_Za3Me%Xm0dvh1(Hl$ax9fL-V9!f-G| zhz*<X<;CT?GC>?j5mBk@5 z5T&ckAiMWC)@Rd4`;YcG@4WNywzs_v&ph)CzW@F27iH2j3$dQ7BeNAAF}gMJ6}m}w zf%~Dh2B9NXsnn<{1B76tn3hdB{LbZ+1p=_^92QTLqym`A;pXeF=@~exxl(zDHgHAL zOE10{J9nOgrG+_kZ37*YK0*#M|` z0Smn0D>rDHz@LH6$vsR1wqZ&dl`PpK+oF;G?&%Ls+8yxD4U|qKq&!wlmA^&FAQG*& z=d<0#xf7UDB&J?16ETmPDPPlOrfpC$G;=HvB<5qI`CayP2d$(KJJLFZHbKQG2KxG| zL5%APPT{A|)}~vLaU&vRnyn2tX^VqQ?sW19y8_3NYzaw<6d1~sOkp7*i3BRwKV^gB zQSzLF<>%;^+kz;z`H{81OTd&f*peG61mJ`xUXNLord)uNdzjB$^kmzDzDnV=IyzzX zi{7(Q2rCkL+8H`sS9HnlN)#sMGu*2G6is~rVY6qoWYAbEp&7iLZ30m-ZCAffZ#(9E zQGh$Fnnv&L&8mQzP0~|U_gr=13z$%cYyu)DTZuvyN3SrPk#3CEADENA?D)J8I_+F- z<0(GcK2-jnnF4KlFrv zGaD7rfU(CW1OLzscHEAPTX8X0l*&}@_G4DFFtm)3DC>4d9NIT*esrdg9_o|M#q z(&AgKK7re8V>EX13$R{t%%6IywLvYAUA*&(kCcFeV3JgKMQj6=?9wY#Zt@U>AB zHjWw4(E7Rpg>}w?C9pE>s*Rvmeg9?TQiY@wKb7EoPur`Av_;Q&zL^>%Z zbbOCWr%Q`Fqq{j+I`X4mgmLJ*OtR1qDAy)R#{N-i9Mgwq9#>UCdK^-kAGmh{A~E0MX@uAe7ozC%fX!xLFo%q~*hC`- za#2w;FR4hr3hm2%_4bCJoVsXXu(7k_x4*szjL{}K5q)25ZjvF}p_yp6#eNEnU*9i= zM&h2VThrc=70^%Fz&7ALUTp*uawd|213nTK41p5ch-ifbKK3ZxQF8N}6Q5AyuVNi6 zeqfpF)ByPe&Rkha7q_{^MNJ}ZN6-tYHxV%H1UGdK-wIiH!Brv2C2|oO5H)V`VITi% z3jQj)X6P@#)bmGm3!p?kz*QEKcqKwZbkR?l$j==Sy8=EX;@39w)QN!cplG7D4ZP~5 z*_t};UHa}K%T@A)+&2=5DuGc1ZEOY_6MvQ$^_;RB@(}On9SYE|SBE@NaUsh&yFkaC zDGesJySfq!gW9`+fJ+9rbUxOQw(D`Y1|H;qu$C&U1J5w&f9A$uO8Y!F({%9NUJ_*O zcT4*Ben+=4Z{QunDT*2xv%qJgM3m{$T+ryI?E2Eq+lZE8s&f;4-{9fme03e7a9Y!I zYPqExexCnilh{AoewN|DBdLJ5M7Z|fhEEewhlxdkw{bk*;DgMh&H#42*{z3ZOEH%8WONU7rA-GID$*-1UxJ!T@&<;$SLEW|yg zm=-%#T53(~D>-~_(hi8Qs7OIv=CqdYLP2`K;VSAFs~Mz^V;*Bilrl5 zo6$P;*PjnM+D`57Q{eo{FIvO>(`*KzS1e(d8k>b%INZeB?5U{j1pE5~lf5R@Oi@!N@ z69|6FqUQLJak{9lYN`GbA-n|96Gj>Jw^I+QfcNoKt9zQNuS1!4GA<$UeNW)nZ5%iUHRoWc7g=RT z<$NNVRfmBa&SVgsITK91)gdJwgWjnyRBgpWTHfRj+(+(_c~|o=NIP0wkH`?B6zr_n zHy+Qf3SJgq2}sq2U;z(DWGaxA;YFcQ0M+OoN62+s%d)&q`_R32sb3D3?^ttOi9Ek# z^9KvoI`y+Kh5+Lu09BoSM=&L-hJX}-PRt;V62borg`MU!I>ZZg%-`zpGtM%PC!%;v z$;i(yBa5lZikSubhsHlIUxU})*{zo*F_c(XW#ekgeBah%g6!Bj8dC-f2q~Q}Vm1bj zVG(6`qCwlu8HK%Nak1XL2*KJ@`UxiA z5cqSBp3n%<tvi)OT6yrlnT7L+U#4#%=1?lG_3T2Rwq%TO(&qxZNh0M+{4aHs%oS8EM=#)l-6 z*1%GczSyWq0N%CBDkAU^Wu`f>!M0ty{2UcxHz>NBOGbs?QWz^FG{SL*3=G9MU18Ie zA(8Vn9L|u#eZu1kUiX3)wqGAQhbT>oB2C*!xZU>Q) zDNt(TiBMNl04kF!^+J}LP`z`Y%s&ivNtAHlAa<6XJRT->WlH*#+yjh89)@HyEMQ8; zaT+UgAyRAnvu})}kJ-aQM%$4}c$Q>0RPIp=5(1(qi!=KuF&|!d)(#e7O%h=#F#|uD zmE>-pr>QAZnHWrK@3e8{MUQvIWqAfd_(ZaPR&> zZC7tRV1vd57A%h3UxJrP_KN2t4@|WcYO8DjYkSp}*USTG-j%V)7J7tY}+q%JcHW#;XuqgLsBu~Km=A<}&|3gOy z3Q$(9G^fE%v+uRSqC#~9I635lciR_W+O2?2k1304snego#6a0hG^)TKG*KfBU1Yqq zm#aArSz9nVwy5#ssu@W2eY#H%;z~%jY6ddEr6SkV9DWjpFxSPXStQBJ0kfZrt#@TfaKMZ|O=8oTIcLfoTP2S%j2Xt3u>UBhkn|`&)2f?1?SrNiPU41`GWc`lR#% zaB7Zk_iLsf;l)|C`-1%4_o88J@g8fP zrl9JamxcG}oIq*8uy{?-W==vhM21gcnl?SvodTN1Xz0#65l&-7e&#>2w;5C?P{|QJ z8t8nN9`U#X&YZk(IzQU;a;Zbc1nUS@rmIwelCX(j9qz9XzU<3?sF>ARd685@gNi_|O3qzVQ<1N?6 zef^{vEk>=GBL;yG7KE;(2*X&s9$mu~`$O=Dk(qO)VPA~NB}dikbxgnzIG;RP{BxM%8Z=zwcp3Cp88W3qW@V^)rPVY;2BwyG%!kk7jm>}8Xb5mB z*EI;tHZGMV;U2}bS-qG?r8o3E86gi@W6P!gLoih>5?JbTY3MS2=TEGXI!mLiC6ApV zOR8{>Mw4ET6s!kg1rdQ>+Avo=>3Aq5qJF5ypQwf!IfJz7CD?ZGuI}2*#0tLCIm!$M)6&>LuncMa$I$uGXXQP(bs-v`hy-4(?UX8l~jGZ&IY=Q`L)S! zWj56-t|+(&~`cw`8!Tn(`0HV{f z)XXKvE>w2%TNVCZ5RRyW_&IXc)U5+(pjgbmnVL~jNb{Pku_Op?k5}T1D% z$v@?6Dh>FxOZex*`*{l@C4atCplY8~GV};|e>*Gpna!&+Gn4(6Ok>IJ$d*CzwA7FH z8K-7A9&SmPtv313^f~fm3U0R|A+1MZEbk{g;Z)9->|F~?j^XyzN-PCQ9-LiBU1!4t zw@}H4$sf>>7{AzH?1UEKwc)Cir2`AdaUe1hzY=Ak5Yg^e{iq%F?3&%Yd53Uw&*61N z*ttzO+IOWhWZ=+`AgoQs5;k0e)5Tup0d&1j{cAC!aO8~$)GE3jv8|O5-F3!Dz*EwQ z9iMy@XGa&h_?bxJor@bpf95OQMFoUAg+xqtZR9;QnI<2+{+|0Va-w`ZvjAT6i-E^p z=nl9iG3JrsNx4lNzglNh$BI7?GKf{|vNR|PgE^%#M_{!GL{Qo_iVnNUp%DDBuL836 zXTlyofJb0}+20E%RkUNJC(?p^GMIdb$$tEv-OIv&Ju_wz`tsCsuZp_HH_3z78Nnne z4h2Rn8(g5IPL;w_`V5D~Z7(m8^}UZ3T;HAkNi!EICUPF+Bb*v)9*bKhcHB_=mc88y zr;39LGEOLd&y34C4^&4s0>Tu$bR`nw$?Yhx5GLsuBLtF5qG zE9_N_PB(;WVgQrC!2#!7*?RVTAJGmqCR=r5%nB2cTx-oxe{#kc|zd!2W*rd zh3Hc8#6v*N)!iNKN8{H43lx@8BF!*rE95WTUMZIQYxL^y6ayyh(%$IFrqdD_;`|%b@n)-*_BAP@g1~4KHGmk zpUM)Ik}={l%Ak!PE#*>>U=mKtT~Ya7W_fpQ9?C3%w3BEoyH5uJQ7nah(c_vIyu<%t z17WpB0RdV&H~v3bOdJk9+X@*QjiCQ)P#woNl`Tr5G9>Bh51K)d^y*MbtGgCEvqGUT0{d8iJ*dxP6AB5-S;&PBXPuhoVLZlsD=eR zz;+4H8uG5DY+I#hN04{Ti=P770VNU6b zzQs^Gr6SIT*1FBWSE8tz9LfqfHN$36Y#*#-V_qh2X4F*(PbZ8RdA9>q#t$B>>mUj> zXBcLLj|Ab(1aQPPB~j4Jqn;r^51viZfsE6iYeY}WH)nK`$uwgEDgtn19 zGD?s9pMqm_gLI6W+=DFx;=Dm%Z@MNYlmlh}xVSV;2My9n&R()@d`vC;T=d@Ox1b#-{ zJ*-T2+%eT&S?_iEoacY#nNW|_-LOej%#XJWLjO4a3Lcnei*5AqOcz2?sw}?Myw60C zQooFd#W1Js(uWHGu^+G7U7m&M-#Ng}bfaZ6#ff-}n)|gTuM-Sut5tuS4ld&N93!&r zJ8368{_VmFX(wA$L4<$uR?%c*T%v7?aR=iQ=rK=mrr0O=6<7#PK2BcGLPlU{r+|vP zEp(9o-0Tf`0Gi278CQ?>P%96h{x!IsYgX6R+K($%qo)*KoY6vzPQg$coDBrlg5d&P zabiSpp(K+5f270_95Z^o@CDL1BumBDslafM@CnsOIwDu6m4xTr_cf`k1Ixi$TaOdo zKqONPD2jh+t+oR|&7vWs!72`vg;DHYJCOwvwLTL?V=F4T5x?$e++}n|_75WkSTSNu zj#XI$iolrRX#78r|9+z{3P{1PBIVZFN!0ULgQEt%53=~tQQ(O}<=+{?lO1lY3hK@v zZ#mTG@bpjf&xl5)&)}WwOzwl3!4wGQoCT}U6ut6TE`y(C%?Ty)2Q8|hh!%6FyQk&1 zMv-6^h1mz0&^qH9ooUh8yae1%j#_;XyA|HF%!E_Em?Jtj}J7o^lDgOa=>(}9mFv+S59zwgxh~XodK(OA&SmF z7?1si9FK%xun2AyRn{w{t;#U+1oFEq1DAi6pYk%oANvm0x9_1U;I*j$@M0Vj!K<}RptM}6n z&FgyAd8gn0RGM@I6z0Tmb?+e)#^rZ(@LX)Rp#tU3u7v{qT+drUx5>Xcvc}6=DLI!Y z%pR8}*5S&ZqhImzhS*JGY`b(0QOPDQopI>oJXOz1_dU zDbx3F({hecX69SUjg$_(E~(vHN87^)23B=AlxiS{9*9RSX1K+jl4^5ExMXTo03SjE zd5iQeVU#+;jMpq!1FaAh)8SKHfr!t(&b$xjb8$^VA8XYAS@!oMKRW^|yN@DzZ7wJ?U;Jyo z%4^mcbweeP3s{-zr;Afc_N;}GNwNRY@$$AxP%>b7+Xa4_z{)1pH1^?YI~rIfJA8FB z>G_E*UDKBN?nGQlI7ko-2a9UNYX>afGDuMtnnNbm;a;SeMd-Mo1SrSwf>z8`R+W)| zt=imhyqrx7$%0Cq_ncXfz)(*V8D0E%VHFr?^*(1*!az~EFHdLx^tA_TkVtsxh=hk_ z0G@FT!E*=_m4%{DmMLR6`HOeMnx1Jw@CyZ)^Zk# ztZVCB?b0Go(Vn;S2Jz6#sckr3*>Be)!^aYxX=-vVsEHY@zN{kV-pH7uRnOHKxFA=D z0fNHA)mHF~IBqu8FA15y7{YaE@@1=&FJ7la3h~&om*b$I2x0+(AS{^x31{E&HtAs2 zN+TL>W*2V#{-boeT7g^Og})0#A>8bkp10bOX;)VhiQ+>33KLt_h_%iMqQV@FNOok+ z3YexU<%WUg{y`!r%7@I1Q(=RO*$$Y zqW@jb)h+F^V!+VyDD|!B04WZ*Usp7=jDZoo{onh`?adIXsi$Yhm}2#p1L9Te(X1Jq z%-FgNMi>KNrxxYZ#_6?DcT7T+hjG2J22|sygUv*PX3pyoG0lWyDfW^bERWt--ZV`6DD{AGt zjE+a<=>>?y)h>8Sqi{>;zrylD^-Vvo{c=%;#O}8xBW}9r=;0EI64tU-uop`Ga=ey2 zMajj0X7ahxeSfV1fsV7+_S+2!eSI1$>p0=T2GJ>f4Cdl4Ic74c_iC_FL2gN4@{!8u z3W3O1qYi5;W=%hY-akmlMKCR1dJo{WOc<4>3y&O6xyHQaf2!3HBb~cuYV$@`L$W?d z1*;IjVP2n?>)!uuywq99#4#wKy{fNFZ+(d-MCam$KqP~p3NTB}92ak#WKhgfch{P` zxR|z2PERGq&-BwiB~E;U>1uSKq;GtwU!XRn^p(L`IviEA0r41~)s= z<8AJX{2ziA z3RP`-!wE@x{*ZS(;8}$=1Poi{S3@7|1v)wdsq|X9AQ?jh0_woXtMlSN#b~A!NT8!3 zF%TygEC8Fr6+#61LjH+dw0gY#1-d1zr^0pqQ-)WM{$8BcX}!=6$> zC#S0BtGgN_>i%_M+@}El~$>DHe^!1534}1ImIwfz;U-bmpxvXC&Ak-()ZOsmpkEu~8zVhMs zll8h;Z8-gX0+?|}3s!ngccL&&q?#%cuO812C+C-*KXjb1lo#5x#)zoj+6Ngv!%tBd zelIf<#7_NzNgb%1Ced1nIaxJ05{+Bm-X8MB?-mhV`tUzBA8BEt-%O}1^9Jydd#d#u z_iop%dh?o^njf1>#Q%|KzV{k{CBS(a+OfyFy9+7Qg|qB-wa7p-sZAaV`LQD~ zEK{h{A|4pNLC3SX*;!GWy3rp4AutBm1s2N`(7F%j;0Qje{8d3~PJ^{XXaz|^#Sd6T z7=InoVG6>w!Pg1kXG7cWf#V^Iu!RDdnQu+R%;;=Ryi#9_VbM|kgA=cB-Dg%AI=*`J za-1!+mTYW@IFq$5whVWP7wB^TIOHm%I5g|@!yo_)QGpglZM8vQOO9anxifT!l=vs1 zN#{fxMsSvDm?h>TEis~i;dAwfx>-$qQjJXNXJm2?lk*Amr|-n=O}p}WuJyh-@5s(q z&}FMKaj-bhAq?C$e*>En>|>`ETKs=w68L7P0_r@4z=Yg?1ME`BRm8d z(N+us>}V31S4lLs^@J_tTpF+#jQUIKq!&ZfP$Plx~Jblm>dbe6S1ady45UNm)J76n!4A-yl@3b!hXGAwcsAVVK!X8f6u z5kfegzkW1|$eoy3gv2?PWnh>#b-hG5^Md;A8(vj^qYyi&Y0a;z*+Da*?LuOsqLDP@ z+MSa_V|noBQI$&JP&g3-**t|?F zgKNmv<$*#S0%_WF7^G&T;xBtf!)k?0ORDHvfLjPx1eAQk3F zyTgC!^7y|~?06k3_}tSQj{bbs^Ct>S35)nuob(&}m1#5L4?Y?3w9Oh``=-6!k1B+C z!DkvkcnMfD@J6odeLmw@@;F`~`)i0ZuAj;hlGCz_amtxiY#)qZw?GejL5#Z#(>Z|^h%p)9t7)sc*iJ97NFEHyQ( zWM^rMT1%daW3w?-+Q}H6FWJmR8|%+u5HR%obfz0VKUh#^c~f9sq;dp(5ys+3#J-|7 zv~$Pzygcmgmjc$+LpY!cT)!^_s6ppqaL-9zykb43j^DPbTvO|s4*$H#H}YnCcMHRY zp=mGt$C@W`4IzrE&HC=g_(bU0iWTD9z87xMyT$54$7!jzz5#yRmu;L29%a#1x0p}0 zX61d~w?&GcqRL~`!zU)fY-XpP-XiK#*H2mC%&4x#cXi1-;)5;l^S{qROI5xpTwI6y zJ2qc~GFDW6iN60rk{}EET*Gj7RJa#w^RxGjjd&Uy3<0oi%-#|Ie1xDmUlPI88W4?SFL>OX+Rct=@(!4|5wgnd~OC{S^On%wGS7_ zlAXD*LM2q7(Xi*A$yS~53`rDuWoWa&bkv!m)PGg2n#Z1>d2w)YKkwMotqm%!aIh#>L#gz*w>!Xm@A(XU)%On#xaBb&XNO%X zbv|XfB}|jjtv98Cq}qnOIs?q1c72%Bg4{Tk_h|*Qt_=(BeiFER-6lPHqo1g%}0!$a-#fb zg9tc+lpE1S=w=la&?HI*+kW#WI=q?E=si8&`Ep3x|HYBgE>)>XZ%`+g!!S z*c{&f@%ohoc)|qdzg<`Y;}QeFV?_G-qnN7*+aM^z-5@uqNmyjNnV`C_?hj93wtqjR z=zUlzzlndp^$!hxBpNQA!sVf?+=+KvV}2Al+?>`$Yh|6aR@{^CS?Rh&Y57n#QQvn_ zO$e@wrQ%zsNta&fRfDP_cU%e^u-qof;>jWxot0T=dt6bdLGtYW--pzGyW|1Vt>tV^ zCISt}=->OB2kn@iYokko>@Lq5&8Pp@Gxe`Yl2#^%0sW0JBj13%QEJY*S{F?lEi!0E zZxOgySo!e>-_!7cL!MHU=qhSdD65cw*fNaCl)vl{BS+D=g`ufA^hU_IJglG?4k9;8 zWFv%tI*7pdjwYA3F1be`z=6-AViu;cTg=9T`$|jwqI^2{@UWy%As}a3?#)Q<5x=l&{+$b6ouF?}QR5qtS%DDeQOKXb2Hg3nm5N`*)Z{e7V%8IPbj zj`Nhrx$fd~;urB-w#0-_mIDl-$Dt_Dzz}4+*^1osU&rs``6sZ(h=g5C>ir2rwW%T8 zImj}w@nTOc-w*QRu-)-HW;XEri_7h|4-F0`=i~$f2oM7zmU-x?_i}O8L3^x7o#FnD z_tn60-wh8CZrv9t;O)M5HmWh(UTIuApLxyi2v&8=_^WC2{qhj2Fff<5%zaa81!x($ zCG5K;C7Ax2VesCr@j+&HjUB|pnl(~yD(*o) zc8zwrBF}17c%Lpc(4LH&=od@qQBKu|VZQWh!8d}TUc!nJ7Qv#t3g*BGUI49f+irJq z1fp#2G-(*{n(opDh(bjNShPpB&}-Fqwj$t(S|eh`MQc2l#f>a966`I0`38|8bFfWN zS$klCD<#OqN{rmzlWW+Y0B4uR~5Bxz)ntFBDxq|F+3xeF7F z(?UFmr)^{6d%r$sEArc86Z(k(H4PhW;Awa;i(w$yoLrGvl^BB|;?Lt?Sidw?!208P znaIr`3LH(tY)Mp^31q|UpmUz|u(B$o%|7q@U(u2ai+w4A)+)mztYn|G!ibZT|8&sB z**Oh#>lxm=n)GSRqDSaEdA^UJnugj%l|1zetMtg29+$O8Y=8X1n7p z1vr}!Pu9v>Cso^ECkX20VednddwcvDUCJ0;F$qbSO<0kQuI^1%9M_!^TNufwVLv)? zt0_k>tc#e87vFUWXync_@|MF)k7%SuGER_2x&~W8hHadEiCF>Ja{AB)0bXuGL?#ll-g_+Yr8(hJw+yl~s zD$*Jg9z+eff;%MtrEcAIuwahb`uIu5DU6LMoDd+cX~(D$tu5_CWi(**XZ#BZiaYVC z)K!GW^s44IloY8Z#COn_lhZ4+4+E8N^f;c1LZ?aMvrNS#wdnIlxIk)S)e5ULk}CIA z*PB~&UG-QBy*l`y5E5#!5UjwF8~OHsn!;^kFrG@2V3?jW_46ZPHtJ^~aLiYzx)O9@ zj|~h%JO^`BmI}l=&V>K5f06$0{q2J46=?VP&`tXR&#A5jngvZ23#=3j;iV7uL4jzf z=MHsXl+1;A`hC0O9wgJ(&JbW6Q0&S7cqaqju(czh!pj5fqk**+&{^leuThYMN)TLy z)V+9e^TRR$nfOxcYT>y1+$)cIMFGP+G~*80FM>{;vd~C>r@>T?mHg}>KpTc4y4A34 z>F0~)b<{Og!TA)!0;eEsj3n6`$PwaK7j$y08ljxh$?~7EWEcL%g0+PAZW78gp;;F! z=hB%A)e3KvY9mb#zN^vU!M9oqi2bjm@&HkRRrP1e7?HE`?QQ`BCQt+~I^l5JY5 zF?S19>m|q%6s}vuR{uKziOt)LbBB0ndy5=?Po40l2r$-dtgB88%6?70nv)jNVm|Ar zj^MQnqWTvisSk?hzdM;e&*OK+IG)W*jm28fp|z{Fva9cAhm$bgTlQ?KKY2uCV5q$r zIz!5m6po{IWa%IM#oO($|FmvpFd@cDEo4P)$iCKe>nEQ5d)v*oh6aKE&%D@rn*-_p zZtREuY)mbnU~mv*inKd?cN}R#}3Jt}v4%KVVOn?IPy!qRsP($0=+XqzyI!KaH zsB0+c<4|q4edFc=Vc5fX>*}LI#?B_aq8>S@CSqPGKohdysFw3zb>Y`Df3wkyO9vy?9$dgVZ z5@~OPc)x6j4d;*7XI*%&^r?kQu*9no*BFPuvG9a;Kt={+%<@BD?DW7k{dbm1e`9cR z0QayyZ9XhsMorTzmBd1|swg_+bbh!|m_gI1A?UHJFw{Cx%_NqCvB7SlJ}Z7EqSIx2 zY#rG8I)1@!#ir!GIo9+cJpRuA=TmL*{IKcJHYkrReTWoVTg-3gj)DM^HwOoB>Mi&l z$xD%3-c~C4emfJGo|$P89%eY6w&AhxOOX$SrMDa)WRI1?B&a>W!qD5gcLF832;$Qc zN$O&IEcTQ#l#e#*n;I=l%%#MOfThbKjgje;A3+*G177h093O70@zlNvj_S|F9gqrv z>w7h~PMjnZnh2;iT*_!9D>{GJ_n%_P^L1_1Y4`7HwP67RaMp5YVp8{HJy9DL*#;PO zW0c6B+7D7wrI4&$n(8pyH^P+{3Ev9Av0vOJ>=E3%6OwqyhBi>Po#r zVG`D2NC^swF!~CAZ1&p`<*h2d=;D)b0z4HVeK;$PqpD?|f^bsaT3L|!YDNq;D>@Ro zv*xfQvOpIy1T1vy>-^o0F~$^`Rt$Ng7fGdZ)~HN5BLp%S8%!1rLZuBSP>^dd98_dr zVEVd5{`;h%IMaHH`lYB)lL_{|)EozPp24fz%j5TaO!X>v!X83-JxcFw#kuk5cV$S4 zVLz74{J+y~jEuF2bXTp0ajlJwK|h~sehz*t_|gsiK7=FuAMuHZzwE;%`F&ekb%MMJ z3PAHF^f4^lUVl!eduU( zku8M4G#76_pSqvH4%|pvI5@@V$K4s6(21X5ulX>8Atg|#4cSy0$AfoOG&RifmW5*4 zG@|+t$z(i6nV^dYK*9IODXC&`*qq@&R)cbydDY9ZYM8ylc~FPRS=dG*`G2Z;jaa?o z2w4$cuKeVA7exGr^5R$U`A2orvk2&-|0P;iin?g=d@J;1HOWDfols8GTzUE~KoLm? zLE}LMI}gHy(~k(lwKzZ@2x$P9aaNFz(&HiN!&u72K|&<>V91ZgAoio}no7{|TJA*! ziQ$rARRrPF7YNsy$`2U#q8+dLaHeD?EdI*M-qvx;(Z~;PuEYJ0*BJ~=@3JHe29(aF)rJgQT_UZN@>)R}`mfvSkqP@#(4G(BEDvgx&-mKryXW#Nk zxWPhLFXd(NIp}O$Es=D~GgIT+2!u!54-4$aL?r&NPFpT>0v_izZ6+Dkv$BUq6m|HE z_vI&91kDO2lz)C-S(Yp1=jqT);|2fy`}|KgvBQt zjaujmXoQUOIb-|UcohK?%-V|yr=39QotMr)2Lg6iN3%Zl@(=Nw z>z!Kd@X1ncjc_w&F7Ynq1wlWhT-6GnSF^SZg37OstOSP;as| znNOuf5~R1f9MA0yb~N!zWpxc(v|5=`QCdTyi$eqw;9(`TGmt^UV{@KDtc_z%`bn{7 zN($FCeoK^@EcY{!yO*)xNPhcg|8iuf2jC*Oy*Dsh{{P-QfGmbvKsy8$5sFw_IWkST zHr{un1U;t!44S*ks$%ggc-{n=jLI2k$Sa zGs|wXc>>X~1JqGPDZT3{T+kasB)@ZyE7(`E_ui<+-$a?U?cGb$EE0e5_(LR0=B_7= z!gCPMcret#dTiPC5!nHR$U%}%HGGA{rC6rfo>KwL-UQ+8oC!wh1Jp_-pkg@ zMrDb256N<+14hJ2P>jqaB?N8?yO(J|*)6a{bnat!(?@d&5Hwu-q#J=9GyW;-ko^=X zJ1zKv{PlJwUd+rR0*zmyRlQG*tsx4hqXFk=k)4C!8iEoj{j>HISt)}o)@*4{lzdcl zdqdpLAAxUO#=w2(_Jtsxc~g$gZLA@s63l$?_=9 z=vC%)H1ca`41|M&7ntqTp@Gc;pW8(S4+Vj-8bu0*+zGJU#@`Ih?m<{J%_ziptq?P z`9J5tTsG`A6L6<+(bSpewbSz-YyS7A1GN_|4eT73g)c@~s1j@43zt*GFxZV+iCKg( zB1_A7C|t!n0l%STu(Dw)I(FV#eTn__Y5k1u{~HAbK2G}l12U&9JRT^tB&ovc2;z+7 zs$wpNK*vDIWbC6=7VmFZm4cIn>qp~IWVIz3;dZg#ZhUxA=bcu0Li`Olqo$;1Qo?W+QJDBV215f|456{L24*lnJ zNyX#%FD*5Xl_DbfO5fK}ihthw!;zV9x&RvuILMd`y16m=zyHha8+}W&_s48U%cr^) zATX55$|qexa#W!+@D!VIM41-m37}-cB)is-nPi~iT1dIZK33YlakXjcRQ>N?&VrPQ znb^{eoa_SO5RVE!%%5jptXYX8DRp%V`>UHUw(ShxU^@Z=6LzNSrSP`1#Sfl#^+_XAx_K#DtR~VtQU&8)?5v(rvXs}$A#ceSw(w?UHTZ{^a1>i0 znCW*zXpJ6WD&xXIjMjsGD0gJg`Pzt70{(u+=z8gGGepHe-VKy^PTaPIanpRb!LwPC zkd${q=!lA?3Jd1Q-=%QPu}=%=m{}-`%hpjY1@emQYOXQ*J||-y+du6{J1+sAx#qEQqoYV1AM3QcH zD|_mJTS6Pvn}khiuFU3hwaBUqVLPEd?D&ab%k!ja+JL(>haOg49wvvb%U^ zp0wN(&9XY}zDz#NaOi-)<7gdDj@HgvBGtsN%;qCzqzO;Xv$iyUixpn*{#Lo7({?up zH=fp(#nQaxlZ9zeiL*hFa6>;8#wSQuk*sQ<`4x|*gX@Yz%0t^`cqXvk?wr$hqmkW- zy9GHc7|WTyyw!=V1-)C`i`mi$j)l;Ytafg&jpne-Bnr;)fO4+FaJIMw0;vKUKE=l6 zET)PqJo!4oN+2njGXHm`yKmcDRWrcN`CQr<4lOAO#TJjn#ri6x(rGCa_E633o8Jp<1=N&bvZsr?7+0Jv*Qy^LBa$R))-n471o88)PU*u}J zr>j-=D!XkH^+ojYfHoTxiAI@>Qx}Q2p}5UY{gua;I$k-=Sr=F1HI|j4#!oI@bR1>! zHcpTmGV70RBFqiC0#rXRsW#NtGq?N`4=Jnk)JurKYK5n}4~OG){;cqxy`#YI-U)~G zSDepoiwa|(c|2VNWlNqn9x3A+YGCtO&3n*(2HoMa)qQ{0rDC&RU;O8M9}Gf5!VlFi zfa;awt{9PmI#aM&A?gkk5RLm~e=?O`zrlI(x|74jo_+8@Gbl>OGV7Tbq8Hxk+m}HM zw&G#!4oo2yj4;;cTpmzt4yW3$grDUgWC-lUPl{R*eX`_tuRL>dnwy_(0^!oM_musA z@W9`*D|2eMrE21fLhny!^;Q;MaG(#!acp()2r3D4hW`p1S!c9vn?78yOC||=y?5Ht zwiK{UChh0vcXnpmkYi%Sri~-Np;73vJl8ip^I*d-7B7NmfFC;PgX$aqE%zR~DmP;C zZm^NVG(VrTv;9h*{E{11Qe!vrK2##_EIr<1ZlH`|yE+*~oq^h=LdTrY+#c&NQ@URX zpWe6Wr|{q19qY#c3NfK@zM>E+AXlMuS2*7aeCw7j_HxAfe&&DsHU3ix5`(k_UmX6h z6Th&Q&={3?ZODiYdrg(#^Qqys)AULx>e6?v#*Lkn$KVJxCrqTM#$h0_jWJC|MB>?B zwc@NZjh_e$e|J{^*y8N(V;HGs-e)$Ran7AGn$mQd{iDhzP2jxKxfo%zi(>C09s9Q( zBu9qv-u5e`t$idk3nHo2La^|v2X2O4qN88kiEf!prhz?~7X83E0M=j5=3jq!gh;thjH;rogw&JnuN{9BTv%(+C{Bd$%MM%S%| zJ8u8p9uEMZWdvX*n864ZnF1CqK8AK#VVJBl+?wYrpfy;kKV35U+uzEkw~Tlna-XD- z1(*rql+#c&R06HYBCC;NS_;ZCeUWc1;lwFc{*R+`4$J#($+laz7MA&bfA4YhPyf_W$Nk*6uIqE2zmrcqnsk{t1QqpcXHM^No>xDI zB_FR5q2Pcni06xm3a_wBKVlCFv=;68(Fe+B5LP#PZiIWBDv_1OjO=_^?<+-qT${T# z2& zq`uiEGnN*kGkiAp*mKS?d-Z}2Dg!aQlrE`_mSHz)7UO$yT#$L@P;8gAqy_=`{=OvPVzj zu5VKUidkP)tQu{8KB|@Ob6=@lm;oJ}86d!&a1P*J$1^!`bk~IF2B=e{e(fs_HIHKt z5&$g}_fmQD+2^$Zl5B>H-CMzTbWku<_${XLZre@yhaoqsuFN}}^vRU5oP@VF&dg6R za1y#&K(+!tFvF8>({(`#+63N~i=g+p6fSQ*)WoE``;3FsTd-5j&8EA8$2ivjM|F?* zetaNi-jBs2tww%Hns@4Kh}+S3_1-g?-^lUn9as8kveIh5PhS0UnV;RYAV;+*hi)XJ z^C=_lKZT|)LZ8t!@}`K~#D?H&;elJM+~@`o^C~>7I8DQ|mi8LcJ5h5Iy=>P03o4Wk z_V&b5VB1EuiD-yg%U?af#jLVEMO*klAYp-;wWanf@Vvgquk61ITpHC+Z@&RadIXhR z0UDZe`NZn*KZiC#^cx8amOS7VZn0;)_O77q{a zoZkr!C;niG(LaZ0cUuI2aSfOxXI*#Sfsl6V}vy z7=}S~?kQEXKR>}`L)XWnLV670Y#Bng`1cn<-Q;Z&WJ zgg<-kzx+zrW#pF{M6Qe%q??%3PQ4Bkh)o3lMPXh+H?`J4Cjg_aggfcKRnJ;?LpqVf zP0|=wh1nA6v|;zy?sIqp)fF3>!rXvEZib7%uCaj z+$qUaV)aZdB+yTUcN&BceYpBu^Fxn-{Tfsf?oz;#k z&Yx)m=ypFZ^(_3%KFdIoP==i~F@nAWr&-A6Yvr#pC~82uKkq_z2yZU&~P zTzS)XvtRAFM9uLULqG;+!xnM#s6>0vf{{?7509p;;6F!Dc29c!*4(+06c*zR+QZck z)yb4;wyLpauTS9Jlzi+&{wb)7lvpc&$+i(h0wF*!2YXl25Zja~ifD~yFD`xjoUx@? zvY|t&%*xBJ)F64dpedLWVi+CgW^;gO*Nv9>jv>8>JMGpbEV`o zR^j_}(?=vKp6J(0gmMd)g43rSc#jq_Bv`}RJLA~#y^bWUX}i}T_Jo3GajCwo4T)GM z5BNr=kD_Y-`Y(h?N89X;tmDJgTiPwR%&r# zZOAn`IVC^O`*XD(0nWqQ5t7Z;gZnf(1_i`Rr6NqVw|nsFa7{A05}+l0y5V7o+ zRW)4^4<;LktHRtw`kG(bV2ziDGRpeIH+eBmgxIQfefSTB{(ZO5^`67;Fncbd0jDF| zW@25+#np_~lkbIb^&wZee~YN2-jK|g$I{>l;)!B?VJwBJ8ZYypy3ycO6cYO~%g1&3 zKmAFj|80_bjxU4T>7dj7m$hvx*9~D@?2{_K^f6MyJ7*0hjgS`zz?QTf@fK#ra!V+^ ziiqWWr;}+RgxMvDttY6dExO{aYIa(@dMh?AtDGLypEyl<@BUHlm*E#Bp@{flkvcs1 zXXbuLuiOOR5{FNJ7JZ2X3mPn_g%x2SD>ZD){I2S+^1!G4R%=f}PkWnJlj}m#pf&!4 zktg5txy;2FZkNgR;VUJI;*I4$eS~AwbVH#XW3aKBLY*GPQI2#k|IiWZ#+|PO;{R$1 zp0!jm4_Gt&nENrw1hwQ#vX>%e;v#<}_LZa8B!KB*`%&bm-sWRd}M~*PG&s;!9l)`WzV4Y`n%QvP@geLxiNGb>K7P8hBZ5YwmbGD{fy zpy)rL+y2>`U!{pq42T61!1R1K3v9gTc$dV60f9x4XL*$Odl>4y$S zJ%gpCEG=G`Jv?4Xq19J|m9;OB=eH@UIS6JC57*0Azn}YkzEn=i??UZrKt7dP5#X2> zV!j~~`_JN@NG5Uv7s7GRpYx0TGB^ z!zFQovcA^w0?^gD9vozX6ugx4h94M6OpUfNJ0qJFDb~-=ArC3pvwvGvFxlVMh|jBG zO{}@5xe3YSPL_|E!(2Z;?zoXGX%HVV!q+pik>`MhLX}llBCc55jf(u@n!-5`YflZ@ zu_Bx7$bnn1;&0za4wW1}c6fRhP+^jDyQqqfMEl*#DHH`zF}QkVc5_9|&h8)wt{Qqggn5zwMvY@~5_OVh)@|AxLYY`k-Ug(WX zO<~B;su~I6WjY3M;i8KI0Xr1tFf>Fq#St}J9x8|YZG%G>6(nt&)T?B+<(kcNn8~K( zN&vG<%}E{Bq7^Rni_NEJlCJIOdizPuV&l8qCJ3a9&$vjtEh%>flkA}Y(py2k?Luv~ z%HAW!5(V`y_4k-n49vAUxn!LcCS)Dg)?yxSJg%-1owSSVYNaIPH;E(HO(?4q@kC?t zZNmXERr;8Jj?K)5Uo2S>0l&Tbjkp?5)da^eyc3I1PC&Kd_L3*}%eNNOp?G6^`?xeV zd8;26x2#V8Mi-b1FFJ<+TEuCieC#mw;NI?ZWZ)J*Va8~6flI%x*ZUO$&T0pSQ~b|v zJ=;3hrH@G|?Vt8ztv;7ZfSK)A!s$d_b#*8+Ft@U?K7J92xxHmtZgCZq0DEB~Q*v$e zafvH*(6v=LEesR*KX~&*u??Hq6k4EBZe*iqgTP!O+h(_BmQ#L9=)T68Y zt4^y#(@K`l?cDUy+^Y8(W1*dZcCJQ$=`VBnA79sM3zuxwH6|XUm&scwBUe+`QrFed zJ3MAqR#Q_`$?FX2cU0+<#AdmyUOwpfehbyD=JJ9fy!0|Ra6?421aoIi%UDJ1M4V7W zb$vG>{*lA8xkCs9)cr8PiYXzchKr@@CJqN0Vb0NsI9<6MYsfiHC6vg>c(7T0tUb&k zA9ZsH86sl)xx%GPe4~RRdB!Q06-O8}=dMrnd@hT?GnjIQl@pq_mvf2(7Ne9S%iNkqs^c5mAQ% zkDE@2f$lQ_k~Tq7O0MW9d$e0M`U+K>&1uKMf(b$J5TS-4I4RNOIg7H@NR;GxCG2G> zvT#~|c==b(U@@d`DxR#VBy6IbjswPo^ae6N2(r&uRnxYRIQCEe8p*_Mnn%n2>{QRMpUI}RU%oVB)|GUf&t_WBVca<^xaMbbe#DhQd`U+~2 zv5Ep;tOpzk^mM?WTydxS zM$8LsV$h(G? z=8(vOj_U007FXab-g{9}6_Fq=yf>MaM|hN)?WZxQwJ4B9!JxyJ@S-yS7NrxIqrZ20 zcyK21+QP*DkUnihjnvqRkIl3TJ2!v!2@8+Q_%I~ldYGVE)Qw8@6Z-ef1Fc9Cb8~4c zf{@B8Tnw;RLc7COi0oySXl?)(FK>i@cLb9*3G%NI=73X$(9$c!t{@tOv~;Z^>JHUK zX2k`}9nS62`Zjq)oEsuqQkW1--`Uk-hG40!Y%kH$pQKE$_JdLWyoG_9ydSb{K8VAH z(SW@SOVHs}K33i29P^chVs6Qsrny2Q=zRxyuyzn&aeA~Y91|rOU@UwikSkMZIOOi9 zEC~)}b>^l5CW9z^>~nMPV&PyAaM{ z6_WoQCKeGoBRcNh82m>1SVx+G(sQB>_CV=TbgiqP34fo_40Mh?Zs7poOb@DISs zz9nLI_V#1~4S)i}7ot02yKtzlwaj#g>PB`===ij-i^8xQO zA_hxK*@hNL$+(b}a*jm9;Qc)qP8%?6DmyKgsubx*itV9RwuKdBWb3TsT~tVyGhvjU zpoNbF_v*~~>y*?oCz|GIc1?}fyZU~?mTM@FoABd7|5mc`H9M;qb=dtW%@Y$O5qZ2& zZFTeH*y57mJ8DYeMk1hF(qV|k!^=CA)8Agbbb3XE7zHs-!3{buIiZ92V<^w{B!!&o z-W+_RXNHO0c0yb_l?I^{4Ks&fsxEOGsIigKXZvt@A3v!0NHa2L|IGCKu{neTH0q1a zF9mD2o;ROGY7EE1wfEEDcj_D(dgrvgAfcl+RhzSrPyYI0Ak;rQgko`# zmmO6M50wWVEgbaZs>oIoSV0==jF_DF2M8*xdis3)eJYo0@YsXo0=GNr_QVcqO1tPK z2x+LIE=f?;6b8Xd*F#-E+2Rx!QH-$DomiE`tPUv>tI07uiCHZM1Gh~CygZ6h1~?tp zn=WIv*@xlyl2tHLT7yjaj~V6D)kPl|okIIB$4Gd>eK zC?lR=DPoAMpsE~{mRShLQ^Oh$4^Lf73(wEP<$x`4p3mE%b4>^vE*P5)tKj=|7{GP> zG4?%B5el&U4fXC&c3Ad1NIf@7@P3#qVm>gD8gk;Zn{qFO`kcrw%@Om%Dy1bJufnzn z^(;X&*Rzw}pPLwKd*Qo(;QPbHyJ-0<2mY@!Sq39YOS+p^<~hmxjxTe$Z_j#X{U;}k zfH+fh&{yeS@zLGBO17ArX_QY46z~j}DK6YAFv{$mav<n%yff%slfu+X#?Ps@QX6z*1iSBiSMb^F=Pf$I2?)-B8 z^u8GIHg4DK)sK9;(1D5;N;WtmtThI0!Ij$56PwQ{h#RJ^t~haye9aXZD3sEaIF18q zSeT3vQhiWFOhQBhs&me@EQEYE$Dy@GEC8oJpswMkVFMaU{IA_Rp9mxbyMV;pljbUYtg zX*P%8V|1EY>NkU*74QAA5$h*HS8{Cgl-T%DLEqg8)j1al_Ta{_I4~7NX&OI2edUIu zI$C6N#ggQ7J%ZRtd|6U|Myvahk)m&I0r$CuQtBFvX#@jFM=x%Nc)lMvUak_??$&=2 z33v4Wm(5q_C@Q1OLiJMw41YNJwZ|s{qqve$=wVt?CnNupU(t9>iTREgY+Qv0!lpi~ zMdmU6QhNKjL&;FBq2L`P+v}}t6VQ+f#l;oM1~?E$j~3D^rUIab20Uci>(JmznpL~} z+<6xx08R~LA}sD~{rpG)2?S+9#@*;t{(I*_^?;O{$dU!iv+_e}kVZN*9P7*%3VcK= zH#7Z~1b>pOr!2fM$xEsp9p=b?{6$JXhGS3P)CgF*239zr#yo9NaGikk69*zb%D=H? z0{dPa30<8B)kgjdB3KLprV9M6T@gHGHk-*jb~BO@_Ad0#($}yqF-+fU>u^e$T6N?L z>|hX3taZxYUSgl5tDx zoK#jmkZqZDr!>{AiUzGr^5OR;6)N7Ov;WD@U_j=W6Nukuv2}Dm3eH^Vix>9&o6HL|4{E$+;7#D{IFu>fdE7P<9 z8W z1ia+G1vo8k-v?Y^Tw%Q5d{RrNf$uX$!KAkQERoZLx^>hl+phN}EZEjc=KkxIv8j0> z=3!*+9iH{&OmO*6M|2TvRkdomflWK}%Bctf&Pj$hCos8gUi}|5%i^xK<}|8{u+f+M zZ%K;92PHRR{b(3LQ}|#`ndD_P-$^+}4@SjNh5K9>oTWZ4LbkcDxaiG?roc(33-(+t zd%4f7={+6N>*0eno&Je`IhX$m_%dJVe6L1W3%aKxLL>uf68yU*c5P94mIgGKABD06 zegtB+P{S#}YVA>J1`Xp&^HaKLrk40C?L?VF+X#Zl!$(Z?nE%Gf)sJERg~D5P3# zT!?1GMiSWLA%I<`ygW&XC%(gs;&O_N3wP)m>0jGxCk0iNi}2m7Ir;WykR?^H=Apz+ ziC+IW>{#2G1G8OzS&BQztBFyrN*AdxB`-&Bw~!cHhCu2({<%wmLk(mptn zLbvMiTLrl1gIer2k zPlDt@`)dZEf!)y3Rv)fy`1CRFd8K=(V)vrJ&6Pfe%)r?Ar}sE3a}hI>KORkV1IfQ! zIpnA+$J@?!bPtL5FX{W^{)TNXVWf$Twv*TS59LavwDz3UgHObT6%lMZ!x_JF5Mn_S zw2dDze+kC6nWz-q(FF(1L!^?v*Q60lp_{rm;c3Kdi#GqbQ50S0gslz)Ja%crij3HFe=#a@5# zI-P}nRAL$Zulg>r1t+xNGq3UlNaaqf7(^A?6hYYO34?C0l%kF449}{sql4hVoT|*N zqo5Rugg9S7e+b&G^KfTLFGe3n8fsT+e`xmo`3t{Pen4gZIWLlQ9~{F!g<%XbCNVH@ zVynkh8fV(-7uWD33NB!_B%Ox?Wv$Oe>JD7Cap$*h)INn^t~^}w#tj*<0TpXsOvX|E zP_goFF!gZ)Dh4KKGR0UY;bBTZ-S+!<=zZP?O)>y=(z*t`$@b@$RJ!{7wuy^T5@V=`7>9{Kg+w}ct@+3hS^We-{@^}OO7!S zvqK=`Xtn^W^1s2;u$T{+5>Vv9djd$#)r9=tfrcmnv%T(rIzKcZhK?@kb3o`dr2?4| zL8_6ms)k5n2*jBFs_JBRB2qC3=k{JQuKast)NKg(cq-5P-EErMgtE z5K+h>I2id?I%;BKRjM0jZ)(?Uxd2?dz^i6hCiu;R09m0D97)Qg3w0p$3DS71m9~jU zO_#f($EAz|74`(<(yR$ckjzmdvc0a-v_okCW%ZvnKe$&zID+060OS4vAFxav95OOH z>h_Mt=l%yf-07ukVRnSPNRY3k|1x&AMtW^Su4~amrWkcM-j3t0W~ZXWeKo{8Mm;5o zMbzw43wBKwR%c9Gn26-{gvI^wjj(YpvH@LJA;wGmcJh9s7%K>cQXb1j92?7|0e5xA zzyvj0UkS@m&ay|3I0$94JT9wM(=|9l0Ms&aRiBf^^=6q8QkvGU8h4L~o6*jUve(r) z!k{UMlgUv@{aI$oME$uH=n}>!wakyGf~XH~cRDItm?$N_@B%Mqs}7AF+#BLyDlZJm zsZs{v4U9IJL8lCJqvy<&XRW%TDr`FVu^A6&90UBNSmDUeL+JLr6ZIhd$BZfG{ZYKC z>S~j*baN#eC4@<+(9|+XWyRjb2TWDW=b*Yzia|qUuha)`zusx`0CUgTW&p_bm49BD z>QNnboG2!O+@Iim>*Ce#7O8wMqGxnZ;#Br1!9jj_XB9+g=%-KuugyvaN zV)tJ!;_oj7h=N>^GfJ4#HK7hs<#4YTw@cKVM-A$|^CIzdEp?)T>O0;>MbgK}<`p1q z-*lXXJ+Bx8x94l{;OUv%@jn_@i)-G_Txja(WSFuU-~uGe{ucW+;5i!zsB)JZJu!gX zy!65;5DlcU*zX*g1-7H;hG7TFv=6@spN#w#3Xsrh3`qwravQC(h}a<|#yk+r^dd!f6BT_EoK&`&24G^Skx&dQ`mQO;fN8+TUMCf!#f z%W1}2VTWJm^;~0B)YtEfJPf$&-f6RZgFc+>O6U!Ok}@6;EfUk!!KH!p6`v6TXJ8TY z^NTlRprFbfXTo=M)dTL1T0G^KE>blpzMKdZ9tX#IA60to&^|}ALtSa0UiBOT7U`LaC8I^t**6i`|Ey`RhJfm@ST{4$ zfSZ%e&V?Lxd2@#)9=m1o`h#+9k1uGfHsS8&Zit@k^Jz-)+$0 z6YOG$<5msIQP2f{ORpS}B4^|Y&`FJlM7!o##UM0UtP;s_liq}D5)goLyEsnmi)ix% zr5Tpl#ZZ|v>~pPsQz!+Lpvf7f{so&Y5~cq591}H4$&T(J+~mF><-*b zVInvx*%^M2kAI>hBFT^6s8v+E#sE^);GWw+$7xuPaHHp{v@`+fS`^> zaVmutbRd^*dT9N(UW>Uv9@`Z`7J37T!fmE@abAACBC}Fe+4crz4~W_;Iv^P2nCE z43etVk7Iw4lZQRK^wcfq&KljD3IeWcubvq5IE^got?3= zLxQ9>%aQ1-e}eO!him4QugE0HGZ?V#r+ucJ7Z;&}a^r#Vj#Qce-l_!;NFY7^utM(Q zBj@Am$lMR4)fz;^>-%*eE`M3POAtD+meQjW8)^mY_PRgNpn zQLXdM;oa%xNaR9FrkF(J69kj|^32@9jzUV?MreiTWL;NQLbi}aJuj|H<&dPOP_TI| z%0@9}-Gcj#d@1$YFB~!By}U1KP1bz2G`>syas=rMBW;RiCR|9m#v#i(X*>uje>Hj# zFfJ5T5zC2H=u45B-53yRjpQ_iXG}L7)3`lY9}i&IwrBVqli z#{tOK=5 zt4W4c*D$TaIHCkagI&Y$EcFoiet5!f+<~FCT;W$J|I6HWsM^l!FLn-wL5=X{3?tJD zhU(^;1z&NU%!7J$QeA~{HK91r4BBGD2ELS6^5d3EBc*2~TcZE+rUc%q^iZsJm1-3NkKRvppEB8H zV6E;RN)1o4-8y5_xG?uI$pZiS96tjr1;g4b(GjZgi{t)iLnq2a(1%htaZF&%kR^1B;Ip95n1lKEZ z@SUnMkF(aLf1H6=mNPOLZ%Pt|ef96YWz*37s)QBgp?%(PMYi!Xq>$2~X_7qQyZjEO z%FbG{9P-_~_NZ*r^7UWVh=xb4=svf6rM5I21+LomF^Iq|b{ z9*wEWbySAc|FoM3}b|Eb-HK|=)UD(*}JX^l7seUOl6JZOb^G}_%Zo9 zTad`on0B9l=G}+EF_{!}EU`S0O~%eYOmgC(k!hts3K&!99A~iUbZBBGryE$B&+H&U zXPs-O`Zs9jd!c<*?ara-v*OE{>^uJ9R2PC}kl{jzD|{FXvKnI*%s2C(hRSIsxEyVC z^I8lJR~?;p^4B*%`e^>UTucv%xp~=3Wf0+A8)z^y${=&D+j|XQYR+5At^sHVS`O^C zWIXXuH8i~4fW}1=bqj-Mi>-*iF_m*mc~EeT??}>#$rm@ph#Wu@nd-1eOvdV z^tVIi(7GA1==R9X(+z=edY4PtDxe&$eIHo{Eo=^5KQCYFhZ394KOGy#QA+$(MFWp_w7F zi~=m;qL!J;UQf9eQ^af>97kU=49|9t6)BPbIVmXvKQkdcN>Ovmr9D2Zu>ns+u-ht3 zPM151KK$oAHqKZnygZ!^pS+eDwHEbk)M%a93&+;7NouVNrN?s0z*b2YOxbT461HkO z*1Cv37#Pcb)P7nR%tCsa4o&#)QuK|3#70%}uXgT|y#{F0Xqlu%kKeK-}(GsV7qRCkohnF)9mFIcVf%m zTxZ}tBap4r-Y`}NKt%Q0OOkck>JeAjX1Xs0FxMf5-3k7+p^id4@Gt;k0c5tN@ZmLL z@Sx^ar0~Agl3_5K_|xRnx;=S>))H06^P=m+_Wc`O$G^c(Ml91m`8bN1QCfS8>R{ys zLyG8u8wZ>At!gS<70vP}q)nvq?c+Eri5UN{;G!eC?c5)NTHRnpdkYEuF@rzs&U^W! zLS9Z5=+R8tHT^_nU)W~g2>Hv|DnFi{64_&yw|cWByBhki06iUY!qL zD4E&d;cbHLfs{)`5gXWR=5HeyhCPHBEOQ2jBu~zbhHR;(L4z1rmZeNOLjqNz@Fi5I zR^G!Ajtt9~VxmzEzkNSH`Ql4*Ej@zSbZ7jQ{2o`3@WVnH=U2=xj2{hUU}e^VhNZ$K zuHwFJUf(0jC9D6yip~wS2aD@$G>%A|#=uf)bRwINsPC10HOx%OSWhVubFGOaq9nwT zgeJZyu4_uhkc^SO@`Y6yH$w`mcjCdztH-AwLk1ah{V|(ALUj|SAHb_;!kWvCHVo^S z!R`oOMu5r$p@S64ERaV*$UXAa!5UlpM+DFj8~D;t9HFHn)=#(}o=D!Fd^RC@em<9JWnGlGrVy(fp>X%OfP3;c;kUkk2aJc8L(znQr%!JT zWPW>Cz_O4~d=WUDKGg(Ja9mygcsIixg}NTXSa;rlRcOT4v(U`#?`59j8Yk7*GPSo#d>T>Z^R7O}&T65({cHuP5?(Y#lE>BMu=nz>$np|^57N~qR zxX>%2V4&zbKxSv5Qc>1aHH7)JTVTD+8v8dblK+`5VY$vKc8dZ_SBdk#Kd-Kh+x6`5RQLHpLAuNo4 zQo>Gt`2e;&R*g0+%uf|=ga4t2TWQ)WX(=b)io;b3tl*<=qEYUi8YTO4g<3G_K`F8c zZmWS6lImrF&ofw15SiMjVjjbLVV9$-Fd&-@G7-Z%)gqA6q7*QU4l&`11G6JP-g5r~ zHxi!tRxBB@B~IdueaBgQ>Xf3DDie05r|vssX@oY&$;w1Y!kd)V|INhAsIQ>sC}SwR z6oXy?AU~)q$f~H@O!T!(;M^$a)eAlRY{T@DDYT6$Flru4gTPLIBiaw)Zabt;*6DHH`@bFI0Z?JYZy|FP6a8RP zO~V7^tUv_|3e@rXSMcGV=)0@ktL!_Xpq3U}veV~NlIVtBMS#d1aK-ut_W}mw1}{)= z3GWf_L;wTQba{DMj+X+_Ac!&yoGCSsX)PgF5IZ{W)3Nr~_qW$JSDw#7z(Awuo2$VW zVK{A&G(<$<$F47D*sfJPaLfI{C8n89Kjy8mFEqMAIXWApK0IM;d&HBk z>sQ0v%0`4H8nXCho+t#nYHQ(rQjPvNYV(7@e1&2z(@6Ej!j0sTr^svE-*tm;6oE=E zkp>xGBN^~wN4r6L1HW&Cx+>T!W$R7c+0ItP(B5-}zM|4Pcrg~c$T71O(mE_;&{=9s z>O->4f~_g^x4!HNIM?lSk|*O&xZ?yDurQ^VZ^ufrLGe&yCUJ1fP<<+y!P8~`xoxhZ z%hvdTQPx_jF$Nl?^9#EvCfwa2RuG}zhdqCJ4x=YAF+{e>9^Jbjf_T0Q88xM7 z^4?3K9NC`Gn_-Rv4hb)!7H0=s1p@~JBD)0L@ObK5L)2O1Xc{*?`frJtnir}I1Rj>5RO_?Y}`1t||t5V9iqls}C zdrS+ba6sYOx3xclXA@6&Q)@4NpHKdR90>RWHluz{(WkFYf6uV^6w4VA4&_MFSVn_i zsYa8j#u#Ot;&<kKEg(CpyT!`wNn11Rj@kG#_`K) z5azwt&c0a%E;!s&LjRdmR{kWto(ChlXZ@Ee5L|l@!Tv>#3QuL{u-K6wJ(Ugx`V1rS z9Kj3zXW4=guvEPc0vRnBDCM*!grM+YQOT;>XsYsu7{Bw^Om~cy+Uc;T$jOH#p0ie4 zq!@Iryb*W8n7GQF>GKF=j?Y3^WA(n7SuF6YHJ{w*L!qBM5LkZ`7CRS?9FQ1PqQN<> zj$xtvx4iK0VCh~&6I&UD3=uLA(Uc3OEl(zUmR3c`-BT(zl9-OEqhLU7Oy+ZFw#2IE zAu0*BzGgMfik@1zLQ1OoUjM=)l_A{$aaV{O9dR4?POg_Fs5&$drd}X>>tO z)o6iRXs-)tP6PeSg*N>+bV#hxICRV3b_~}wQ3P$~g1XGzre^Ea{We!87PcgqDBaqc z_kO?_$P45J0kCY1Vdp9|_Tp-)rEDrT+FU*?_%C(2Reml-2o&c!1Sc43v`v9zVdaKk zIHp41Uz$;rcI5l1ZM&(iZ6JEKZLACJ`Sgozv)CC5eYX_&4(4oX%2gMETou1pJ#o~M zc!HD--hIx9i^Q1>-B#_HR=}G_DoIW(0&7<|a}amLV{GEjqay>lTo#eiKZ4H--QQTeWalnkzIbJv4-nQ0hoR)#w?yW&AVS9DD=6RpKSH8o!bu6BpYfGbMt#l|yR zcU|DN5olBAAsA%^P}AQW9$Zd`L>T4tIdK7Vo>m>QGhV5#Btid8g#nG>H<7NJy%Ze- z9l#|CEt4FtqdjfK)%ym6aQW$dM05ZsQeOf5xoyt;H`D|CgDt;6jJQC)whKgmIJYOe zs>a)_!B5?VN1A@W)-yfMj*MTn9N+N#uTJi#=TDDr6$n7)#u>SF73iOI`7G#jq-j=I z$FS*RnBt4(!UjwFhX%|dV&&gONnZth$r~Y@w#xmHF>xa}~Of_FRktIVb88Kf} za~_epkkn)<0tjN+rnF3@HJ0m{v6yi9puSq&N_@db)2JuYF4;w+yKd!1HkB+Tu53WM zR?|#B()PoI>arHoq*a%P%$E-#cwfjC32na3K!*Mlp`j#L=-XPyfJlPh%M zI;51iy;&i%_BmEiUa?_hcc0gsABV$U4S`}7bUp`FFZ9b_VT-LE(@hm=Q4Ugvxi#R7 zD_1cR;z@r)5J<&5B8R6yoSD~3mbRaWSAKx#F6ItLM^uqb`xJjUSn`P5RP{SPHA7jV z5(m3O_MAA;vTWKcS^iT9lwkQ&X_QKlRC1I?O<9NysK;d^QDe-2oN}gv+ctZ=`g;WV zxA#=#V)pV=tPS6P|G(8_3w<{djRwG!|D}}Eo=pP24=x}gf=>!Vp^)<|ZQHE$=H!qb zh>j+1FE9J@10DH%bs?WIjd&|wu@0X%|3%5UWaAW8-*LUZwtZ~NgGQi_V z!}5dYO5ADP@Tv9uo@?h0A6O0h0Sr;hq5s<#1A%8AA@51G9loIzVP zQ=N@VHE<+brpD|*&q-DSu!t(N*D&84#5h0nfg4l@RjXp->@JpWOSMhR7klZ4HF->e zr6FeP*<5;IdEh*`6;u>_3d|bj2@jc^eoNwWLSN38S@s>lG4d?mjs}HLLyh=qC3sj>HxrBKDZ%KKZQ+#GANUJE zAt{LTsT`xdEKvRZ`mx*eAF07%7T?%^AGHxHi)p22H11Fn*Jd`68s<<(wTJ3n22n}H z3j7{5kmqw!J}LfZyq+r{#9YxDT2EywUt_iQxix-C|W#r6Io#|8CDUY-I_zlva38TnJbLo2$qj)$_<79ed37*zkdI#Kv3i*vCkD5f2|O8a7o!5=vme z2)kFMK;fSNV$U#75vCsz2${@~UewlJDgWGz3fMxz_D*QiRJFL`yrzOzc@1-19y7KW zkX8RSYAtHim7zE1taTz$Q*?$)I)=p1VkFzt&?3Sig-~6PLxAxUUM=ReZ|ULAF6551%Be^Y4PNsLS#!()G2k` z4+BcUrdQ%Y15Zf*xfKO$$G=IzhvQEH(Z4qRiKTFV`UxOaeEfrsyRX7$5Y~qoZlj|} zAA?nJ$tt;oP3d z7xQ$rK+Tps*>cziaALb42hGz_ignY6->0IVKIbeDe&M+1L948k(9}evaLT1mB3*Rh zJ_V!yIh(am^8pNX!=q$yJVSyWfNz^-gn$XTE-3CaVElU$bZp%TN%&a&clc0RSXFq3 z{_1X;vgGI9hXUMl+xkT6nbx8hC5MlR90xPsfk2H(Ba-T(@oLCI7v!(ylOxXi)$3-?xoVm~7j&t=+u2Hd|Y5ZTw~1*lccgZMN-pvu#_?z5nNB zFX}VX+;d$gj^le|yL~#T9cMbCj0>uCa70dGB`f0R_V!(PPxTL4^@@8KM4~TV4nLb)dAgCsQ zc0T)!FW{FkVq;vW6k1b|$dMP2r1g(O%?UBk+41tPVkLUolz|pifU_s}pXudKlngfL z3N5PuklFr7dPcrjs1O1sw)Eu)omR@RQn_`z@x%V&-1AS3S*o6@Q*fE%(FHzCGNZla zy7<$LEA&6zbZ<)yR{-%gzR=5Qz1#hgg~~!hqRFJ6gcoxzor^uRG`eytIl@j0RipWw zk-GZf)I`s6gp?z*7O$E%&i)l~^~b!LsvWAOjI(K`;E#s=kq0&Q3{vBhfG$;hCMOu0T| ze&<8LgNXor=A@cqKK=gCi5Z#r>@NH)CU+i&V2rc?Zw;9-t(6TPiOL+w!-@X!%O0rD zji`_+!XL)3hw2@2sg-m((&{#T<-$_Lj7n_r-TA}N+YjZ=SLdCqv>>eAnrLf+F|^^& zwJm0QRcKhr%XNH&XapgSek{A~@~JQ832ra&qRmyyN=|=+UiQxMGG$-mLi_QbhqRP$ zaXKRebah&emeCiMnBA9zrF#LS=w(C`p*mxE8GddhDE6N!@4Bl$Q&Vw)SWOOh=ej(@#4^=_vRaR zvukubwBlNeTB&;@Sv&kh-*>u#eY!*)Sf1ZRNZcP)+Ff_OtNPF(ZRTkBp1G{-4Fv7V56#LcW$B%vi?6!VS)r#oT-%fTzln z7-`K@qxNgB-=|sMTXQM+XU&`vd11=-Jm>!x7W0??)}wh5;Yj>Ni}Xea|a`vVu zFT*|k(G6T7GfWH@;&H-b0+YG&;WHdkW-L@~WwjvLXr?8Th~B(7$dpWcEqVfCN#dY! zr2yW>lV;6`QQFeC&Nh@f*SV0Quu@}c548eP$8Y4t8qIa4B(@ae5;C25+ssf<5*g3C z#&*{Ce#hQxg4z5e353C1F}$P7cu;4)sh7H7x)$OrA*k}hCb)irJ1Mk0J+1)6L&Jhz z9&&}W7@ljY_(yXQLt{j|yc1;h3>ASG7cx-{4eET>uo+JyxNWmr;T)IcszgFab! zxg#&B5gjT`D84+J@`3TaPJ16`Uo0#GwGrG;Rj$ zo7Up@JBI!ROuar1i4Fp^9J1Wpi0+<&-LF+(3P-k{3*^yW4mUbR2n>`-2bP2~hOG#M zzmB(|2{I^^A~r~(w10I{E129h$6~*I`Tinj@6hQ0Vjl_32T;w=hXtJA7r>erLn)cs`$g142e6SwfPYPv*ep zf&W5K<$Rc7xfbp}G`?2xZvFQHuT@G`Gx*E0v=aItiYt?fueR7{y27E6B|rF~F)%}a z9p1?vH(L&TV*VvJuD_g{gHEBOOSA&Ztu?PYNWE}d8&@ZBoN76Ubujc*= z8~x!iql)irs=Y9kcPvg7WPK+^uwP2zZr_X7x7S>V&;AX!Vtrpt+wbmIa?zC4u_FRL zIcq2DffxC)%)+&VW zROdlzZ7_Dn(rEvFXkN|Zi2FL8q)Dlw7yXst95Rwf8BmAnaq8XnhhsK-+%aqz4l3^& z^%7u0NZ`u+XZSO^tui`)a41Y@B!R5DO>{Vzi(7X4SLd#T z_?Q=znPOF$yd(Ve>OICVW$B~-6PE~IWz9@`^bm2e5ayFzl;uqVrq6$qLJ-VrKc&<# z;^oi8B`A8y@Yvy1C!qAY41EnLvdhuH40MMqkHjw`uuV5L2ynRwWU zl=?Tl)(n{GrY2@5nWUB&NRxh@$Q}_@h{-!siQ;_}sLYA4MiyT_i(3@5*g!fBI;wm?;*m!41s z3&>OnW>nDtGeBTVYD-{BlP7s)?dH)TybI$8FcN!64s3oJCz!+@`3g$-F*rh&!3Z@+ z#Nq_X+cZ%vDPf1nd09OG1bA%DUZ3IkydK#=QIh}3k4qb`a0}7wmvocL z_L?Atk`$;!2WQ{I*GzWvs;c^7sLAu1ppc$~gnOPjfFT6>&q>j(@$&$%3yaY z^BHu7wU++Tl|__E%oTV$&ZQJgf~{vQKQ3d}!42UwBbvaX(i7Sjp|Hj(4{OF_3X+^3 zWR=F8HJ`pan32`#YKk|QWY;4J^~2JLa^-!43c$q)Ucm8hXTf@_L~!!s(AClP{Nkdo zs8}c&b<3_qXmKsDfSxP!7c`6qr=2K;_^gan8EM>N!t3?X@o))vJT}9Ja#A_SyLVzm zV=ja{2oI)mLY+x8M#o-?dRAA3I&TYE0l^}(+fF5~{h>c>x8g4v16Lp@Z{cJMNN(f+ zPSY3*-+QNdIqrN1n}*?HgfQ8sT)M2pk^KMA*pH8c(cGPEOqJ z{%Bs2eq4*10Ird@77>q*>_=$08)e7Y93O4KXp-8SZ+Q8V&!mD4j&eR!$1W@T0XdUW zE2e(0(}^+2E`E^tt?R#bgT0ObC6EcJk`AXI4&7loPkK>!wbgi#hy{=dx$xg$JCKD3 zyts8p)Rw6sE_-%(`Bkf8d9f@G+4cIz>z-T^n0aTYwoTA=CYjP}=C=6JfL8q381GVa z3{P02`yLI!Pl%pCnXl+!8>-n+lR;uCe5UXC-oj}wO6zP8+7l22{u}6Ik~fAVuK{nD`!?!XiKr@P%!$~*N5*j^hr1bolU0uu86vpcHj_7up5E~v?yr---tNH*2veis;ypZ-jin{-i)+<*UO%JNF zv^JWTN$Qh{ISB9RC;l5cV)^e;IXdieR*rScbK_0qU+7FGpAY1UYsF^go~KdN-t2aj zggQHv!+9_GDj-+UV;=jR!a2G@{kZ;2_U9w*Cjhh7)XB z6K~N&B^zN{8O`)H<>zo(xRqkc(}8{n1uSkT3UP0z_sh4Kg{{8O+Sb-($1holz9txQ z5JpJKt0;c#{!kZQ8Lx?9pbd}$@pMqSIQ|X^U0%UpZ|iRN#xgU#Z~|$DRQnVQtKBEy zwoc+E9V4ddf~|xYXvz9aW`hy}CqXAyWF^J+(?85& zede{2H1*2;ZQ5^J<07MFU&sZYlUsT}F~RwLT|(|B%aZIoPq2-+%Cylk>X&ie99J4$ zv8&sNN3*)$&4~4q^d`aBCJx!PI|Vb&{Uf|RM5!g3tZ{efNW{Pu7)c={ zCL|>?PnQ))&xi-=DC;fGe#9d2xFqkk7f$TiCmTGB-6%nMX@;by@dqQyxv1a3le?aF8{`R5 zqum;cW+IzncaS;TF~^V2KZy)Em+8B>KVKB&P3UjE&!e9HB`cKCqixP#9cM0^Q`101 z3Vrlb6$RLx+*i)`yF+d(!`ul#Vkx7g!`PdtXoY^_{45?{t>J}H;cRbTQR|ASTGH@f zI=}cI^6iUo8g8M8BtK$VUbu-=P-`i``xzM^MT9*FBIbk+`_Lh&BC4k55L4^5Xdg)J zweHWM!GqEoqlcG3A8#LtQ>ALGUO=WL)-9!Z@jW#f21`G;?DK*IMzX@X`RD9gVR1(MwCCTu*rZ3YU)TFIKaZO7&mGq<9G030OpW5prS9;%z`v(;zlxiuKjzX%zh#{$wV zi>X&4!VD+?2F%o(%fM)(o!Mu^)(<=e;lzw9wZDId0W{OCk{(#;*q!Yp?$hPP{$tB9 zIZch)+LrJhC)Ul;J-OzG^_LocB4cwOk{WTF7=l~erp+wMA&2u>hT|TZMiv(1scd1l zi#OAfj-if+)xaTAZ|K7MMZ7=Fj(d<%5M_0rs~3j6x~8lN;I$5_W#P*m*=EeC9S_D= z@tuG)SU@KZYZ0@uRBr+Bq>pgiQq9f}zfELfv3Lls^k+>|DNpWIsfv27b%Q^vYv6GQ zn!zA^`JgQ1YQlrX*4`QJ7u<-Vi~Sq#$CFFPbilkl-vdXUOBWX0vN)ig9VJCMQrSz^ zt21_I$;7v-e^IV>ulII5yQ;dH!R<&L3sakXvH(4%9okTWPp6LFG-wa+^2by5Bvfn{ zdP50)#zX`S6GsagmcvSgD6O$%pDwNuEqlHQDv&-Wql)+R?zc5lft6uXCKauB&RZId z&>*Van;@s&IG8lUR~(jDMTIGqQ2B?D9-i!*@_NDp#|29vUtN&DG6 zD$Ml{7^Gw1=sbI2VS1JhE^sOQ>Y<0mJK)R1{w*a`2S`2?XfkV(>~F^aM0iZAOo&7N=4uFW1V;rFRI3K%2w#Ae@8F0fKK`T zK;`bhc$C{lcgep}H~H$ZSTryamt}hxcBtl~TR1i2#Po+Ll;^|g;46ml)H{^cQUju< zH03xm6C||I>&Q?|rw2CeWacOByPErbUAN61@kNcvt?gxob&nU~3WL^h>wMdq-@k*h z1YJjpPHF)S`ROv?RAJ-max_P_n97kPt5Vg$%4}#%>sGw97Sg2wdA;A{3|(Je&(~5? zw7=c20B0pIvo4ic!rEjTS!Ex&1=LV<93iYP^Z0=(!o7d)?jCo+mm+`AdcL#@Ku?nH z>^;rtXPAVC+y|x!+S0V7K%bcT$EE6vYx9ZHh)xtSBNgOw8x@b&m8I4qXJ`55J=qRU zVSnyRO2`5W+I%+)9Ve7AQ#ZILGD!wt_@{kI3}UXj3}P^)FB18Do)_BqxLrw^Z{J$t z!PIafbYWCa(@0w@*zYqZqTd~#s&8DGc!prJrzbq{3#1173o}gxKv`?wZ0_rQf7#Ki zW?Tt&e8_ec)GFm`kcwhfx;b9YTHi&r{&9vNTe|YsI1XGKi`)NlA=jr+(4n%<*=?jt zEt*?}%<~zF`;qRRf|6brS(?vYCj?j>Vv;~4F+Li@;jCM9Q$RzHeeEW!?(*NEjoI;k zDGnX>zURytK=pm41ZI739)BR`8Pmhqh>!s>i`Z%H2CJt%nA5!Tij52_{p(z?F%*!53JjJA#QDZ9Tbo$ zV{v)D<+?SBuu)9GvrhYw;~+ zV{a_w>VD2k0) za1&iD&!2n*k7)~jlC8$by+|?pHH%!AKp~Lm{18kHd!-`8?E6U=1aH#$WhDC2?Cli|11a z_G<;*&L@-aTQYw1?&SQ7}Q4zA+f+n`$2Km}w&IR#eVB9X> z1K~t~P%k7HaX<}^1egy7uCH-|&@L}&Nd{wx8S96xE{%a?M{Nqj_z_B_O><`JhV&d0 z99@W25;O=ZVNqDjQ5LtS!SeXeFze;9S|&x&5EbP6i~%WSV_# z(?tR@FGEG2$$cJNeNY)0=XmE>g`cQ!%G|v^j!2CFt0k)MHfBXBmfr)!B)Z1J-+ww@ zQv369{@2NiHYxOoUKbUzXb*ykDUg8xJlS{fk}5rm0i2DkBpx@|6(^K&f$ZQY^ZK6l`sUy4{jBR=h=8-H zr7X0?fOIG}r+*+)^YVVHV6k)F0`vpvS)Eh@qCf>oNzB)lgjO*NP<=s?G~0>BWb?%q z(Gc<3gC;E10?%FLE&gDzB05~zgW6+*3{(!!G&wuq!u z1k$6Hmu6wjr^CW`d_Si4>QDXr&H?k{0SLb2)NY2zAoi0Q{>A>~Kiqs{inymB{JL4J ztdk6Dh~7jA_J=QF?lIRQP_m6pz_uy}Llt4d&B%^yR6NZQt-{QTV61rBD&bUBN-%)Q zb_Gx5fK}F%vzxf{+qcKoVh4oi$gVnFl2wxrl@4Xq zE>>6rV@m{5qIXa^;Rnnh$nfKt*Qg>ITRXZ2XX9zo^|Jc?&qtIvs!-U*%i2a?vFMvs zqB=Z+IG2Jjx(tJLaiX;+?Co*wasjo20g)a@q*z|vD0k?lldZw0p zXz8s6ynE#!o`$RGR2v!ttmOk>A7FdNK*SV7{00JCZ#{^t?a+QeA%?Eo#I(f-1dP@J z0gyXX0^R0vwvz|f! ztmaDrZ!Zr8Ok<_)bmyrBMc;>WEz4p3wsNI7iF$QD8$Wd$K~@xM?RVoPe82OO*O?5n z?j**$kEJhu9c>H?aX35$v06}!vaOxBNba12cYR78sybQjkF9dIt20v zn&=>}>*K?p9{58PysK6Gc$MdhC`eI|IB7^c0*j?nP0vr(dt$?F_#du2n-ru@7}$Bp z2xf}|vOi~01w}Y9wr21b`jus78PfWaTlxgtPWD(e-GBEuP9S|c{-@Hr6zqBv)Y8+- z^kDUUM*YD0I5zyikHY*QkR6xYWbyzKTTw+{;fVx&uq>2v25wl}{gi~S=qpYa(E;EQ zT!Wqc{;ids(7$7-8gTll0~7{ekVhhoj2W5!#C3|VOWmlQAjv<#KIv8As!8WUU@gwWnkT7Zwm>kz`UjX)QJADeeaVHAZWMsL z2k`v=tH5&s6I@+$*v8|lU9KkI?aQfjt+%H?;DtL*A_#WIv$X}Ld6?yzRYu--94K8* zc%*!x9c>!GGwq8~FahyJ+HKSS1qg$GNE>BFK$52X_t8JS3rY1U%-{KGhxTsG#R|dQ z3pbyhw|AlUiomN=R%vHQ(Fh2 zJ|-#4jcx{9&-2dF23yTOac;V3s^{?5enwuu&P0!knlQbQ_SBM?oSYC-4Yd*vg`k*0 z05F`=_nXmhPb=hK0pL~v2B7wnoT>RR>_WsOe{nX;hK%J6{!<_wu_*E;(j|E0v!=JJ_%}f*a@R?IWcWSN(drH1 z|7;v47W+BLdf-zQRcLF5^ov=Mq8xKD8Zz{8EnEC~JOPn9<-`UMwSb zn)GZAIV~nPX+@vl4Sb%=0jU8XJx6YHIg?qU zkA#$y^xqqhR3tY{UTporcz?bGbwH$;BN!=%J7xNItgg6dhJ>?!pp42}+vep5$6#DWY>Yq;s+?u9tcMY1CNvz%wZ$9ureg-N=XV>5g{q`_dn6B>QW%bjy z>l6q351;m@WkRwIBj-B|09F-TIPi4HcG&H?FlZlS;qxvO#s1hYj=J4{wexstifX!* z4P}`Q)3*iZJeO-r5CtPXJ~2i4NC4==fE-FX5`q8Vf~{&=_(9$L^Z|Bkcv1Gx^H%c( z=v8{!1Grs}U_jz%vA@{)zMlbf?&zm0U8ItK0v%9J%B{7umLU&_j$5U=L}LpJgDtm# z6QVyQ#{*&AGgHtNWlHn|(&s92;c}1F*J?a=Tanu|o})Bdvo#WhR|){pfxtmUvM8a^ zLTyqJtm~Gx`74Y0VaGy;0*#0_h>La2QAPJ*%a}}JEOksUC~PQyv1%HF9!MzJ-}qqB zgv2m>zRUDRBMZhQJn}J|NYF6LI%*VN;(B3&rb@2oNyC zj(W+*YwN{R<((OGT1g>OX^0Babom4bVd|cTI&}OeX-&Zi;)t~5x%$8&hBHQr3L38o z;fH8Wr82sGoja$oFe>_w?EbEAI5Xp~Pj$vHx(odW`W5X6NxCsY?FtSgL&Z*t6V9Zd z8zx8Sgdf`CDSMf<*Tu_5UxJcIIFM0T+VM21X4nHZef;62gn{Mq&qc~&lLac!?FJhZ{_Y&;csLyz zjXC4<*k_8#;)@<=Apy1&(LUAPud6~aKKAGj>rSX7o{!4onkNvMg1+#87{aNdQh&hC z_vWPL@Ak{a2lkZD-%zC ztMBa;M(RCpdk|^AU(Vq3IOPL=ElaL#?TR4%B8iTq3YZQdJWh9$nJH%0`39s0qNia8g#L`G6d|Pb%U4Rbqxw(H zrvU!BLZZ^@MnY^(LhR>Q1RNb?-sF%n6Y6n>@hvA5NaIVv%b~ax26eurOCI}h;jjvE0zOG| z2S2mvqFTNL9jqQql)Tq4LNA;mk!Q|N%N3;SVq--qtZ5sDb%CF4!m8+0N6aj?vUWU( zCVN6agw5XzxS{K87~KEK5>Kac2JC!xc#B<3fjY|2{g~11;Ia)4;xc;3l1)tgZd2IW zUpFNMutK2VnX=&_84+9mPB+F3ghxk$M zPuF~GIvOi*n0X~K^A2yF(Y69(R4hm|b+dV^?w#ynP?xh)BC8y!=Y?KpH{Y`%SP;HL zTt%FJ&vYQleIRpXxVU>Ev?2wV9BuR5iq4EAzj11*tiRwA(xr9WcA)S3m4DWOX05#< z5IJUUZ(yU52<0<{D<~S=c%9hTyy?1nO6$bRc^kL58V8<2>*^24=v)_XvW(V28o0s- z^afn>K|Jlry3+d-AjQ|e7Us?}sK*GcA^-0(7AWsMnY?^B9Y%_jOQLVO+VMTSFBJ8E z+xC;{E|d$xuj=&awHz2#>QUuPfRF-vEF2dc7^bns^47$LkDl7!ushpTe+V{3YP4HH zXp7IsQ^lag5p(LmP!jTj3tR?>gR7yz2L7|(&9#kop%9>exor^Q!Q^%Vuu7crx#wit zMChU2~dW;Zn>L@TVp5CHjlk{_y_7MwTh;Numk#@rwnhP`fJXV4x5M z`>_%I<#Rlx!Z~J*nq2};nbHO51|CP$KsBW|7lfY{{6umtc*!R6 z&@z?F;A!dAz14TIeOs!wNaTaz*yS4T?1I70`DX+kx=!cifdxUX3FIwk>{_vFEI`SR;Y$QMh>6-LTCRxFh z5$aDiBjF~H4w9mhXEUdTF(r9-!vnaep|X4H^%q#ysz~W5%H2=w>yUQM`mO)Li@t8n zxdAzVT(qySvjH1h&yN2;dIds_9u57Mv*u3PO=kO6izfCOM(&%fNV#mj0aA zk&QI%d?HW`5q|*C@l@~KY>Ef&&?;^L${(UM)jJ1(Ic4(k{>JBgjTI3v^oxmkQolJW z#lRhG_%y0{wTml4IOSOkcAEWxPFnQ3bmf<-3#oVSb zcefKkQKu=i3w>8e=50;PwYOmTwV;as5+cM&2@eX(!Q08Cw+NoS&#p66!SpOa6L))E zfxNp2b;oYjSnok*42$h>(^4V+ze5YJ^D z8qQ!pzXE(l1vhg>HYBTRchVVFdkZ7ume}87U(bMgk}7*Tvn=i6GzR!}-bW?hw;x`> z&_63J;IY&5qEF4x2Vuka`K4yFMi~iH>y|v10U@_cnLzQ{)EKQ@n)%@?7FblTfBp57 zw`CMIgDUAL7oZE4(97b8Aq+&Hm`@RH|1mesYNu;xI3&l~5pp|1&e`Py;F}P6=XK2maI|o68QQtnj7eL!Mc~5Z<{)os~oX;+cp? zL2L(}j1^sy1i+nEC7mrcnui~`A6$+|lZ0>*F$fZ&01IFGt-S8!La>buN)6D&o~OV9SOv*kJ2uZvCMMK1%6}FpQoSN zU9xIVnUP{_7!foE+iNgBb^D`2{2H%NfbZoh4s~YXzz=uzY7 z1m+2bEA7kJnG(AG7&BG*dZq3N4dO-%iQjBdbrJ6@t@}2=j%`b2k&(%=vm2D+0$T`% zlQENC4=VzSB;9m#2v~w#J{Xy-7rihkzKNr09Ugu9zF&kERhPRCA2=k=YEE1#bKPSvtyS#b32x;g z(fNVyGU^zHFhe{_3OT(5Qg@_O@w)S0Tg7?_nr%f;8pxHAR@YVra&4P z?7G-&LzP5wz?6$c5oF>wNyT;8*{p0h$=k zpB^KznW4c=hA8HIqVNh@?JHPvH_{q@y#9QLL798yf@?DBmVCeDY6&vGsGiM&BRF7Xbw zT>p8al5Qs~FzN5#jrR9EkUh8iL}tn&D-fJQ)X35e!!nI6)Z_kMaf#C7p+!k3j338+ zDYCipaa0X7x8C?bC|;_^hoI|ecY>i8#>c|P%Lg)0y278X)x$j<8)#8My}|4bJr$0z_2u8B%bKApXD{h%&uR}A15H+{K3 z0~FUvz1oPwZZ>O;QK2FZGWo_x6-TElu8X%lxavg#`C_-on}mO^$WdmHPUYh|mY_yK zWguRuoEcfq)ms-%P*BokBd7PWzBrAi-8O|DsKWVb9~q)%`<5;ODHwEJdd(ze z#6fO$GAmbP+8L*22y1dPG?^thUCuxx>Hz7i-_#9lcaC92;h9l8&s>Xr!NcLk4<+hv z&wQ*xoKsIHpVyoGBT)nT$FI+jd3i<5MxR|iE$JaMeJ6lWfW#+aWmF`_EUJ@49LktL zP`dgANc%v{d9oZb8gWGU%)5yY2vkPD=yQbOP+&fts%Ez@Ufy~zyZ>b8Rh`vA9RE$E zomAfpzq$$u>9Re<4}wb+u?h_HN`T9{DayvDh~wb;L}`sdHoKG0Kl1COM6*6X4w3CU z4TTmnu2D!z z=yTWk<$e*F1bE`YS@Dvs3>^#D3WcB3-0>MXNJHaL&GttW|A^!IZ7vh-DY5^{{4wyl zlKkgxY5wnMR^d@L%6ACV2VXM%0L~<|pr!GLU&AZLZulRf3){a~g)VOs0ctnLT6?>8!qG;RC3TTJs zaaEq~5DGq0gR7t#t*7VFQYM4$OXE!APVvsF4enI>>>D)8nBjzw4vj8c%u7IH&15dJVk`>+#1jpyB{=_hyb}PeC%&S%Ct74*r{A zkw~f-d7E--1Z&*vcEMt zEt$r9Go`UOo^{PRZ@oO+lRRm>`8&)_ZOom>=q~XWpv8Zg6nTPNCt$-x$+{_3#Ve7e zXx0?j#4iW?jtbR>L&$EubHm#0e(R&YhRu~d{8xE0E?^Z|9V*lu5?uCP@Q(ibZFQ*4Z>wppL z@!+G_H|FE~<9cIY7vo^x z9Nb#1W&Ero`xcNV})}8;79iufCI6}c8396kOWW|Emjn;s0r&z z9D$b?7<9FNC6j@l=kUU1X+|@mwWcj_WbVzumG#Wm^w6f+i=_8=3G<$_&H-K)%~Wo z%VE~KIrzli6V$U5U)q0KcA&)*Wta!BO5LHcUs;&Z-S(KhICU3#Oaxim$of>i?jh1`e%!x0px^ttuFl{ClMTm=DX z*QddJHA(*&`|jmNr?B!BcxHZu+}9nxy_ZVE7IOAU`SqT2JBZssOn1KlZ!c?Jf^K z-ly0JqLnqUiO{8-ql2Ho|9GZ+&d*3OB*GCl2U9yh^ml4b4kkfOqNj2MD?9SqH?gx` zOAMEajqB8sG^I(&?&m+wdisW4TWQT|fYx?W@M80*IA;*3h=~3BsVqiVEK~ax|BF?K z3aZ19yzR1&w{^pa8-|c1nS=r9D?1cCtbj3}wIMM?p1;gHBVRbNv73O{32L8Ahv^Fl z!FZV6(CAx)8;>w3|Dcz4>z?h8y@>R zv(re5OPq^%(<1rU*|=7jFALlbpkYicCno0B=8FCLd(B!BFATjgEZYQ{&HiGt&@Ja{VY_&@_KLIn8;yRjWGwPs8(j@tk7YTBJ>{ zzBJsDUVm;BC*5hNc4zLqny}k6C4DL3BUH!Uq*ge)$@vflWHUntA|KkBi+uhK4Yivg z!(pFHn$zIe`{iW^d6MXFHe^4_c#OGwuko~8lgCj_YfG+5mEMGX9=o3NiZfXUfNjOq zg74eHKQ$(M&J^{Y^L2tHZK(j9(BjGL{LC_3Lbar#L{gvF+ZQpFXW9SK!u$OCzw~JT z4(yhIr}&~4oW=uy>rGTlYqf?qP)g!1f-5Z%#EcJG&l*=QflKvy_^KJ8CS5&E`F0K{ z-nJSytaUP{5_iRC4SU}ZU!{q4A`i*j`GD931L2UN>YqOId@KGFCJs$h{NF6meE+js z@ByafpiptJEt30qlrS|o!%qfK{qeawV*Z-3mJoS)|GB%)uDzW#AFMs^T#G88?g0@J zB7xh>TlRzYEY0S?**%++OWI6ipDy%675}1fcHHEm{K?DKE(HV@qd$g1Ym0qQSN&aC zXC@`tS}K73d-;>b!n)6b$zR0jO<6fTS1$Ap;pWufFS)$~3p^^A9C2_E2?umRituzc z33`%y6#|Xc77Z^IWe71s!|vk;PO?ICT%b>%sZsIKC}3k4M?;)yXHmXa-FSbRu}IW5 zU<FBVIgwTZChG~AbOol+EZZYixum0qr#;SShgVNERuaw zVX=ijw72L?p-A|tGbO$Q&CW7}9jowff^;AM3|>)vsZH2ddhP}!jpeBmde$Kdm*_U3Ff@?!C!9Ckn*VywIHzK!)%A>kvVM2 zDFlIF6(Oup;L>-bn%&ZBswFvfcW&>mCih{V|46ye>%S~H#cp+UeRw{SzPB05@WW~} z{M*6)u=6AYP_cw|2fr{6SriW+=xM0Fz100qH~ zpTKlVrz#Qp4Hz4YFa6@E8vp%z<{q#9%_D@~v`k3t8w0bRNI&QRerHBzA3v(~Z%avO zbymS`lNG_~*+n2_pC(Fw(IVoGati`^$n7CgSRk{sx%agI+RuG7w0O*7P!5My%Dii3 zR)la}NKxJJ+3X%S2kV^|_MZ%c*kNAd>py$Ul+KBic{Z7H>jmqc{Tw;1y=k*n@!BNg zR%@P6sC(QAQjn_5?3C6Q+L8s+Ke>1`Y8q*8cBwWi9I!aaLV2Q;K` zefsZf;`cX2C~(Fxi>9||=;=BQ&t$n|GFs&WUl!HJJsYjK()&v&X$j-~$%?0v+9Sx{ z$>pIfY==ge$g^xjsQ9fjDJ<-lFTT`TyVPIXMX_hl-uWP_gcVjBv5eN^iw8xyV!R!9 zNUesIQfeG`6HuB|hj#m+qKimn3I^XGa(wb>b&qkaP8|B(qf&uW#o54ah3 ztoSu;4xEFp`*v?A26tteB*&=Nl{Wtr7b3t%y$Kl0Fw%=uMBxpC?DsR#lWDh&Xvg|` zBzTMaKauAu^(&eDv5@QSG9#!Zke4v0Mv_n56iW^N2LE*)5grrySP(riOXi7zi|HxU z=P>jVO7s#+I%Wb2RmgA3YoC*zsOfb@Ep)syO|*T!=tvz~uh3mT6Vu4SkJI{?)60U0 zQ+JPZn)i~Cw^MIJ`Q`K^w(&^f-(ZX}ytruD*z#)!pVuc-VC>(3|g?jrZ4szjI1 zOTWT*Ayi#h!jg(t#{06zm=9Jx13 z&8iwxrcno{^dF|(x_#Xm7@o`uazl2UZv|d~kR)_JY{zAcQ-6`oW@g<&GFzGxnvEfS z&0Kr6G^;!c;s_aj9?hOiWjNT}+~WU&e=~fk{(l^u1AATF7KKmH*tTsow%u55Y+H?O zTa9fuwrw?P+}KX;{_Zcxb8`0CbFDGQc!xNqHrwNKcm?&Dcr?Nc=8xzr<>~cc5Q$Ms z`X2JuC$vW&yq>@LpSS2@p0pbI;Fa!|W{>^dFvpp$$K!%{JYu#Lgw;qYO>5%Uion}{ z@=IC?If2E}W&MzeZZ@b^S-7mX^tAp=Fn+fQaeu#kobIYNXa%&6Oa(Ob8E(r-Ntb5W%}pF zvi`ddMuWgaUt&UCyP8|wP-)YVHUjH8Y@GlN=P@!rP_#G51)my>IGP9k?~kPchY@S7 zI@kkviLg~{-Pim!29PwBLe_?peeoxLW4beLmDdg1Z;rNayhMafDsI^!f(k@g#^ zcczIE(9Xlc>%9zeWMwet5nu@o{?Q`ar5p4Yi_@lM(3v?*Y&tbJAehWsvO|@>vCd14 zDnatx<$G_Ig7`{iz5XH-RPYPU&TN%9qBWG;FPcv9Z9JE%U2q%HqS28b6!+!UllUw0 z{7l>IJZ1A0M60-{->H^!@Vd~}rA;}bmqP^-~fFL)lhGlZ!`X7-;V0UaRR%2G%z zcboS|X^K_)Seh^6tYI*lZrQW=H#yFh7v$xRF{LlYuU-0>k8r(75~lvP_jc%OBkE(0> zWY_&r_w(;^Jtv2%c50T_lyUiYZTAr(4D3j2$_fiR*nZdBe;=@WU}icFQXwus$E;po zU!V4YJ3O$$?->CM{*)q#yt-y2j_JALpLFjOv5IN7!u{BA1AF&LG#mMo;z!#m98wD; zE>7|;IgD~3;KV!>XJl#z_w2suy0Xh??>Ujs%qt4>v^pXf3M2@c&@@a-$~G@ei643W zry@(L;hKH^eO2^zNBWzs&Gpp?foy<;A;fkE3bN-tE>U}2{SX;5+;gkHB_w>;!GCHB zy<$--I5K-e>)|Ya+(J~?LUf&-|G*?#X4ggg&(GW(=7nDebQF|jtM6lDTN~zVz{FU~sgu63<{TR+@?M+KEhjrSJEOMlNC&1|?XzZz4A(bKT#XY*Mf^ z@KwkN+I~G}n|z|Nno(pjt|w&y5?IoIvA#MN?wnzs976s7gEu zUH{d7etphcFzkXbii5Jjc8?9q9SMb#@+BYga9>|tJ>EnFRecC10kr~7JseH8;+am? zjLHfuA?k?P=yV?Q?O*LDo!(X8_iJaNS7+|396?vSOV5>WJD<0geuRL_8w;uk&L}K& ze{vf*XlAgZ4G87P@BSeO+7&+HIPx6lRBT&@7dbR&Qd(#tkd>uJ$7ahVB=X%qeQMVK zM0}h1Twvar%wP`_9N4pU)!q9KDh;^TWW2AV^naXC&mn!8I%#0v-0HCsirAwn;hpSs z{2>(4K!`0rh?4>v1#7?sM}?q-lRo-^9u*id>UVT25qoybZDeO-^Zyf=UFY>M`N+S< z+;^LC9>e!Pmq!SN&-YHM<$HVBv<2g4gHXfK@|IUVE1bp8fvo%5jNW_ zAMAM;dtCx?YIrJccylCLVzHVkXgda~~j$nt8tE_7^k+XHhZ zHl9-SU%&qO_`7jku%YL&GYRf*F7&$QvBI|Ru@z(pa z``>lzT7f&+00=akl_>oi8AJpcOR^xwk}PrL-jfVLYBrojP}s;G zwc$C*(L_-1U)slVBC+^zYKGA2gG^rlamxo zbGxXfwTcWHcGFny&HJ7|%)0YitgTwI`B5mhWbzuYX2GAXN#F2s?sYy^fIQ`?R2i@kU%H5_#`{2?MDS&#rXYEy z1h+&;{IUhAh=>8z*>y66FgqD1FTL?RncRLJ*^3l20h-(m?=hI*e|Z745n%_Z#IZKm zg)N*dH`%&sQj}>4gcLE7=Hoi7<%qOsJAnc@7A>kM+S^~^REgJuFgIoMGV-4^o@J_j>bDefoa{Zxty;r>K_nD5WDw^D z$tMe<{S_k3GpdQGYB!6;A=i6dU-^q!>oJtD*WB6~_TT5|+uG8juNWAvt_Uu#bJSLk zam6Gz@iB*!m@xYwG^37Ghmci~-3Ln1Cy(F<^SaHaC-D*L&{d_WhI#}Z$8)%{x2f`s zZK{>lL2RdHp5o%cKi^9c!15Z<3=+lt_&PdZ6oP;_4dX}g%7Q0j2x7H3cq!p@FDPLV z2aeT0C>3+oAPB{|DzyI6Z2hZ)K>{JIW-M*lC_?D-9Hm4C%#sNwm5F!gZ$6ETrE8Iyd6y$kE(BV|;EIEsCLX;gHn8dm|Wx?P93_dh1gA^20Vdyu{ z2oe%PLdW*Sx-GaXRJ2WHnYlU~+&(Eaf!fCb9UTI0AT^EMb<8<=G$U2JGLY8&VhRZI zA50&$pGTh)3FXoHpY48u=Q)JlSG9uSRFJbq0l;wo{qSP#uzM!K@9_EVF&1S%vc8_l zP?fi$ailMH9ZdEq8K8(8|K-m&)KMwSHj07heiM6CT$Psi!{a>g>FIt7krtE`#Yik;lwAdmScLm-4bMm z)a6*szB&}YC1EYq20s}BjE{A53E~qZ&UqgEkxn0?l?oTwt?-4;HMdAqFp43~@upjL_c9T@ulQEH zt^pD4%i1C$&?#2K;|w(^3}afxUZ}T-MhatcOHU6Xr$wO>q)`Twkv?UjKZpHq&`+xw^SAV8T08P(C5ur{U~?_d=bN5wbEhpz03; ztH8i@=Y&PN&5gP39DkvJuVY52COd#)lQEuN=Bz_Uolp;=@uivZGq*^`qMJ44n1`i0EXjlEAGl=LUWlBOtyF;$iUn!KB7dyfiWLw zd++&EOM`_v+FwEGT2;oq9ddXz-Xr))B|cQ?Bv=Bb0XM0hNsv+FlCme*99)V-4ORJ_ zszTQJ$qV@b^LJ!>9_1RQ=pX>~1RrjDk00%4297f|CGLDCvQlqVwnK78>Vq4K(00aZIVSpFP~H0L&$ zYHGu+*+Ux&&w6`q6D6jv(a#@z`UB&oC}tU8H@NaNp7afi1<)P-cthsGY=t8<)>>%B z89m`_yCBg`5+`w0E;OFMOW2Ajxh0k$t>g?E$?}SZ``i#&YDt|p812W6(QM%#i`xw! zv5ZTtcKWPl|yJkzBP>vC2j-k6f^2Wx->c-}PcYUvs z;7HT2X1htSvLD6TYc%l6tNUMn^@>`~llKvb&1;SDyFi88>~V~Y>`&yCI$arvi)W>q ztc}J83p5VzIzjJap!e5gY^n0VrhQ+L!-Arr5u$IN_Ba0W%LYo9IPzGrVd65+)LO~X9kY!uRDO9_e zoZCw!Hqiv`u0rNgd`2ehHVbR1)0Ty!3F=71jjFdZi@XylM29OR^JB&j-AVb~jQoB+ z%)0(ZEXZGd?Z7e^*S#lIUxp1}BfJ2M^mYGt{cv-kQ*9Eb|Ezdajkt6`Wrkwd=d zSYNQ~kKx)%=R`*b2j+4Af18U{<2XaMwJ>Mfk~|X(BznAB^MGWSd z_(pF3@Ob62HR){*#)d>snt+DyXO0OCXu%Fqb`;YZCJ&hOS{z@)DRhu1VuQ{=;#18@ zdhWf&?+T~(>Zf!$i##nqI-qk%>3=Zb%Nsr1dSk3noQmzIKLSNmz7N%ohRPVCa3WJ? z7|wYv??mDL!{QH*Wa3Upv=A8Q88jKc`WnJA{Rpi?jiFB2 zr9Tm&Ak=2Omm8e|X+=>3rPF83)8$K6w>Pk{jM;#RPsF6(7XAM2188t(=jVo}BP)x1?v+uq5UhQZZpo+tt|rgje^^w1L@#6(5Yt4 zT2qPhq+=RR_HLSWj(_7z@(BuDrH?--{{b>4BJqc<*Z1yOHkFpq)I>@!{}4D=D9}`M zT?Nn4&V)3wS+hhH(uHCIxiGvgJ1T+P`WecBVD@EqffJ!&6vj1k4~AM~FlrdE$nQ0=A!Xw);*NO0WB|I~`mH=4aXR=zRAw3NLudWox;YnLp6>ZRFGR8kA@c832Frt0H3@82nKLcZ-2V-s^b(%i|4g#WR z1JHt3U}u+?Kw6-R#StJnHOCC>nw1RtQ}%x5m{Mpo7d=rjVYJVIMd=W<=oV)f=!$nb8=VXNt$}1*T*64#~@3JtriStw~+1(l@G_Yl>#{;fCqAN5>hre{*qP>r_ zU^Pr)eSR4mKrN*CiFJ>Yfl88qRrH!mjy!>D==_Ihm8aH^Fgpt6^K0c;Fh&fF>g<9* z_%*H5WOVbgYP}Cr#f|tcm1Nmk&HTi=(m;isvy;oN5f9tom?;JRDJQ;TJ%U1#w`cEw zHIu*-o6r-N&^7N&tPK~op#!-zhl@YPzZ<$j&Z}(!Wu2Oop(nSnCS+1yd5-KWbRc!w z@v8jD#IO+!^EV6);vu9c|HSF>q+V81jQG;1v$#!JXK^b=Ila?caJptHdj=}WQ*3%W zIxBgWgl|dO4+e#fFlXY8?qiTim*c4UH#|Gk4N5vj;@_9xam7K2SepqDe9|BB!}+j? zqBba6^oMCkBA0p zKVEA8>VNk432< zM!vlbej}K8G-cUh5jJoY5r5%*mneI!hb(f%lDdDE5?oM4es4< zC8+n;asQeRX-yvWk5YvwTOl-K@btc&r39q$pcJG!O80?(*)b$pav>XAw0R8)qM=1g z-)YJ8m zMFOa^HWHmeFhnmbuLS*6yq_ZP*x-T*l85)}jJ>W}do9GFzG_6X&jGp#sj;Y>#-8J{ zZcUg+st%?INCRh2KSOBRgt)1m5-%7@N;NRUh@hYmXLO5kMGaA8xG^LO594oCSfDv# zc#$M{TFLVnxM#?)Y9^`DnkV$4cTmuKBgXaA&JMWNo##ApYb!)y5CJwW>f3nj z2iwQ}=baF-j6g20E8L~;N(UI|(f37<77Yp)@c)gC*0Ero6@9>z3$SyLu2II!$|C4A z%vC^?!U-8yld%&NrONbdhe-v~pJnu43~D^4;g#C#fYW)ixi;lcYDzd&IZaMpvZvR@ zOz3@|s~PA=&y@!hC-8_MZ%U)+L;@PwRYO$`WJWSJBsSM%Pyr-fWxp7rJb`*41gKxvB|*3PFEVtCjQp6^_98_+I^!Fdj(7xf_nl z2%d)twa1Hv^sl#zhqF0HZ?Gp8s^bUZ3L}gX*B*8aX#6*{k^;x%dcYE&e=4t3o5z#o zWW>m@nwd1Dt7|(a^5#n?=(26kf2pt`e+{M;R61Os{a(}u8rJioNQyXTHPR-TLM(3P zFA@y4L;qrGcROd1(a>m&Pz{^K+1tR4hT{<2R|mv=%QCtUXdTg_-7csW%dkaO5x<}U zXT6RNFJQclY<&>9xw+K=n*7?2?kq*Bbl_>)+#J}NX2McHbUs=X;Ed6Dh7>m%;DW|u zY7#0eU$aB6q}FhIV)(^{7zeAy#EEOHd^U)LwxvqdDC-VGhMswFl>b!4XLh3-4Mo9L z5G^hNV|#%k){(fZL#5Q-17Y~nOe38P<$$O_qSQis+FLLQ{Kqu^T-P6b(;n=jlyKE* zGD*p*!J-6C7zc2S)Rn-tgr?P$0Td8B1P7VMAg_cZX- zkKJ9zCXyoIS#^`kGP{Czin&)mj=HeAQEJo}6O9`VIeCiL;ic;8Z^jW595?=p{BVj& za!#>;=aQT?uyWeo7t|}lvWGIO@YFNKXpUg#ymaHf+5|@!H`Z7GR>+(vF{a zRgqY!-KXI6uR)0>J-aXEO)*2XL=6jK(qWoc+9c!LvU}X02@`YSeex>S)V4)n=m%0> z9%P*`NQ(&0Q+7jf!}-z!eU3weMrWMR?9NrI!4g;*?|~KCg<*ych{!c2JIIYS&|bHa zp*%c3)(d4A2<95Qn?rx!*hrr#DNBj`vkllnm$4om-0C~1{f&9E@|PXzaRwLr*H02~ zDaXDQX)~z%d+9bsT<4>$neDIT#yRsSLj&)Lj`U6xWZs|O6 zgPff*ns7DyU4mzKc54Nq0WR8ZN$FVbg?Dg;mU6p=;=>2IF<%8CtnP^r2BCx@ zJok{VMOvkUib3WdEaFh+ja*|w%|YR6DoXdxB!NV+5PIq#aVESGvlTz$>GVbs3l7*l z)JtN~q?7&dzo1Z-(SQw!bffE<6~I$f36Ah>UB?<{@TB7cTMz7uwxeH^M-z$VK)W}i zvlO4&N&#=xq*u+XRpd~`ho6;b#1bRqu#K{uVj6)ut>OW%1jSsUr#?urnz$07N#>q3 zd@tWuZ+u0a(N8lttnoZv;xycgj#K+m;Gg929qW!Oy- zq<5W%zBHwk~FkAyt2)AOiq{2v7o5)wTHy7-kPgD3pulz|+E;VN^0* zob>)KuBlj}dUk$cVE)~Iba@%`?Bd)|Gp@d&I;tcAt^kAuuLvs{ZP#)NF748E3Zt1Z zlj@c|&+e8rCyhq~%FE$dNKd-kQgXK>=X0RIk^5EpbNc2nI~4*>WUG7R;r?qwz}GZy z)zVH3w9w?79fVCZ8lG@7bkL4%p9fdbgM`Y@#sx{TG5z#0We^`(cc_e?RAk9qf(WTx}3e1Y2-Z^dSuC z&clZN$j0`ETUBi>F>h(rrqAQ%@7Ift?`uDO&W@g6RSo0#j{Fe&e}3aK`K4Hd9S|aeo8EJ>vpa) z@-*Vislw9M43W$PYRbulY=-fE*cgkrU44^f@(X8Hwek!EYA!SL-Yi39=0PSR2rSp2t});a)*W3v zp;_118g*XH20v>r%m*A>8nL=Yk{f@J`C`$ncJs+1X{xX)XKS_58Cq?F?=L6RraOL^XzB{*7V*`PY=+mY7?u zxzQj8gnpG(TZ~q+-2#B{WWbNNmwF_CObP{f$Cb&yo9xwQ0RU(zzhGZKf4>*>&pYW| zUZS|`U+z8701o5-Xzvmvyi6!VMY3K@2*l#(@Zr(@p)tJY{Fz~fvusOJhMco-7Ca;t zyay2m(?2|iXB7w-5`QOQ>G_O#k9LxCu4+thBc+iy!g-@Z>d_+RW*;RpeCgjQkkI#~ zGB{;P98uVn17X=6lYNmXPg!zjzI#$P6h5)h8yfrtfCoHVZ--pO`0{_;w|)Lwo>?97$;6?iPSt`Hq-MGjHDZJlm+BMon0MvFx7O*3;x~N9i>w$1F z&fO+z1b;$9Qva9J)mmrB7iaaGwR&Z|h8`JDI(haZ;Fd)HWcY&53Y>W`AfRZPobIcJ zjRe!ITZhy7Y{uQDZT_^gL#+m4G&MBSu+-fDSwwBQiN-Z8DoTRm&`>Bec~NphfK?(` zO=9UZG`Dj<4sdRE`I$fc3Q=>*y*|I6&)-WLdeaMmg;gBV2_)9=yIc23;rL}qG4U3o z@D|n}y}VvmH`n?u^d3&rEojb7*kmMv{u@t?9YH51vCo|%+$^afvz0%u)Kk9dN>mL` zCi`e-az#HMv%!bTnJ5zc8Ds3pzzS;MKZO2j$m=4)P9109T=VrMPQ>At_~}tu`2giU zHjNe>)+abVGz<0$=!LbQgo;(W^jpp_yqyoJ-OqK6`I-#UUw8@ETaR7X;=zxduL5$@ zN}J1<%b! zv^xKGQ4$mF;uS&~5*H<6x^ocIDQS5iSJn*60!`c<0W3#Gpf+8W#7ulWXX+kpZcp;{ ziF`?w1+l4V;=%y|oMd4gK-=Qng0bTH?cMf6&2GF{loSS4Mp%G6*_MZdS2V2 z;iNf-NDQWc8_J|Zz|>wt3aI>!6Y=xE2RL(H3Peitde}3o>0h)L4D8LJkC=b-K0F{j zJW{W2=NbssqGpC~cIg5|7_hg`;2nH?-0mOz|JL^XOUc!0pexuW4An~MaS%O!D*7Q8 zg9YqoHE?u>s%XBo*oKO*_VPS-5UPej-L4^YRfu-Y(%O}2C;8P+?DbBMQLW{-$yMvd zVp3BX`W6B2e11SP2p$q;BgXsP6yNvQb?I}JGQ#I~mLnAiN7jF;cgy>qe|Xz(+Y11{ zV|ED!71k*zEEFA?VA{!m%0YF-)03H>gAuU7l!i0F-R&x-5nK7bsKVwz;N_j@rAsh0 z$hRFERE^mE=f_S_g>CJ>{L=hjmf-^PUJ-8*@7-BVb#*ApX-eT6@iz*jOloMKRygtG zZTEm>5wln{@l`wq?T7(j4XQL-6JZQs!|i`AWL?dtb>FSxKLS%D4B6*cEPcw1vA2%y zQaml^n0K*e9IOFu$7iJuU4mWT(U3BP$Bn`g-0D@a*>-BawZT`RizvM6Hv!(-A7LF2ECZRJJnTUA$GY9Y0n2PH-- zbnw>>El{t*0n%`yM!DHj(;z~#U3Sq-hzLEOSf_V1GIuoa5wc~bV;z}*FA zMEkP%E~Euy`(rmYt9a*W&L!`BDL6>)CNXViFhZg8Gj1ymn)s*E z^d&tvHNN`4$JJk#tUUe^tzUUUlaEnLDvw~2x&;0J-;T!)v0xlSx-d6;uhPCKn^5|jX=rHnVNihIN%xdc5q@0WjCcgMmC6VEki-UX_dqfRZ zRU#Z`Rp~GG?2F0gh(cdacWb%=y#0~d&%{=C8z!Vzdw?wjk-g99_iKUeTY(YZW#BA< zO#HS3&6+i(K~+hY5y2EsBPO9qiAFzSo^mp_C8VAe<4r4QbI(C)R6kqfvC{%wFh&mT zU_p-qtsBPEVzTNuMacHK&#<{2elkd!b*mXCF@KoxmGxH63bMOevIo#HK=@Q2Jr_6#F5J|o;dBX|K9;`OcJtu07R zCjDXHu4x2p84d)oMU~?2*GTtvc4XE9`p4vydhTt#z}DPNKf$DtBp~|>Y8*|i;T2R* zMOj9fQQ=ZQzPjf#7>uAj8=87n%8JBhK;~^KI-QRqV3Yp{zfyPONEuX3oX#o;F7AGO z+>Qbs(p-<3ZwA~1M7-6Yowk{-FQ0$)-{b)BKo>Fb=hMiL^M*{-@BM;!j2$vkIgK!Q z#0by`303e42*Oj7rg-RVW zTs9>X?^ZmmuHF2IBWau&=}h~iuEr!@iWXt_p4nn zicWNJK={qOAypg`%#R_(WaInIWu1L6+UMTv24HPDYtV$VQrHWYa^%N`522ym)R{_~ zw59zKk-fFfmy0e|EoivDihV0F|5WYN!pI>jmDaQ5MWQo~XC-8-KVh2s`^N^>l%q+m z<0&(#mzGBPVrxZVrK?Y4JAuO%dFB8swbxOdI>xfd6sN;0X|v)o5x8&&Zp!5?qr;<^ z3+wl_;;Q=IpV{gP*y_rxie5T&Kza@`MM=UcgE zl~c_+W*k`kbTpG~v+;+CyOk3EDRc02d`vC4pJaP+A5ZV;QzcIsxgTrfw`!o?h$-}^ zliU}}KQ0Rk*XHh9Ku0QFmLf{N@<{l&j9m=Bj}aON8#W9HGi^&2R9}H%o+t$*Y_n@W z&D|>b6+zMof>M4_hFeO+aehU%oisQ=G{tOM8F=v^q*naZyZ(YxxC;Z|x(0SptWS-z zd7KfKSBC#f+O4lcK?M<2=pn!Br8sHdy>?*=7#fiHp+zB_>jL;ZqOrIlttDL)RMFt) zzXbc<+L@e4w;9~w`E@H%Lq^35W(yoZz#Xtt&|igeb?%PF8(RYw!kUhYekq}sISmXc zuGZjyNV{&>*BW@5*qf~5z=%?^-&$?8t4^HnNYat1&J@ae*_bKGeXa3Jru2^<4N-0u z?|VRM+Z@WG*wsaXxUuS4TKe!W3OL;*s~&!-}#Vi5V=^ zEqeqr7R9m~!1UrEHv1gI1Wb)q{0J%P$H>Wd-#J8P3~qXl}9m z8wN}(%Ai3EGJRs=@bjvy5IE5lWHvTc#?H=!Em^#}h|Dh^*ntXHNf;U0i(kzaa9dj4 z9S+7*10y3ag0jx2cX@NCShHs8wI%_g4km!I{gR{@zzt&C#o#DbnNt92>f;OrvkZOC zIZC45!7)!E-Tk9NpO2H=A)rzw^;hXeMMW6bTN2k!D85~<{@W0=!%XRA$g`6JOyX)3jcuS(Uef%i zc$c4jRj9_d?3MKTWKU#emD^>7!76_@aip$s>OP~>6oULCud#ZSbkfoAIGl0K! z1@*^m!**mV`|8GN$7H; zDXLgMg%Y|TTIU(!=e1#8)%DzbaYW-FVp*Qz?saG1^ch683rA|i5`XuioZ!B$8*TZe z^6$XkC&HqB$A^U-#e}hjp=G^fd9>;Q+90$h!msz78g zBQH&??JuE;3;D!OxgMq`k!@pWxHdaAyxp&M2py<1=A|F&_qmNnLlGOA{Nzo}Bpkgq zS0)dT8zuEnNKISV*Z!D*M}3MjS<82eE>*G+Av*rMli`2mnUPe#7a>`^2X|eNR*XYY z!CA5492knic09N-J^MzLu1K}Iu|1M<&iJF#1s0o053*6owQ-H1*ybHL4u;qI-d6(b zdJg_(`a--d<4B!#frX1n1T{V9cRr9HSn9le(SozBq3Gg;5X@DuQi0uU&Y(@T^>4cEyjQ&*;~SSB8e9)C_INP7^5BtbVdUf9iZjkiMmN z=O0HH22UD1c=#k1Wv7B19`+?u$SAG)2A!MVSa_KP1KuIeHZ}O}81Bft)JID{u+-J_ z?eV|&FdruGDrvzv9cuz4adNekf#G2)fMXN1n-|v%+zOnhz5;WNge+Rh*v7`rfS<^i z8^?eJr$ER-js#HGkeoXi;Js38#Glths)E77gXBp}JuCrO7hr()?L1Mi=f&!6YU%2V z>3N&z89WmDd^QegUmKOV>0&!d3_ zCIingg)2^yNj15b6BO(LNWVS+wgq($%C7xH2Qb00LdpYz+P4Ql9f>>7v9+4RZKlKZFgY`T`eZqe{3w;XhN-+PNtABX|pg>Q+Kk4@OL;LBK-z(QA6wqC_ za#MmM#@2?ChbeiEMMmM{Zn$hD+Z$#qu0*@5+M)~o#ps?Xa$ABNGpvcNo~wZ6f;YFhbkryKI0X4tu;v-Btl#` z2eUT^Q#glHs+ijk)Dt=wIitS^*yQ~;uI9=L%fF`=eK#W4;4yG>mTywABHk66o1!)B z+4%Ek&|3dhrxPe=5^$5I0sYr*_Z#R@1*Y*DB-J=gED*h1fmB)6I028?dr|Oi^^a;m zrO=t>te|4~XU3%LK8ndVCbkS>OPaemZD3d^8X+J_RX8MsPvZ{=L(@>%?{(l#M)6=$ zpFSYYFc#^u5DuAleQHjPOSn~1VOk=onz5OSkx3bF#7z%sr&mW9QBBSFuxhQzc^b2W zOg0&I1c+@bF`-jufi}(*+L2q8RHsF14i(ljL*T}egbm9UGPW^_?5YyFT;cV@4oZd& z@3?;)23$c9U0Wy$1^^$QibIRT86i~6#@-%qO~w>eLrBPTvX!CugY`D3s-=McA~5Ye z>5&kn)e_ZS+7BOO4jR<1D2FXF2&4&10`DD&^G58sll#OHx>pqn0V1YfM%@0~&<&rj~9hvG&8I6$1 zQ<6i&3HJYvl$oCd3M{HhTfBy{7#s;PQ-NC36pX;w=*7_?h{K(BX3Ls7bPlG;^48^| z?R>d0l`J-0JzoeA@Gu(q_-0GA(ry#XaKnOgl~3+(GxoL=j%f3i;@ zbi+Q=a~ehe{x2XqAoMmcVpdiq@U#>3bSw1f3yk>yX*3<{el-aqXML~=W2pr}2G@1a2gh(Bh zQLIM0WhfZc@dHR%m}ypSlWK}~7;I7laW11}R%BNHz)XSgrsVqQ$VVfnU7UItmFGHG z#1hp}0@}?!73Hc>)Mlh{?11EVC64t%+Kn{3?r^9x?!5#Hz^)ta(YhcRRGiGX^>hl zpyRR|f8nIbp$fw<*|&$8q=HNM6U3sN$yYJnr+LV1IJD+OF}?SYrDaHP>EyX>sWj&! zl*MSW#hr@K>|Yh9sMW&aSLK8-Iu}0l>@r4>?06VK7c7^j$fqWpS5=E*jS%{85a~E` zX&Vjh6I!nNyhu+Sh6HWou}X+#^0Gi-u*QhCJwcVTAZC06D+Y_?#@V67I??hSMUM{zx#)WqUKDa;UdfiNU zwgcpiBA02|Q5_hBfU%rHyh1qU79ZJ?e?AE%AA#gMv@XG5B2pTuD^;ia4i3wA1{5v0 zdcg8bwDQ9X3LpRU3ng>ZB;6Jp`BHg&(=t-5KMDd1i;CV<;ztM=v@rz@^A8Mo?k;E= z6!86hjiDwxtGA=ix7Q~?G>ing>4f64fT^OjchCiF8y?6#PLSU~037-g-S@=r({Hc; z*}1I@l|5b5TB2cIi6^uI2LWBtfsF|1pIpJT@?4*n97Sqw#2Ts9rwErugzii}&a|h8 zahYf`-3c0!4>!tc$Ke`w-$N{>t|h_EJkpo+TwDU}QlizPNECIEZ0m(T6@EyLEBR z&90qirb6L>iFZl!fF2CQ$H(_{+Vf%k$9YDeT5bK^S3g_B3qnSo6DLpee(sWRSYA59 z0}LbeTCMS(S2j!OUn9h7#FB!-7n!o7?Iw;52-Eo__^${XF12QJgq*ixLHk~mUj((L z0xK_62|Ui<8d^MLU|%&NZkN~Y!AyR!`NK`30zE_Kin*da+iBn#;ST%iHRU{W6xcH9 z?^_o{>9}1LiB{7U;#F|4x}iufv~7cjl&0cGB0N5_NnN9(rj)Tw`U?wKJ4I7p9r2Y2 znzvDpSJznMtZcV~n7B-*N;Y=gU02Xpy&zakSMT~-s$#G>*Rzuvz7}%u^U;S zhzTFS{Gx*de{g?vIG8uJv9s~#Q2*-L7F%68KbM#-mfYa*GYzc|RK|&Ha`OA%TGad6 zvh0iIL>QEn=wi3B>P+dyGX}tdj>~<{$Q_dN+j&|;&3$OXzZUw4`xMao{H7PB|GcC> zko$3+Yh-S&DU!isrPG8ArYvd^VJgMod@N`4JAhwu2ZoFmigG8`fB+gpy8c&G^*?&? z0b@6r3DPg=N=nkCScfRf0T+VZvJYao50Y6ztOX zvBP1*ww1E_^!GWv{xh85T|V+|f)tT9E>?ST**F37y=(2t!V1><_rAx$Gqq>3qu^VT zBiF6OMZ?*Mtn71}NMZ_ewA=I*RoPE7!yuZekaIVsvQLr3OkTe<-!wYI)Smo1{G>aP zBQkO_3o@GI*p3Pe*65tsF|-4`HFbR6#~ZJb!pGlxTzm-!bF>)P4o3d~TM^Ks6U#u6 zDME1?o)a86acQDEc?5R*EbmiQRS4(V_k~1$W2i!}l}8CQ3$B&fU}~*WGX8H`Tjc|j zFp`#Xgxm>R>CO1{!U}T!YGK;nJgCh#{+;;D#|w$5ZrtYE?3j&(CXH>{`1pvVzZ3$L zFwQhu?6Hfej?fy5GYumBP8*|xrhmyIk_VZuu4)WF=)!RJt zJKvw%$HzQNt2(`fNl9@q7021WzZ2&l1SCW8HfdDC$FZXHv{vCPXP$$Q^Jb<-AG#*h zhJ*fM2|C+wgqBn)@9?hC?1;2Vv&ODS826S&j-RkIYXZmY_`lhN(nPUk{ZSzmASq@h z;>D%@rjN_)p*8w1c#*0dn)jpn%MC5*Ih?{@u?yX*dYpd0?6u(Fec%S~_3rKs6Y|FX z(CRY;+8=y$<(aIlPghruAr>KK#g_1ks!B}3D=RAIU+t=qE-77k352)k!s*~m)EXZw zm3w6PB5ubWaDN_EH$h^2t6pBR$A_Xy;>j$5;tnrBB`0$nmm$3cmkG`&i_#5)&9Yu6 zYsFls3`&IFe0QR!SK?BDI3oM$Q(JOa)zo|`veI!%8j5?UF;aLdU)`Lzd}pX|Ai~@I zR00w<3+$>8l#y3@a3%9dEmiXc4s$DjyNduw8P6l`j5id!U`#zsG@# zr1-!=kIL|+6$dg+6sa&NaaF~t)8VI;DPO||iH&YsG=OPVwz2VM?o7Msw)8BPZyv(h zgJEjN^4!xFmOWkVPfqat4szc3aSH{nAY`3u?qC>O2I(m7ed*>E_IA9xWQ%9l_i5l~yVlHG#+zXw> zbtn!QAxt8ZO4Va?IiLiQ1QcCIl!M4l+WL6(qdM`n#IZ&iv%;b;uL87oGqu3EXViz72Ty(uwF4_ zAJm?k%AQ!h%Quh_!qs%)3bu132=Z%nv?LCql-Bl-k>MAr<>g^+*MArQULybiU(Q*C zPoZZ4F%+=Suv!uW8wsjOjEl+-@o65yIB~3!#j)g{s(9FbUvPAo>9puqM${jN_w2`P z)8WI#zyr^JWkID+AHo=d8Gx}}X_Xy7GG>K5PKE5u;>Ei_JwT~7Z$Zqt@d=Qx2-z$CK!a%wNU zREc6t)4`+c_<)DRsMM!d5e^Rd5Im1rR`fX8D$3@(e&p%NtB{c8&9Vr=2kA-dq8h{c zcj3_oFOL6>W$U_7D3Wi>8@l4(libl_x$09}?yGj^eYN|}x6eDpw+H)o&X0@B4_Yj$NBc^C#)({V@2O&b{L4``TV3tJw$`&ITlvwsLmCTivBr&SHxln;Sq zl<4^A^mHgYV$&_P2+|P0T_sC#AJH5n%z7~7iqhTJFcx3=!J>XBjUVFRM&&!-QZly@ z3f(6O1prAgh2IzLmC$Icl5c&)-NZe2m%g|_T1WGS-w`QFB4*5_U|<&+@lJW5s(=EQ%GUnE8+kE;U4gzaAF}n06wshu@9lOHTj*_fmY#H#meD@ zk8z<=Gmy+~rxj7vq`$ZL>3E;CF+YO(9Dks%W?aQ*Tm( zI`=An^B<>~$mB)WDFpH961gK(?FT4mRY8P7Lbn$U^YON37cWiY;3n5MfD2zs=3 zFdb8^&w9d+Io8yt3PZu;V-ER0&y#xCEnFa4*l#d@lhYbhTC`~L|5R3l0gJU-fj1VY zAX3E~!H|lZGO@xrh!z4GM(7SOqx*Qb%#Hw9gl)9Q;@P6VxtpyE;ZWfDjz+W^Ap|wb z1|l&Qn*k_O-o2=4wLgaK0pHAD;_fSCkCl$CkRyOV4gK&~;WqVkW#`E6BX^hZ(u42g z?%U-cKwi7t`Hz)j!~4xYQcT*#{pL3(l<))^zP|xkJeDxJZO_$~po3wSss_C8A#hebJ6P{kzv_ZcZ#-Q-?FqG^Cyl8c#w0l?TKqzZ1bNM)U8}4r zimF%xQVZ)$Fpau_myg=JY=IVsmu1fikWz#844bp?zqTOU`x>Fg<8wm+)CmA8#kbwT zLnz>_5|LD@Sg>;Iuw`t^KWxF@FD&LnUmGojhmTQTwr$(CZQFff+qP{dZEV|4W1CIVq~GrQ&E!w+{K?F|=i1kKuC zO?8}5=I*t#jN4wnOFAvl$MotCT~ccwJQVHzLjL7X(@i+CPRQ9GCJHr)8di#+>(T8f zaHbN!5HCu9PGb-6)g1oTRa)iz!*RTa_|y3(&p7q%VRT51y;cx8!%d8c9CkXufeQX)eB+ zMaM%k{vbcV@xF--w_`7{kN`t&k`@m`GI=sG{d@SA9~jU=L%e(c^533EgAV~O)o)X!0^KJ*U&hca+$xk)AjN?`LLxA$gFPD$VQcB_! zeUB^>_Zp`(z5m0TqF$rc!3YvTu{gXAizl9LAap6hySD%$eerUzi%3k45(q(U99VVt zDN{7*P4-~O(c`O{%i{;f)cZN|dm`Z7@gJsV40wq3wt%GOv6KXC~vk0QPuX$c63x1Ad5 zaw9!LRAytwM{)rG2gO3zAn`1O2`&%Al$@)pO@f#&W_}Kcmv(m8aS_;YlGyQ5xc7@= zp7T$KrqI`A3@}w24lK2rdXh$lYcdiu#Ap8Zw zQ>j+GYy^?Nji$bHG1C5 z8(N*k{QmvN{fwKH%VFIA>lD`Pk`1D=wo9QfiWcJ>kl%RJ7&zBR>iv0aVrUgL{1o(+BbLu_x-_TOjX0_szi?;qC z+V7&En1!&z$5K-t2NG1~*pz}ezkDbqQS*YGJQ5d}_m01JWwOZBmu=*+=lTkruZtQ? z#v5hL_CBxPj?@@^8tpUu*9{BkV^he~mfyQy?v;Ge{w<}$fon_Pu2M5o;CitQ%&dG+ zqI@$8b7qKm)!93|3TcYIaX%`)*93Afj8t1AL9gg>iMSg|(St($*nw;MIyM=j)AinV zm>g?5;X!3gE?z&&JWzo=00JxufZ?Y}kkHJ5eb|`0-`F8^-#8NJAv5)JLoB(+rs4#I zX*8;Zf1j9{5rh@Sqrh+TKORi#GBSw+F^30!2mxY)s?q8<^5Ci+uy zri(U(&E~AOW6fw$O)1khNHOwBCU5)UxW0RS{+3nC19hg1g7P?OwM?)$VK@rtz(~y- zeCv(7it$e22_AmI-1tML_e4WdZae1nSN3ut2`UbDoB=vKOKrJu1w-an*)`802%ChA z)+Bb4TP5$_BY=K9_!wKuN62*T=OH{r%yHAxF}kXB6JUuiY`KWHt{KS7HPcXXzKpD~ zgC=kyrZw16Q9(@Fl5%a5sS-#u>$heG0SEGgBO!cSo=*!>EZS0!_e%avE#(0RiUy$`5flMuO7qk=;?~mjQ&$pAOcO2 z_N7amgphD1-~KrFxu4G<9Go(IXYD?Y|6$mM0|5e*$k!M?({^8IGz|8=YyE}$mk+%g z1MbYfr~3X711MUS>1@cvW^mr0)djL7K6U7OevrTI2WdbhlRAyK0wz8R$)LCT6;j*e z@pQaP6*$M*_446MES^6!H^%HQ8Zmh+XN0mi9(?yTcP^%vnZeicdE$VV8??(<&aY6d zzjrkN?@r*c`lHMKe-Qtgz8AdTA+LW~7!A5}lhR&_j-(gJ4SAKg9dzFj+C^-B*1feg zvKl2vgo!BTX5+;RtLZWD2kKwARRC*rxfn*seNz}PUvL{Za>*e?QDbf>(LPc0goy(< z%!K?AU)C|xddNS3JDQ)cNGNeS_i!H(^O7l8uP9l3Fo z6j+&8@>5)tg@6czF~PtuBH`DvBDGr$Hpt1rhhYPy}Dd1(*VthXv7hem?K3Ofhc&(kGLEw^1~=g-k2 zfVB&i4;1+B0WsiyGNS;9g7+?^{Su-N?3>T03YYUmi(%r{lbczj3-9=HSimzEXoi@E zNsPATUnV@1EDCDvFl*WllAK-%&2hyppP`C~Y66*SJ3%EE9%^dA9o*;>9^pY^@Lu6hW8?(!B@ZASHOKDSpU`b9nh}-W%sH6 z-28p*0eY(=7XQc#N7!$zs&0((Z_n4}fv-n0+K%pift?cZ zF`dv$IrDyfiXeyhBloTiZR*a70#<(+eYZNW7!hPDrGi0^wY&k~4))$D{u8LmFy+P{ z^htP*L-iIAMG#&giKBl6NRV2k8qT203d^Zq$tzsnc&HQoU7RnI*Q*c)7K4=1KlqD$ z30dBBJALmjliqo2Yr`A|>HGQK7_0D+L>}6tqkR+$ML{wRR0gV9Qf>UwP0C&}9sXj= zwk5!i2rDMLC(^^iwb<3%KJZ9{R~JJT+Ag&cL_QiBPtEBmUVLwfgfa2R_ckQJnOAL6 z!Dn}V%UtD=m95FtzJ5zgI&(>kY2f#6st|-$#P^zXHZqDn)?Jn0!2>RB0t?TY?2pRCyf6@wmbjmcg$wA@GH&im`22*mY~jI#Z<3S3B+0|yW+xp>4<=j0hXJcDMUu{{(zGO` zgK6S1Evii-z_%b-&tuUa&%nQ#a!9T9CjbnA3cvGDg^(KpuI=uUGq&TmjQ)IT8yN-Y z`{}d;0OdWVuZNJ|aRGnzfRd&gkry4%W_e}iGPht7B&#F!ezZLlwHHnBhWIvLKa}vA zBFTp)ZnPKa87gAQdHSB~&2L_L(&v|nC#CwL)Hk?@nBs+%^|eVX$ZFG+Y8*70VDkE5 z>mZNHA%#&x-C`?5g+kaerD)(Rt9NN26_QKIW9RsxJmoDoygwbZEYtq77_2g96g^8Rc%g5)b6Oa*S`<%?Bz1m}1icqUu7+?WO_u7l z{E_dryk{k_9WP>PT)MT=lSW)5!56sVq4N+;&lg|`!&UQ}faqiKXELKV6u9~`8@_g~ z9YP1(9Vg^8fei0I^UQf6Sd+t7LAfaZjWF9^U(4&XG4V5=6bVL&~!tt|(;_sh*SXDLpH zN-dfsoriO&N^^yInoF3g&xUz{{C)mMuGM}RQ#S@v2e5N$&iDfPCzWSQyUJxza>W`JDDiTA zf#j<2x565ygU}Y+vU~__df|*(a0no;tE^sM9jI75rXwt^a7&CW3 zM=iEW&h8%cbReMO;##<6TU~?Z_B(-iA3{|$@FC+=6d-tSWaOV%*l(18#~I(y~^++no`Zp+=oUeg+wO_^xr zDLC!_eP=dej}(c%ffcm=hVYwwtFw8N2l~a5bP4$1&{9+6V%>UBs%^9D>fI$$bOr`LB zwlf;oX!$if0*`e+PL^QoTTXv@WKi>`< z(mz(&xwT0Fa4cSIQhRM^@3SwJObJ3q{UHG>&g`!5IAM46nsvJ?z70U_J$;)S0_Fkew`%%`;^b!59e1Z!K}PjV%%bfO3Z!mLT#auc7WA}=4lQ4 zByGHtIugVY@dbws3&Ua;J{uTzBI?rUftfZi#b>FmTly26SeEx;95t?_bRP{>KNTTP zHNRXFGn&oXv_sKen=KCQLU)~uv=9mYu2?jay)p+%9E0|Dfxi51*tR^G@4h#}a3vyv zP<^Vi!IvoQKbW?67v^E#1?F{`{h#IeMLP2P{6qhYsa!7a?^ec6a96kXAvs~C`zM{u zUX@0A(`K!kDu4Ghk#yFgzAkcXJ|_h!B)LEk=rQ53-B7XvlL^j)K^OhwSWE+0MbzM1 z?9MxN;2%(rZ6`FjXZt}>ix{hy()$h2%2;rPanLFPNvYRk@A6Sf-c5KK=*V&e=mmOF znNJv`aKquO%cpA@ju1X{Mo|^KS!hk$QV01CQI^<>_WXqnd1-S{b}! z`2rFaDuuu5?C>A?p(SBze`m@Y(9Y&KnA8afZZyF%X5nhKVJ=yKae;=F7x%WM)?-HZka=E?Rxg=jU6|N=9-b&v$&*-nZ@NHOQUdq6CX(e zkcQsidjA|V9X9;c4IKH$9nuf@e1=YV(`W@y{QvsFHs<^}Gl4IezR0MA?=hVbK)PU@ z%@>FPf+GuHF!Et&6|AjFDQ*w z)(-fF`ZqlZdJMxV1g@dFe}5Ro0wOZ3&|UJ@{naTy9-xN~M4{(Y4PK>e00%VQ?v!U{HHOVMYXzzvK5T}2E54$3PX*=BS{t!_K*x1GI z;`N8AGUq~51V20blAzf7Qbn2vc%`ikDW=5LOTPpx^}^tPc2CT{ZGPXWN!S{PdHyYY zFkw%5giz<<3D+v7nBnATQ~C3%VKr=%syzc|DR6w?)%&c17as9Kf(#HUY;xtg#i0r> zFowg~*UKqLhGgi7DU8Cg_u$26ZQc zBBwLwEY~-76yTLOW$9qZjYmJ=X6<0A(Lq5oU)Io(-7S5u?Tm&1hmppgw}QVo{&&y; zD1Zsk?rs_wXR2kbh*l#3+p+Q^rhdfjN3R~JbaB(pj={1*TpbXS0R|mJfVdLLa&Qn9 z!yT}XkNFJtxIyobILiYVAX_%TIN?`W!e23Reg)b=}=m7N_CDDGk! zZ~{=v9*Ks;1~2b!Qrh|6hz*0z7axFKB8 z#=P6yCuI^vvf)|G92vw$d@JHNo@-K9gQBn1BZb}wZL^$nW>1Ep=X}da`<_B#(;L*( z*9Sl%;x~Mefdg7Fo@Zb=2aoqks|O?VJN76^L?nh zS}+>K_cV!*;s(kZ?l2-9=~gq5b;rvhL@8I90~YkCPsy*`hT=H->*v{2#pym$<+&mj(YULlOaSe6FtS zu|#u#lWm1)II{-9w09+Nzwl+f!|nHn(oTrb$75fA!QTyN=lz2bu-ZvKKJ&-mW3Na$ z>RWLlaAHb^mH6PBRxiAy8C63hBk+01Q?`yhrrWFj z<6Yxar@$v=n-{Ojo+_opeMF1|3n!)&hxclgE`*@e^(+Qw?%&H)fKaLq=>cW6pfN=# zK8C(mtE?7;>G$N944992C1gp&3{VS$nZ&mige5j*&DwiNV_5b`m9K8)g=?s;yyKo_5Va6n@ z`x63_Jex3bIb^%2ns%6b{M=~eudpu?Tr>}>Ynwv^b!@i!(Q;frW^|frhL34KkGLiG ztTAP1WaXHb1tBd}eEz_M&#&dmC}keWrvsEVb(Mh-tnZ}JL!?@$O{SsN`wkmQWKd0Z zdL7qq-7@|Jk~Fjv)%^#c-9RjFq^01QzWX7X&tt2Ir^xQ;>K^$tCsuB*fsRL zY<)-8+B$jU`3Jn-_Bt*2Csvc+1ndE%p=&bdz{lhtMNwcdphck+MX4BjXn0Mb3{y}v zd%|={jc=$~DK^sbkmCbDc^@m(5`;#h)FQLsPGi&vP6Ba2! zS_NCRh`y^jd}-5-I(?i<-@o+0Bd1C__By)PPZUp!m0+V2sd^t;ocUiV#Da5W1YFBr zrK~*GRT;EJ%KW38dD}=5s|T z3L6`}yNp2T7mW<_&T>#xm@xOSpZRS~y;NgD{ICLHK^!hxe3m$!_=qpNx{h%+2#V8) z6St&l9ig9OMCMqVWkRw*k)QOH{7Bh-X)`xSMt+sSW!UF5GY+27U&-PJOoDkyqZg!2 zG)j<~BnpNeZGS5%w0l7{G#bN3?Uq`JvAscoDv3sWo?pf=jlF~O4>gUq4pxHSyy2*x z3twZNp`8q#CNbM^QM-}BJpIR}2M%tKe~jq}E5Sn@&z(!dh>X62G|z5NOEbqskb+Lz&LQj zKKWB=@_wPza92ayIH;#b03eb*K^ek_sw5MLs6 zuC$1OxWHJ}cfHOQMSXnAK|6yD=?d|kp1lK{|JWBA3Zb|eR)uDSl08ykURe^@Kk92E zF|5_*w8L`gn6nglOih4*bBsV0V26|XHdi4?KOJj-cH2Jv=Usqru@FHS`P!2|q>VtL ztnJVa#eC$Q9#{_0CkCWXjR`KUFoy){NxHs*jk5(z_v@L4p&Kl*O)7nRhB>;D zE(wUcMF#I(R)c8ck>)JXxcz#k`>Z@|9{9DltgAF)CZ*kOx~l*SAt+j#YBOP{2x&d0 z4V|D}%(u8y8W<$f0#KZD8PW%zS>6A*$V%U!=zj$7$bdCZao~GzowK<`D;=Q#lo4Yy;Qg)HycmI2IzSaxOa( zZHZI+3(Cbt3s#g|3&6p_yw?q0g!x?zh56!}4e4o(E7o~LMC^dd759HwLnha>%g5@? zFNrDm^2x1O=H)KLF#J4v7}|0ws=TZ1?{lMsw!FUEG zCJazT0}~8`#N>10vLvQ0?NO;1aot1u07)Vo>gfy^EC#POllZmRubPV|*tU|4GH^m#S zthuiUVC3?|dZ|FOE5vy(NdLa}4X>-40w`P~?!05WyMp!l8-pEgSdDEhz@IC7I?{(t znnv9Pv7l;D-p!3m)*@~Zm7i%nos28NN@S*U0##&Y2uQp%bB~LpDNI4P7-nw8niQIN zbnM{6(Q5VFhC%e$MT<$B@hSRAF<^9N30dA{L^!-iy3+lWtYJ~XMdKNg>Q1^RSjD4T zAa}%VWWGnMm9wEDZ%DQoYpcp{z23-nRQzgtA?txAmE40;xT~a(p#Y8fnK+JoDx95| zp!)9e)<7m}sXq10icecFe{ZQ4fa`*6UB5lNvG7D1->ZvNBDEm7#Oh#V*U5 zmAMA5?fZsuYIhOL!Z7cJS~(gBau7LYEMAXNW2kH0u3Y@4(YT6ijmvOkKAWi^E3sX< z(*tXpV6=Q-hYD_ruF_=PvGAZoeQ5kFX-YlK#YkQrRuY(q5*!RCN-o1prfWg1jZ$%v z6zP_bH)3Yh7X?l92bx)~d}7}^^dAfVH?RlBs8A{xbzpk;2a-C7>)JrvI)$o_;*BtV zDUZ$)VS?+q1iyja@)Ca8oE*5-W^%iXWiaRgBpQ6smo>B9=Mbf?DR z-&Hj_emY)ykW7T)vsf9hWpJmlBn$I7gMd}^?rv|3H~bnhM-w&#b(N~ejm%-f1oom6 zOf@#HnU*(Wi&6PY*p~20u+*^xmfEr()AG!sp(ipt*0)7}d^Ez%x7&Ui={-UP7H?8p zcz3qpxT{FoxqhRl_Q0d$`$|Nlw>ckMKZ3ayG4LB+s3U;T_){-Yh2uHVURC1(#|x8{ zL6Urp)LXqIH2X`?&|S;^YGl|>z$9u)z7s{Y-s`g|wrHuT)K~H2(%wYbhcqXjV!pRV z0izE+tt<_eh1RnY8z$AE1&Untx3RhU?&2Zsy7jPC$-aI@gzb@~1OYV^?29-i`tP z_Z#|~uis-JP~8oUO-)hH&m2JPHO6_SZOs(04y_plfam*6xdGr*Sdh@RHV)6*h1*wP zh39hRzNP?BQ=E_ior6VE;qQ)dfD@6?pG7Q}n{A$}o2$KpGC5D6ima)tD`uyMz_V^Z zRU-|PIc3;5WB^2iVukr5gv47Qq57igZBW%8FYp^PsI`92(j0rX*e;%3QM~o+GzrAR zsJ*(Vj?cnyUW_y0onkakQWGvQ@ieICu=E{$FOIE5pxUS{b7kN&i|TN=E)b2@1KOJ7 zbG~`==gc+s^(E9@JLK{Opl)^X!H@t&0LZ6v0^#LI#$e@aRU6H~Yi*~;Z+Z~yGk^YU zY2zEd&VXVVHZFw4l(`|t#gUc?Gj`FhOR@-eBKf3v7EE9yi1&K{W zqqXal$Y8l^<}PTqNKmV5t3d6&IHSgfyE!>z&>jcc^4hBhbu;_U{lpPgA3GX`7c|Ss?FOXFHSX)4qP%s!fohBLzDBjh{@^>e4DWji$rwdX(_2Iz4hC`EEUfDW#Zu zR~(wvxwmH)vgZr-ttUS)9wiKy3j{5-1ExsC0C|3y<3cjo7#0TBI+z(Fy_yedW)lYt ztmU___-r91XmqAF9FahA!UPpr27@KXih7`}JU$MvlLIbCBOQ;YYL(h6HFvRA{NRU4 z*4tEyxxx*<#!gNs|7~GR%*;xhwOe|x;T$6Var>Wvg)@M@QRYGfWD-@um} z!Xu;i%2Ms6MFm>|!ZIalnX|ujJYFmrBU#VC!#f)mkP$KSH8`dDn%&Xb! zVWA<@_Mp3O#o7m`!S-khB~|lnzjIa$fkwr_v^oNSzYBih?0r0mS#NJJ@aBfaoTbpD zEf0_f0mmGur99O~@<_8#zpthlZwRm4&ut~R_3qb7edJ`B9Z~x{`;gqR#A_k?)BRQq z^FCBOBk)ho7t8eS_wvb+!*!Ti`u+=Eu!M%BW)e))tK|WH5a(|xqWT&d_3X0gPxASL zT7*Db4nWwT1n8sQJ^s3XPyS%zR{a2V3$zbjmaU?VTb=MHA8pDZtIm=S4I{Ij)mC^( zizuq$Pw`JDbyp?VqfWL}k=xb!^e=lcKwkw7lF-g6WPP8S$7wZtc=i~?cguybMyB`Z z(|^X3XoI9x%=mJ&P5mNsD(L?tf*sCR)@v83=dW(%@{CRfT_;erC86mk$*+$fr{yaKsV0VB6BR2(1Z}#IG z#)0X#YvMO4PYYUH^`5}>Cvib2h8&Wm7l#Us}Xot*SJ(Cu7H>!V9;H#j(v{v$mR9I1?UzC(BSHy z`Q(143~Sf0X<=c>;L`o(*>01KQEC;(*>}nKbhvkI=;^gN!M2F+w&IJ(xPD^huqo|26Z;4|a4hGwpYEZtN<4$C?(ycCwv#5c1iZw9U<~c5Iy&5~%JI@}#%GlnG8ibGfw6hMJY>DP)BU$Nx4jp#X-7f)}yJofR*;q;ZbWz(=KB z@}Qj(%ES1PsdYfFYPs^B2nu1D*i01!M`Z%CGQ;(YURTGs4O}+gzoyTa)A+G6fuvNA z{kvFb~Gh5N1%h8TOIhwPr5fa+FX+s9yz0dKnq|IW3kB&BGraPN&}#Hvlt9!cyC;jOAGgbS(#JV#V<(!e^0Oe=`jhx83|m4_Lb4|_X0)d- zfi8oIDsIkhp{lxi{_>gS>XjKgmf4W#pU>NweH6f|;N@Iu|I*HwgA#j!@TPY^sC6Ce6!B=F?j;npsPo6#X6+kixMrHYKL^h3{aCiXC)ZTAF@Xhn)TTZy@boFZHtGc%8+ZLy8Vm(#h6T>E;Q0bfn zqA-*u>pV_8SZ!uk+9f-2I5bo?Z+K$0QjXhlsvj3gB7~2JU+InAZ$zItL$i+lNEC&1 zI6>;ukTMhYZJ3Xc0ziR_K}%GVMV*hf%fX;+MgoaU(6n_xzAwf)nGRPLx^^1trDEH% zK6t2D7z&<3g&hJ|$w6eb>XH;0NHOc!%y?CxCVjsIwJ-a06Vqs~jc-nvbX!_9w@UG&gGJf2xcRT_&rliD8KN_z-?dhhjmKflu zlz;h-K5j^lDU1N}yyqJ>U>w_ak2`vZ>f5JUh6LL-nQJk@iVJ#wI_{?A`^}vZLJ?ko zPnU^KfK$n}NLx`}qiQ?-1k4docz;Q8mbLZ3XbCkUA< zCaa5SXT8!#^S5$~ony24Bfyl8Dk|QP&?>0#3Fg}A1_siQB`HRKWcR|<)GMOv)aghX zQm1sa{cfLB=jN`ci$x(klVz~z;c$K_!2@2ssJU6(kO#gek|x!c@=MQ=(>56)YNP!w zy|X@LD5)E>Uu7*u{7p1(ZMv&ZcG(OH(Sj{B^(Y&b%6x{+MtH}qRPA^qik!6tU9sFd zCMH!#fzPO7<154XilkCB$>-x#>&HR{-s^q#_L{~gdE1~|=k}JGkp*tUQ{RMh5qR%p zmny@+XxWyP+^6VNL7in8H7MUntwSdG$)Z3bISDINFtt!Sw?mXp2w4z4`2rg?w=)8a zV7oR1QQk?VasYHZ7_I2?^5LlNQEXP!zHVk{J+> z@r$)zgqE2E*P4bpg_GX!uZ^!rBqCX|xNs;oOU|@2fWulQ30`JdLx?cT z<#quBhUekUP2BJjlJF2vs3Fi*uF8Z}h!hB%WWC1a!DRfXgepA3zR;Fy06AT2qXRT!G zhnMl4toUWwrl zIo8wD^ujb|tu$ZB?G z{ABP-HGKBV_Dy&uX85b1gK_ftwe9!Yv9=|n&B?hGew{fP zS0bWGe)NYqi2`}lI$omk5!bXpvB`NGK}?0&PMR!Hx}#DpF98Z!zBkg)m)CR&^$iKX zj3pwhLCU`z_Ed*fCpZ!ZjpD|zxs{q$iY6oGGHc9GBso7>6Q$q8OH(kMk6zmu<3?p= zp@G@|P-a=tSBsUD6upo>Eb9k(KnMs%!qw|xD^lEXEo#Nj5#Q*?(~6&-_x#=$0VJ>F zc=yKCQO*COp&5lz zHhKCiNonm&`f-~PWwUJUK@yeHp>*^z4^kd%SNP3eQ0!>1Q_H%i;s-;`8+eDqqu5;A z&I+>Bmc-aeK!Uw(+piy}kOzoh6aYaRD7QlfVrrkw*)pQ&&=y13kA#uB1%uw-2wK9E*~-B{8+^4J z4M7Wa^pnK92bY91Q}M;w;)AvK)N5`8z-xKOM)=8F`JCBo+Adz?#g&`8yhAi)MkV*ct9>B_b#zOOQ{@PdPUs2nKa3f4-{m zj$r?al_ZBcNDnvXdaB{fe4!O8*@sjW99yIET{c?7g`p9Z`H31V#bk0K#}o&4+_Far z#4@z0Vs{%xTIU^~oIFjw&GKWn>IG$Bp?lM8vQ}m`$P{7( zW~?5P91R2+j5Bwx9E~U>6nzw~2o9E5+U7a4zn)^d`PJgPa=gZ7sw$^qA>_PT6Sddx z($5RMPX&2gxiTD@x(55&#MYDURoL-bcsJdGtd4`>a`FPQrF``{&}K8LehKVG)ip*f z4)!?Y)H_K_sl`@md5NUO=_FnjFvrE7q)bYD9t%Br&9mG)Mu*v5pZg#4jhab1$@zI= z!?jtaKwQuF9&9=%j~dJ5@)_zvUU#5a@gHk;h=h|>y%*7^zRX^C&;AJBcFz3(_(jyP;Y2*)+SZR|E;3TY2 z5g@MA+c%&CS8T@mbW`83=&~S0-$b7CMS% zt9k4YvvYtzdCAZ)$*&8yxM$Bv*Uu#h3u(K$q=1lNv)S9NPZy8qj)&oHHFw%vtoqQE zBnCo)biS(n=kmRo<)d4HH%iD6_Ct|w=@%v3qGbsp(iYDQkEGKoJ17Y88qQDsC-RLS znJbPzq)q;d>6BFClJSYY@XO9i-c)aZWq_xWlOty3KB>)p`38GDaK>0v?fpqeTHHu4 z1JCTx7Wd~-LK7r%c56|;WfDL661>(r!X%6AW%p<X7>yQaR5hZ7U7y`&&p*ep_pSWBz4lEM&~wfN$Bc$yX8l>eXq9q|$mV3$e(d7%Fy{!=vr_I%Ri>S-fo;n~yH zB3yJx(n#OUjq4d`NwHN8OXd-H;o^cEh+mE1Yb&nFO8%1y6UuY*{+sp$$|gFbTdHLK zZmL2By|iH;3=o5-t=z7z91s7zxtY3dbON9FT`5&$PM=g>hJ|k_vgFNj{Dm&bUpu&# zMbc+K{{XF7qK;OW0f9YLqF

S!S$z2o`HcWqmhIziB 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≶|{#3nCL4h4~HEa^4Vo<|!pH@_@jBiF3J? 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%0Db2cXYgZH+xEP`TJ-r3r2FN7cY35c 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 Date: Sat, 30 Aug 2025 22:59:21 +0800 Subject: [PATCH 13/35] =?UTF-8?q?update:=E6=B5=81=E5=BC=8F=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=9A=82=E4=B8=8D=E7=A8=B3=E5=AE=9A=EF=BC=8C=E5=85=88?= =?UTF-8?q?=E5=9B=9E=E6=BB=9A=E5=88=B0=E6=97=A9=E6=9C=9F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 4 + .../xiaozhi-server/core/handle/helloHandle.py | 112 +++++++++- .../core/handle/intentHandler.py | 14 +- .../core/handle/receiveAudioHandle.py | 51 +++-- .../core/handle/sendAudioHandle.py | 89 ++++---- main/xiaozhi-server/core/handle/textHandle.py | 29 ++- .../xiaozhi-server/core/providers/asr/base.py | 152 ++++++-------- .../core/providers/tts/aliyun_stream.py | 195 +++++++++++++++++- .../xiaozhi-server/core/providers/tts/base.py | 140 ++++++------- .../providers/tts/huoshan_double_stream.py | 144 ++++++++++++- .../core/providers/tts/index_stream.py | 111 ++++++++-- .../core/providers/tts/linkerai.py | 135 ++++++++++-- .../core/providers/tts/paddle_speech.py | 30 +-- .../core/providers/vad/silero.py | 6 +- .../core/utils/opus_encoder_utils.py | 17 +- main/xiaozhi-server/core/utils/p3.py | 37 +++- main/xiaozhi-server/core/utils/util.py | 78 ++++--- main/xiaozhi-server/core/utils/wakeup_word.py | 140 +++++++++++++ .../plugins_func/functions/play_music.py | 1 + 19 files changed, 1116 insertions(+), 369 deletions(-) create mode 100644 main/xiaozhi-server/core/utils/wakeup_word.py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index efcca44f..c0aa3f93 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -59,6 +59,10 @@ log: delete_audio: true # 没有语音输入多久后断开连接(秒),默认2分钟,即120秒 close_connection_no_voice_time: 120 +# TTS请求超时时间(秒) +tts_timeout: 10 +# 开启唤醒词加速 +enable_wakeup_words_response_cache: true # 开场是否回复唤醒词 enable_greeting: true # 说完话是否开启提示音 diff --git a/main/xiaozhi-server/core/handle/helloHandle.py b/main/xiaozhi-server/core/handle/helloHandle.py index 8147db1a..e4e836b4 100644 --- a/main/xiaozhi-server/core/handle/helloHandle.py +++ b/main/xiaozhi-server/core/handle/helloHandle.py @@ -1,13 +1,33 @@ +import time import json +import random import asyncio +from core.utils.dialogue import Message +from core.utils.util import audio_to_data +from core.handle.sendAudioHandle import sendAudioMessage, send_stt_message +from core.utils.util import remove_punctuation_and_length, opus_datas_to_wav_bytes +from core.providers.tts.dto.dto import ContentType, SentenceType from core.providers.tools.device_mcp import ( MCPClient, send_mcp_initialize_message, send_mcp_tools_list_request, ) +from core.utils.wakeup_word import WakeupWordsConfig TAG = __name__ +WAKEUP_CONFIG = { + "refresh_time": 5, + "words": ["你好", "你好啊", "嘿,你好", "嗨"], +} + +# 创建全局的唤醒词配置管理器 +wakeup_words_config = WakeupWordsConfig() + +# 用于防止并发调用wakeupWordsResponse的锁 +_wakeup_response_lock = asyncio.Lock() + + async def handleHelloMessage(conn, msg_json): """处理hello消息""" audio_params = msg_json.get("audio_params") @@ -28,4 +48,94 @@ async def handleHelloMessage(conn, msg_json): # 发送mcp消息,获取tools列表 asyncio.create_task(send_mcp_tools_list_request(conn)) - await conn.websocket.send(json.dumps(conn.welcome_msg)) \ No newline at end of file + await conn.websocket.send(json.dumps(conn.welcome_msg)) + + +async def checkWakeupWords(conn, text): + enable_wakeup_words_response_cache = conn.config[ + "enable_wakeup_words_response_cache" + ] + + if not enable_wakeup_words_response_cache or not conn.tts: + return False + + _, filtered_text = remove_punctuation_and_length(text) + if filtered_text not in conn.config.get("wakeup_words"): + return False + + conn.just_woken_up = True + await send_stt_message(conn, text) + + # 获取当前音色 + voice = getattr(conn.tts, "voice", "default") + if not voice: + voice = "default" + + # 获取唤醒词回复配置 + response = wakeup_words_config.get_wakeup_response(voice) + if not response or not response.get("file_path"): + response = { + "voice": "default", + "file_path": "config/assets/wakeup_words.wav", + "time": 0, + "text": "哈啰啊,我是小智啦,声音好听的台湾女孩一枚,超开心认识你耶,最近在忙啥,别忘了给我来点有趣的料哦,我超爱听八卦的啦", + } + + # 播放唤醒词回复 + conn.client_abort = False + opus_packets, _ = audio_to_data(response.get("file_path")) + + conn.logger.bind(tag=TAG).info(f"播放唤醒词回复: {response.get('text')}") + await sendAudioMessage(conn, SentenceType.FIRST, opus_packets, response.get("text")) + await sendAudioMessage(conn, SentenceType.LAST, [], None) + + # 补充对话 + conn.dialogue.put(Message(role="assistant", content=response.get("text"))) + + # 检查是否需要更新唤醒词回复 + if time.time() - response.get("time", 0) > WAKEUP_CONFIG["refresh_time"]: + if not _wakeup_response_lock.locked(): + asyncio.create_task(wakeupWordsResponse(conn)) + return True + + +async def wakeupWordsResponse(conn): + if not conn.tts or not conn.llm or not conn.llm.response_no_stream: + return + + try: + # 尝试获取锁,如果获取不到就返回 + if not await _wakeup_response_lock.acquire(): + return + + # 生成唤醒词回复 + wakeup_word = random.choice(WAKEUP_CONFIG["words"]) + question = ( + "此刻用户正在和你说```" + + wakeup_word + + "```。\n请你根据以上用户的内容进行20-30字回复。要符合系统设置的角色情感和态度,不要像机器人一样说话。\n" + + "请勿对这条内容本身进行任何解释和回应,请勿返回表情符号,仅返回对用户的内容的回复。" + ) + + result = conn.llm.response_no_stream(conn.config["prompt"], question) + if not result or len(result) == 0: + return + + # 生成TTS音频 + tts_result = await asyncio.to_thread(conn.tts.to_tts, result) + if not tts_result: + return + + # 获取当前音色 + voice = getattr(conn.tts, "voice", "default") + + wav_bytes = opus_datas_to_wav_bytes(tts_result, sample_rate=16000) + file_path = wakeup_words_config.generate_file_path(voice) + with open(file_path, "wb") as f: + f.write(wav_bytes) + # 更新配置 + wakeup_words_config.update_wakeup_response(voice, file_path, result) + finally: + # 确保在任何情况下都释放锁 + if _wakeup_response_lock.locked(): + _wakeup_response_lock.release() diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index 2ddf03d6..81911f55 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -1,11 +1,12 @@ import json -import uuid import asyncio -from core.utils.dialogue import Message -from core.providers.tts.dto.dto import ContentType -from plugins_func.register import Action, ActionResponse +import uuid from core.handle.sendAudioHandle import send_stt_message +from core.handle.helloHandle import checkWakeupWords from core.utils.util import remove_punctuation_and_length +from core.providers.tts.dto.dto import ContentType +from core.utils.dialogue import Message +from plugins_func.register import Action, ActionResponse from core.providers.tts.dto.dto import TTSMessageDTO, SentenceType TAG = __name__ @@ -23,9 +24,12 @@ async def handle_user_intent(conn, text): pass # 检查是否有明确的退出命令 - _, filtered_text = remove_punctuation_and_length(text) + filtered_text = remove_punctuation_and_length(text)[1] if await check_direct_exit(conn, filtered_text): return True + # 检查是否是唤醒词 + if await checkWakeupWords(conn, filtered_text): + return True if conn.intent_type == "function_call": # 使用支持function calling的聊天方法,不再进行意图分析 diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index 15a9e434..e6f39632 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -1,21 +1,28 @@ -import time -import json -from core.handle.abortHandle import handleAbortMessage +from core.handle.sendAudioHandle import send_stt_message from core.handle.intentHandler import handle_user_intent from core.utils.output_counter import check_device_output_limit -from core.utils.util import play_audio_frames, play_audio_response -from core.handle.sendAudioHandle import send_stt_message, SentenceType +from core.handle.abortHandle import handleAbortMessage +import time +import asyncio +import json +from core.handle.sendAudioHandle import SentenceType +from core.utils.util import audio_to_data TAG = __name__ async def handleAudioMessage(conn, audio): - # 检查是否在唤醒处理锁定期内 - if getattr(conn, 'wakeup_processing_lock', 0) > time.monotonic(): - return - # 当前片段是否有人说话 have_voice = conn.vad.is_vad(conn, audio) + # 如果设备刚刚被唤醒,短暂忽略VAD检测 + if have_voice and hasattr(conn, "just_woken_up") and conn.just_woken_up: + have_voice = False + # 设置一个短暂延迟后恢复VAD检测 + conn.asr_audio.clear() + if not hasattr(conn, "vad_resume_task") or conn.vad_resume_task.done(): + conn.vad_resume_task = asyncio.create_task(resume_vad_detection(conn)) + return + if have_voice: if conn.client_is_speaking: await handleAbortMessage(conn) @@ -24,11 +31,18 @@ async def handleAudioMessage(conn, audio): # 接收音频 await conn.asr.receive_audio(conn, audio, have_voice) + +async def resume_vad_detection(conn): + # 等待2秒后恢复VAD检测 + await asyncio.sleep(1) + conn.just_woken_up = False + + async def startToChat(conn, text): # 检查输入是否是JSON格式(包含说话人信息) speaker_name = None actual_text = text - + try: # 尝试解析JSON格式的输入 if text.strip().startswith('{') and text.strip().endswith('}'): @@ -37,13 +51,13 @@ async def startToChat(conn, text): speaker_name = data['speaker'] actual_text = data['content'] conn.logger.bind(tag=TAG).info(f"解析到说话人信息: {speaker_name}") - + # 直接使用JSON格式的文本,不解析 actual_text = text except (json.JSONDecodeError, KeyError): # 如果解析失败,继续使用原始文本 pass - + # 保存说话人信息到连接对象 if speaker_name: conn.current_speaker = speaker_name @@ -107,7 +121,8 @@ async def max_out_size(conn): text = "不好意思,我现在有点事情要忙,明天这个时候我们再聊,约好了哦!明天不见不散,拜拜!" await send_stt_message(conn, text) file_path = "config/assets/max_output_size.wav" - play_audio_response(conn, {"text": text, "file_path": file_path}) + opus_packets, _ = audio_to_data(file_path) + conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text)) conn.close_after_chat = True @@ -125,15 +140,16 @@ async def check_bind_device(conn): # 播放提示音 music_path = "config/assets/bind_code.wav" - conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], text)) - play_audio_frames(conn, music_path) + opus_packets, _ = audio_to_data(music_path) + conn.tts.tts_audio_queue.put((SentenceType.FIRST, opus_packets, text)) # 逐个播放数字 for i in range(6): # 确保只播放6位数字 try: digit = conn.bind_code[i] num_path = f"config/assets/bind_code/{digit}.wav" - play_audio_frames(conn, num_path) + num_packets, _ = audio_to_data(num_path) + conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, num_packets, None)) except Exception as e: conn.logger.bind(tag=TAG).error(f"播放数字音频失败: {e}") continue @@ -142,4 +158,5 @@ async def check_bind_device(conn): text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。" await send_stt_message(conn, text) music_path = "config/assets/bind_not_found.wav" - play_audio_response(conn, {"text": text, "file_path": music_path}) + opus_packets, _ = audio_to_data(music_path) + conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text)) diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index c61b0d0d..0ca36e1e 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -1,25 +1,25 @@ import json -import time import asyncio -from core.utils import textUtils +import time from core.providers.tts.dto.dto import SentenceType +from core.utils import textUtils TAG = __name__ async def sendAudioMessage(conn, sentenceType, audios, text): + # 发送句子开始消息 + conn.logger.bind(tag=TAG).info(f"发送音频消息: {sentenceType}, {text}") + + pre_buffer = False if conn.tts.tts_audio_first_sentence: conn.logger.bind(tag=TAG).info(f"发送第一段语音: {text}") conn.tts.tts_audio_first_sentence = False - await send_tts_message(conn, "start", None) + pre_buffer = True - if sentenceType == SentenceType.FIRST: - await send_tts_message(conn, "sentence_start", text) + await send_tts_message(conn, "sentence_start", text) - await sendAudio(conn, audios) - # 发送句子开始消息 - if sentenceType is not SentenceType.MIDDLE: - conn.logger.bind(tag=TAG).info(f"发送音频消息: {sentenceType}, {text}") + await sendAudio(conn, audios, pre_buffer) # 发送结束消息(如果是最后一个文本) if conn.llm_finish_task and sentenceType == SentenceType.LAST: @@ -30,59 +30,45 @@ async def sendAudioMessage(conn, sentenceType, audios, text): # 播放音频 -async def sendAudio(conn, audios, pre_buffer=False): - """ - 发送单个opus包,支持流控 - Args: - conn: 连接对象 - opus_packet: 单个opus数据包 - pre_buffer: 快速发送音频 - """ +async def sendAudio(conn, audios, pre_buffer=True): if audios is None or len(audios) == 0: return + # 流控参数优化 + frame_duration = 60 # 帧时长(毫秒),匹配 Opus 编码 + start_time = time.perf_counter() + play_position = 0 - if isinstance(audios, bytes): + # 仅当第一句话时执行预缓冲 + if pre_buffer: + pre_buffer_frames = min(3, len(audios)) + for i in range(pre_buffer_frames): + await conn.websocket.send(audios[i]) + remaining_audios = audios[pre_buffer_frames:] + else: + remaining_audios = audios + + # 播放剩余音频帧 + for opus_packet in remaining_audios: if conn.client_abort: - return - - # 短音频直接发送(例如:提示音) - if pre_buffer: - await conn.websocket.send(audios) - return + break + # 重置没有声音的状态 conn.last_activity_time = time.time() * 1000 - # 获取或初始化流控状态 - if not hasattr(conn, "audio_flow_control"): - conn.audio_flow_control = { - "last_send_time": 0, - "packet_count": 0, - "start_time": time.perf_counter(), - } - - frame_duration=60 - flow_control = conn.audio_flow_control - current_time = time.perf_counter() # 计算预期发送时间 - expected_time = flow_control["start_time"] + ( - flow_control["packet_count"] * frame_duration / 1000 - ) + expected_time = start_time + (play_position / 1000) + current_time = time.perf_counter() delay = expected_time - current_time if delay > 0: await asyncio.sleep(delay) - # 发送数据包 - await conn.websocket.send(audios) + await conn.websocket.send(opus_packet) - # 更新流控状态 - flow_control["packet_count"] += 1 - flow_control["last_send_time"] = time.perf_counter() + play_position += frame_duration async def send_tts_message(conn, state, text=None): """发送 TTS 状态消息""" - if text is None and state == "sentence_start": - return message = {"type": "tts", "state": state, "session_id": conn.session_id} if text is not None: message["text"] = textUtils.check_emoji(text) @@ -95,12 +81,8 @@ async def send_tts_message(conn, state, text=None): stop_tts_notify_voice = conn.config.get( "stop_tts_notify_voice", "config/assets/tts_notify.mp3" ) - conn.tts.audio_to_opus_data_stream( - stop_tts_notify_voice, - callback=lambda audio_data: asyncio.run_coroutine_threadsafe( - sendAudio(conn, audio_data, True), conn.loop - ), - ) + audios, _ = conn.tts.audio_to_opus_data(stop_tts_notify_voice) + await sendAudio(conn, audios) # 清除服务端讲话状态 conn.clearSpeakStatus() @@ -109,17 +91,18 @@ async def send_tts_message(conn, state, text=None): async def send_stt_message(conn, text): - """发送 STT 状态消息""" end_prompt_str = conn.config.get("end_prompt", {}).get("prompt") if end_prompt_str and end_prompt_str == text: await send_tts_message(conn, "start") return + """发送 STT 状态消息""" + # 解析JSON格式,提取实际的用户说话内容 display_text = text try: # 尝试解析JSON格式 - if text.strip().startswith("{") and text.strip().endswith("}"): + if text.strip().startswith('{') and text.strip().endswith('}'): parsed_data = json.loads(text) if isinstance(parsed_data, dict) and "content" in parsed_data: # 如果是包含说话人信息的JSON格式,只显示content部分 diff --git a/main/xiaozhi-server/core/handle/textHandle.py b/main/xiaozhi-server/core/handle/textHandle.py index c1e8f6be..1bcf4545 100644 --- a/main/xiaozhi-server/core/handle/textHandle.py +++ b/main/xiaozhi-server/core/handle/textHandle.py @@ -1,12 +1,14 @@ import json -import asyncio -from core.utils.util import filter_sensitive_info +import time from core.handle.abortHandle import handleAbortMessage from core.handle.helloHandle import handleHelloMessage -from core.handle.receiveAudioHandle import handleAudioMessage from core.providers.tools.device_mcp import handle_mcp_message +from core.utils.util import remove_punctuation_and_length, filter_sensitive_info +from core.handle.receiveAudioHandle import startToChat, handleAudioMessage from core.handle.sendAudioHandle import send_stt_message, send_tts_message from core.providers.tools.device_iot import handleIotDescriptors, handleIotStatus +from core.handle.reportHandle import enqueue_asr_report +import asyncio TAG = __name__ @@ -44,9 +46,14 @@ async def handleTextMessage(conn, message): conn.client_have_voice = False conn.asr_audio.clear() if "text" in msg_json: - original_text = msg_json["text"] # 保留设备上传的文本 + conn.last_activity_time = time.time() * 1000 + original_text = msg_json["text"] # 保留原始文本 + filtered_len, filtered_text = remove_punctuation_and_length( + original_text + ) + # 识别是否是唤醒词 - is_wakeup_words = original_text in conn.config.get("wakeup_words") + is_wakeup_words = filtered_text in conn.config.get("wakeup_words") # 是否开启唤醒词回复 enable_greeting = conn.config.get("enable_greeting", True) @@ -55,10 +62,16 @@ async def handleTextMessage(conn, message): await send_stt_message(conn, original_text) await send_tts_message(conn, "stop", None) conn.client_is_speaking = False + elif is_wakeup_words: + conn.just_woken_up = True + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, "嘿,你好呀", []) + await startToChat(conn, "嘿,你好呀") else: - # 检测到唤醒词,开始等待后续进行声纹识别 - conn.wakeup_mode = True - conn.logger.bind(tag=TAG).info(f"检测到唤醒词~") + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, original_text, []) + # 否则需要LLM对文字内容进行答复 + await startToChat(conn, original_text) elif msg_json["type"] == "iot": conn.logger.bind(tag=TAG).info(f"收到iot消息:{message}") if "descriptors" in msg_json: diff --git a/main/xiaozhi-server/core/providers/asr/base.py b/main/xiaozhi-server/core/providers/asr/base.py index 7885261d..972818d4 100644 --- a/main/xiaozhi-server/core/providers/asr/base.py +++ b/main/xiaozhi-server/core/providers/asr/base.py @@ -1,18 +1,18 @@ import os -import io import wave import uuid -import json -import time import queue import asyncio import traceback import threading import opuslib_next +import json +import io +import time import concurrent.futures from abc import ABC, abstractmethod from config.logger import setup_logging -from typing import Optional, Tuple, List +from typing import Optional, Tuple, List, Dict, Any from core.handle.receiveAudioHandle import startToChat from core.handle.reportHandle import enqueue_asr_report from core.utils.util import remove_punctuation_and_length @@ -53,10 +53,6 @@ class ASRProviderBase(ABC): # 接收音频 async def receive_audio(self, conn, audio, audio_have_voice): - # 检查是否在唤醒处理锁定期内 - if getattr(conn, 'wakeup_processing_lock', 0) > time.monotonic(): - return - if conn.client_listen_mode == "auto" or conn.client_listen_mode == "realtime": have_voice = audio_have_voice else: @@ -66,14 +62,6 @@ class ASRProviderBase(ABC): if not have_voice and not conn.client_have_voice: conn.asr_audio = conn.asr_audio[-10:] return - - # 检查是否处于唤醒模式 - if getattr(conn, 'wakeup_mode', False) and len(conn.asr_audio) >= 10: - asr_audio_task = conn.asr_audio.copy() - conn.reset_vad_states() - conn.asr_audio.clear() - - await self.handle_voice_stop(conn, asr_audio_task) if conn.client_voice_stop: asr_audio_task = conn.asr_audio.copy() @@ -99,13 +87,32 @@ class ASRProviderBase(ABC): # 预先准备WAV数据 wav_data = None + # 使用连接的声纹识别提供者 if conn.voiceprint_provider and combined_pcm_data: wav_data = self._pcm_to_wav(combined_pcm_data) - # 检查是否处于唤醒模式 - wakeup_mode = getattr(conn, 'wakeup_mode', False) - # 声纹识别任务(公共逻辑) + # 定义ASR任务 + def run_asr(): + start_time = time.monotonic() + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + result = loop.run_until_complete( + self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format) + ) + end_time = time.monotonic() + logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s") + return result + finally: + loop.close() + except Exception as e: + end_time = time.monotonic() + logger.bind(tag=TAG).error(f"ASR失败: {e}") + return ("", None) + + # 定义声纹识别任务 def run_voiceprint(): if not wav_data: return None @@ -124,83 +131,50 @@ class ASRProviderBase(ABC): logger.bind(tag=TAG).error(f"声纹识别失败: {e}") return None - if wakeup_mode and conn.voiceprint_provider and wav_data: - conn.wakeup_mode = False - # 设置处理锁,防止后续音频片段重复处理 - conn.wakeup_processing_lock = time.monotonic() + 3 # 3秒锁定期 + # 使用线程池执行器并行运行 + parallel_start_time = time.monotonic() + + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread_executor: + asr_future = thread_executor.submit(run_asr) - # 唤醒模式:只执行声纹识别 - with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread_executor: + if conn.voiceprint_provider and wav_data: voiceprint_future = thread_executor.submit(run_voiceprint) + + # 等待两个线程都完成 + asr_result = asr_future.result(timeout=15) voiceprint_result = voiceprint_future.result(timeout=15) - - speaker_name = voiceprint_result + + results = {"asr": asr_result, "voiceprint": voiceprint_result} + else: + asr_result = asr_future.result(timeout=15) + results = {"asr": asr_result, "voiceprint": None} + + + # 处理结果 + raw_text, file_path = results.get("asr", ("", None)) + speaker_name = results.get("voiceprint", None) + + # 记录识别结果 + if raw_text: + logger.bind(tag=TAG).info(f"识别文本: {raw_text}") + if speaker_name: logger.bind(tag=TAG).info(f"识别说话人: {speaker_name}") - - fixed_text = "嘿,你好啊" - enhanced_text = self._build_enhanced_text(fixed_text, speaker_name) + + # 性能监控 + total_time = time.monotonic() - total_start_time + logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s") + + # 检查文本长度 + text_len, _ = remove_punctuation_and_length(raw_text) + self.stop_ws_connection() + + if text_len > 0: + # 构建包含说话人信息的JSON字符串 + enhanced_text = self._build_enhanced_text(raw_text, speaker_name) - # 性能监控 - total_time = time.monotonic() - total_start_time - logger.bind(tag=TAG).info(f"唤醒模式总处理耗时: {total_time:.3f}s") - + # 使用自定义模块进行上报 await startToChat(conn, enhanced_text) enqueue_asr_report(conn, enhanced_text, asr_audio_task) - else: - # 正常模式:执行声纹识别和ASR - def run_asr(): - start_time = time.monotonic() - try: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - result = loop.run_until_complete( - self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format) - ) - end_time = time.monotonic() - logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s") - return result - finally: - loop.close() - except Exception as e: - end_time = time.monotonic() - logger.bind(tag=TAG).error(f"ASR失败: {e}") - return ("", None) - - # 使用线程池执行器并行运行 - with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread_executor: - asr_future = thread_executor.submit(run_asr) - - if conn.voiceprint_provider and wav_data: - voiceprint_future = thread_executor.submit(run_voiceprint) - asr_result = asr_future.result(timeout=15) - voiceprint_result = voiceprint_future.result(timeout=15) - results = {"asr": asr_result, "voiceprint": voiceprint_result} - else: - asr_result = asr_future.result(timeout=15) - results = {"asr": asr_result, "voiceprint": None} - - # 处理结果 - raw_text, _ = results.get("asr", ("", None)) - speaker_name = results.get("voiceprint", None) - - if raw_text: - logger.bind(tag=TAG).info(f"识别文本: {raw_text}") - if speaker_name: - logger.bind(tag=TAG).info(f"识别说话人: {speaker_name}") - - # 性能监控 - total_time = time.monotonic() - total_start_time - logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s") - - # 检查文本长度 - text_len, _ = remove_punctuation_and_length(raw_text) - self.stop_ws_connection() - - if text_len > 0: - enhanced_text = self._build_enhanced_text(raw_text, speaker_name) - await startToChat(conn, enhanced_text) - enqueue_asr_report(conn, enhanced_text, asr_audio_task) except Exception as e: logger.bind(tag=TAG).error(f"处理语音停止失败: {e}") diff --git a/main/xiaozhi-server/core/providers/tts/aliyun_stream.py b/main/xiaozhi-server/core/providers/tts/aliyun_stream.py index 734681e2..0bfb367b 100644 --- a/main/xiaozhi-server/core/providers/tts/aliyun_stream.py +++ b/main/xiaozhi-server/core/providers/tts/aliyun_stream.py @@ -268,7 +268,11 @@ class TTSProvider(TTSProviderBase): ) if message.content_file and os.path.exists(message.content_file): # 先处理文件音频数据 - self._process_audio_file_stream(message.content_file, callback=lambda audio_data: self.handle_audio_file(audio_data, message.content_detail)) + file_audio = self._process_audio_file(message.content_file) + self.before_stop_play_files.append( + (file_audio, message.content_detail) + ) + if message.sentence_type == SentenceType.LAST: try: logger.bind(tag=TAG).info("开始结束TTS会话...") @@ -418,6 +422,9 @@ class TTSProvider(TTSProviderBase): async def _start_monitor_tts_response(self): """监听TTS响应""" + opus_datas_cache = [] + is_first_sentence = True + first_sentence_segment_count = 0 # 添加计数器 try: session_finished = False # 标记会话是否正常结束 while not self.conn.stop_event.is_set(): @@ -438,16 +445,28 @@ class TTSProvider(TTSProviderBase): self.tts_audio_queue.put( (SentenceType.FIRST, [], None) ) + elif event_name == "SentenceBegin": + opus_datas_cache = [] elif event_name == "SentenceEnd": - # 发送缓存的数据 - if self.conn.tts_MessageText: - logger.bind(tag=TAG).info( - f"句子语音生成成功: {self.conn.tts_MessageText}" - ) - self.tts_audio_queue.put( - (SentenceType.FIRST, [], self.conn.tts_MessageText) - ) - self.conn.tts_MessageText = None + if ( + not is_first_sentence + or first_sentence_segment_count > 10 + ): + # 发送缓存的数据 + if self.conn.tts_MessageText: + logger.bind(tag=TAG).info( + f"句子语音生成成功: {self.conn.tts_MessageText}" + ) + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas_cache, self.conn.tts_MessageText) + ) + self.conn.tts_MessageText = None + else: + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas_cache, None) + ) + # 第一句话结束后,将标志设置为False + is_first_sentence = False elif event_name == "SynthesisCompleted": logger.bind(tag=TAG).debug(f"会话结束~~") self._process_before_stop_play_files() @@ -458,7 +477,22 @@ class TTSProvider(TTSProviderBase): # 二进制消息(音频数据) elif isinstance(msg, (bytes, bytearray)): logger.bind(tag=TAG).debug(f"推送数据到队列里面~~") - self.opus_encoder.encode_pcm_to_opus_stream(msg, False, self.handle_opus) + opus_datas = self.opus_encoder.encode_pcm_to_opus(msg, False) + logger.bind(tag=TAG).debug( + f"推送数据到队列里面帧数~~{len(opus_datas)}" + ) + if is_first_sentence: + first_sentence_segment_count += 1 + if first_sentence_segment_count <= 6: + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas, None) + ) + else: + opus_datas_cache.extend(opus_datas) + else: + # 后续句子缓存 + opus_datas_cache.extend(opus_datas) + except websockets.ConnectionClosed: logger.bind(tag=TAG).warning("WebSocket连接已关闭") break @@ -478,3 +512,142 @@ class TTSProvider(TTSProviderBase): finally: self._monitor_task = None + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景""" + try: + # 创建新的事件循环 + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # 生成会话ID + session_id = uuid.uuid4().hex + # 存储音频数据 + audio_data = [] + + async def _generate_audio(): + # 刷新Token(如果需要) + if self._is_token_expired(): + self._refresh_token() + + # 建立WebSocket连接 + ws = await websockets.connect( + self.ws_url, + additional_headers={"X-NLS-Token": self.token}, + ping_interval=30, + ping_timeout=10, + close_timeout=10, + ) + try: + # 发送StartSynthesis请求 + start_message_id = str(uuid.uuid4().hex) + start_request = { + "header": { + "message_id": start_message_id, + "task_id": session_id, + "namespace": "FlowingSpeechSynthesizer", + "name": "StartSynthesis", + "appkey": self.appkey, + }, + "payload": { + "voice": self.voice, + "format": self.format, + "sample_rate": self.sample_rate, + "volume": self.volume, + "speech_rate": self.speech_rate, + "pitch_rate": self.pitch_rate, + "enable_subtitle": True, + }, + } + await ws.send(json.dumps(start_request)) + + # 等待SynthesisStarted响应 + synthesis_started = False + while not synthesis_started: + msg = await ws.recv() + if isinstance(msg, str): + data = json.loads(msg) + header = data.get("header", {}) + if header.get("name") == "SynthesisStarted": + synthesis_started = True + logger.bind(tag=TAG).debug("TTS合成已启动") + elif header.get("name") == "TaskFailed": + error_info = data.get("payload", {}).get( + "error_info", {} + ) + error_code = error_info.get("error_code") + error_message = error_info.get( + "error_message", "未知错误" + ) + raise Exception( + f"启动合成失败: {error_code} - {error_message}" + ) + + # 发送文本合成请求 + filtered_text = MarkdownCleaner.clean_markdown(text) + run_message_id = str(uuid.uuid4().hex) + run_request = { + "header": { + "message_id": run_message_id, + "task_id": session_id, + "namespace": "FlowingSpeechSynthesizer", + "name": "RunSynthesis", + "appkey": self.appkey, + }, + "payload": {"text": filtered_text}, + } + await ws.send(json.dumps(run_request)) + + # 发送停止合成请求 + stop_message_id = str(uuid.uuid4().hex) + stop_request = { + "header": { + "message_id": stop_message_id, + "task_id": session_id, + "namespace": "FlowingSpeechSynthesizer", + "name": "StopSynthesis", + "appkey": self.appkey, + } + } + await ws.send(json.dumps(stop_request)) + + # 接收音频数据 + synthesis_completed = False + while not synthesis_completed: + msg = await ws.recv() + if isinstance(msg, (bytes, bytearray)): + # 编码为Opus并收集 + opus_frames = self.opus_encoder.encode_pcm_to_opus( + msg, False + ) + audio_data.extend(opus_frames) + elif isinstance(msg, str): + data = json.loads(msg) + header = data.get("header", {}) + event_name = header.get("name") + if event_name == "SynthesisCompleted": + synthesis_completed = True + logger.bind(tag=TAG).debug("TTS合成完成") + elif event_name == "TaskFailed": + error_info = data.get("payload", {}).get( + "error_info", {} + ) + error_code = error_info.get("error_code") + error_message = error_info.get( + "error_message", "未知错误" + ) + raise Exception( + f"合成失败: {error_code} - {error_message}" + ) + finally: + try: + await ws.close() + except: + pass + + loop.run_until_complete(_generate_audio()) + loop.close() + + return audio_data + except Exception as e: + logger.bind(tag=TAG).error(f"生成音频数据失败: {str(e)}") + return [] diff --git a/main/xiaozhi-server/core/providers/tts/base.py b/main/xiaozhi-server/core/providers/tts/base.py index a65fe194..40570326 100644 --- a/main/xiaozhi-server/core/providers/tts/base.py +++ b/main/xiaozhi-server/core/providers/tts/base.py @@ -4,14 +4,12 @@ import queue import uuid import asyncio import threading -from typing import Callable, Any from core.utils import p3 -import time from datetime import datetime from core.utils import textUtils from abc import ABC, abstractmethod from config.logger import setup_logging -from core.utils.util import audio_bytes_to_data_stream, audio_to_data_stream +from core.utils.util import audio_to_data, audio_bytes_to_data from core.utils.tts import MarkdownCleaner from core.utils.output_counter import add_device_output from core.handle.reportHandle import enqueue_tts_report @@ -33,6 +31,7 @@ class TTSProviderBase(ABC): def __init__(self, config, delete_audio_file): self.interface_type = InterfaceType.NON_STREAM self.conn = None + self.tts_timeout = 10 self.delete_audio_file = delete_audio_file self.audio_file_type = "wav" self.output_file = config.get("output_dir", "tmp/") @@ -51,9 +50,11 @@ class TTSProviderBase(ABC): ";", ";", ":", + "~", ) self.first_sentence_punctuations = ( ",", + "~", "~", "、", ",", @@ -76,14 +77,7 @@ class TTSProviderBase(ABC): f"tts-{datetime.now().date()}@{uuid.uuid4().hex}{extension}", ) - def handle_opus(self, opus_data: bytes): - logger.bind(tag=TAG).debug(f"推送数据到队列里面帧数~~ {len(opus_data)}") - self.tts_audio_queue.put((SentenceType.MIDDLE, opus_data, None)) - - def handle_audio_file(self, file_audio: bytes, text): - self.before_stop_play_files.append((file_audio, text)) - - def to_tts_stream(self, text, opus_handler: Callable[[bytes], None] = None) -> None: + def to_tts(self, text): text = MarkdownCleaner.clean_markdown(text) max_repeat_time = 5 if self.delete_audio_file: @@ -92,14 +86,10 @@ class TTSProviderBase(ABC): try: audio_bytes = asyncio.run(self.text_to_speak(text, None)) if audio_bytes: - self.tts_audio_queue.put((SentenceType.FIRST, None, text)) - audio_bytes_to_data_stream( - audio_bytes, - file_type=self.audio_file_type, - is_opus=True, - callback=opus_handler, + audio_datas, _ = audio_bytes_to_data( + audio_bytes, file_type=self.audio_file_type, is_opus=True ) - break + return audio_datas else: max_repeat_time -= 1 except Exception as e: @@ -139,8 +129,8 @@ class TTSProviderBase(ABC): logger.bind(tag=TAG).error( f"语音生成失败: {text},请检查网络或服务是否正常" ) - self.tts_audio_queue.put((SentenceType.FIRST, None, text)) - self._process_audio_file_stream(tmp_file, callback=opus_handler) + + return tmp_file except Exception as e: logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}") return None @@ -149,17 +139,13 @@ class TTSProviderBase(ABC): async def text_to_speak(self, text, output_file): pass - def audio_to_pcm_data_stream( - self, audio_file_path, callback: Callable[[Any], Any] = None - ): + def audio_to_pcm_data(self, audio_file_path): """音频文件转换为PCM编码""" - return audio_to_data_stream(audio_file_path, is_opus=False, callback=callback) + return audio_to_data(audio_file_path, is_opus=False) - def audio_to_opus_data_stream( - self, audio_file_path, callback: Callable[[Any], Any] = None - ): + def audio_to_opus_data(self, audio_file_path): """音频文件转换为Opus编码""" - return audio_to_data_stream(audio_file_path, is_opus=True, callback=callback) + return audio_to_data(audio_file_path, is_opus=True) def tts_one_sentence( self, @@ -191,6 +177,7 @@ class TTSProviderBase(ABC): async def open_audio_channels(self, conn): self.conn = conn + self.tts_timeout = conn.config.get("tts_timeout", 10) # tts 消化线程 self.tts_priority_thread = threading.Thread( target=self.tts_text_priority_thread, daemon=True @@ -225,16 +212,30 @@ class TTSProviderBase(ABC): self.tts_text_buff.append(message.content_detail) segment_text = self._get_segment_text() if segment_text: - self.to_tts_stream(segment_text, opus_handler=self.handle_opus) + if self.delete_audio_file: + audio_datas = self.to_tts(segment_text) + if audio_datas: + self.tts_audio_queue.put( + (message.sentence_type, audio_datas, segment_text) + ) + else: + tts_file = self.to_tts(segment_text) + if tts_file: + audio_datas = self._process_audio_file(tts_file) + self.tts_audio_queue.put( + (message.sentence_type, audio_datas, segment_text) + ) elif ContentType.FILE == message.content_type: - self._process_remaining_text_stream(opus_handler=self.handle_opus) + self._process_remaining_text() tts_file = message.content_file if tts_file and os.path.exists(tts_file): - self._process_audio_file_stream( - tts_file, callback=self.handle_opus + audio_datas = self._process_audio_file(tts_file) + self.tts_audio_queue.put( + (message.sentence_type, audio_datas, message.content_detail) ) + if message.sentence_type == SentenceType.LAST: - self._process_remaining_text_stream(opus_handler=self.handle_opus) + self._process_remaining_text() self.tts_audio_queue.put( (message.sentence_type, [], message.content_detail) ) @@ -248,59 +249,29 @@ class TTSProviderBase(ABC): continue def _audio_play_priority_thread(self): - # 需要上报的文本和音频列表 - enqueue_text = None - enqueue_audio = None while not self.conn.stop_event.is_set(): text = None try: try: sentence_type, audio_datas, text = self.tts_audio_queue.get( - timeout=0.1 + timeout=1 ) except queue.Empty: if self.conn.stop_event.is_set(): break continue - - if self.conn.client_abort: - logger.bind(tag=TAG).debug("收到打断信号,跳过当前音频数据") - enqueue_text, enqueue_audio = None, [] - continue - - # 收到下一个文本开始或会话结束时进行上报 - if sentence_type is not SentenceType.MIDDLE: - # 重置音频流控状态(新句子开始或者结束) - if hasattr(self.conn, 'audio_flow_control'): - self.conn.audio_flow_control = { - 'last_send_time': 0, - 'packet_count': 0, - 'start_time': time.perf_counter() - } - - # 上报TTS数据 - if enqueue_text is not None and enqueue_audio is not None: - enqueue_tts_report(self.conn, enqueue_text, enqueue_audio) - enqueue_audio = [] - enqueue_text = text - - # 收集上报音频数据 - if isinstance(audio_datas, bytes) and enqueue_audio is not None: - enqueue_audio.append(audio_datas) - - # 发送音频 future = asyncio.run_coroutine_threadsafe( sendAudioMessage(self.conn, sentence_type, audio_datas, text), self.conn.loop, ) future.result() - - # 记录输出和报告 if self.conn.max_output_size > 0 and text: add_device_output(self.conn.headers.get("device-id"), len(text)) - + enqueue_tts_report(self.conn, text, audio_datas) except Exception as e: - logger.bind(tag=TAG).error(f"audio_play_priority_thread: {text} {e}") + logger.bind(tag=TAG).error( + f"audio_play_priority priority_thread: {text} {e}" + ) async def start_session(self, session_id): pass @@ -352,21 +323,22 @@ class TTSProviderBase(ABC): else: return None - def _process_audio_file_stream( - self, tts_file, callback: Callable[[Any], Any] - ) -> None: + def _process_audio_file(self, tts_file): """处理音频文件并转换为指定格式 Args: tts_file: 音频文件路径 - callback: 文件处理函数 + content_detail: 内容详情 + + Returns: + tuple: (sentence_type, audio_datas, content_detail) """ if tts_file.endswith(".p3"): - p3.decode_opus_from_file_stream(tts_file, callback=callback) + audio_datas, _ = p3.decode_opus_from_file(tts_file) elif self.conn.audio_format == "pcm": - self.audio_to_pcm_data_stream(tts_file, callback=callback) + audio_datas, _ = self.audio_to_pcm_data(tts_file) else: - self.audio_to_opus_data_stream(tts_file, callback=callback) + audio_datas, _ = self.audio_to_opus_data(tts_file) if ( self.delete_audio_file @@ -375,6 +347,7 @@ class TTSProviderBase(ABC): and tts_file.startswith(self.output_file) ): os.remove(tts_file) + return audio_datas def _process_before_stop_play_files(self): for audio_datas, text in self.before_stop_play_files: @@ -382,9 +355,7 @@ class TTSProviderBase(ABC): self.before_stop_play_files.clear() self.tts_audio_queue.put((SentenceType.LAST, [], None)) - def _process_remaining_text_stream( - self, opus_handler: Callable[[bytes], None] = None - ): + def _process_remaining_text(self): """处理剩余的文本并生成语音 Returns: @@ -395,7 +366,18 @@ class TTSProviderBase(ABC): if remaining_text: segment_text = textUtils.get_string_no_punctuation_or_emoji(remaining_text) if segment_text: - self.to_tts_stream(segment_text, opus_handler=opus_handler) + if self.delete_audio_file: + audio_datas = self.to_tts(segment_text) + if audio_datas: + self.tts_audio_queue.put( + (SentenceType.MIDDLE, audio_datas, segment_text) + ) + else: + tts_file = self.to_tts(segment_text) + audio_datas = self._process_audio_file(tts_file) + self.tts_audio_queue.put( + (SentenceType.MIDDLE, audio_datas, segment_text) + ) self.processed_chars += len(full_text) return True return False diff --git a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py index 463c16fc..33e2750d 100644 --- a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py +++ b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py @@ -4,7 +4,6 @@ import json import queue import asyncio import traceback -from typing import Callable, Any import websockets from core.utils.tts import MarkdownCleaner from config.logger import setup_logging @@ -267,7 +266,11 @@ class TTSProvider(TTSProviderBase): ) if message.content_file and os.path.exists(message.content_file): # 先处理文件音频数据 - self._process_audio_file_stream(message.content_file, callback=lambda audio_data: self.handle_audio_file(audio_data, message.content_detail)) + file_audio = self._process_audio_file(message.content_file) + self.before_stop_play_files.append( + (file_audio, message.content_detail) + ) + if message.sentence_type == SentenceType.LAST: try: logger.bind(tag=TAG).info("开始结束TTS会话...") @@ -425,6 +428,9 @@ class TTSProvider(TTSProviderBase): async def _start_monitor_tts_response(self): """监听TTS响应""" + opus_datas_cache = [] + is_first_sentence = True + first_sentence_segment_count = 0 # 添加计数器 try: session_finished = False # 标记会话是否正常结束 while not self.conn.stop_event.is_set(): @@ -445,14 +451,37 @@ class TTSProvider(TTSProviderBase): self.tts_audio_queue.put( (SentenceType.FIRST, [], self.tts_text) ) + opus_datas_cache = [] + first_sentence_segment_count = 0 # 重置计数器 elif ( res.optional.event == EVENT_TTSResponse and res.header.message_type == AUDIO_ONLY_RESPONSE ): logger.bind(tag=TAG).debug(f"推送数据到队列里面~~") - self.wav_to_opus_data_audio_raw_stream(res.payload, callback=self.handle_opus) + opus_datas = self.wav_to_opus_data_audio_raw(res.payload) + logger.bind(tag=TAG).debug( + f"推送数据到队列里面帧数~~{len(opus_datas)}" + ) + if is_first_sentence: + first_sentence_segment_count += 1 + if first_sentence_segment_count <= 6: + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas, None) + ) + else: + opus_datas_cache.extend(opus_datas) + else: + # 后续句子缓存 + opus_datas_cache.extend(opus_datas) elif res.optional.event == EVENT_TTSSentenceEnd: logger.bind(tag=TAG).info(f"句子语音生成成功:{self.tts_text}") + if not is_first_sentence or first_sentence_segment_count > 10: + # 发送缓存的数据 + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas_cache, None) + ) + # 第一句话结束后,将标志设置为False + is_first_sentence = False elif res.optional.event == EVENT_SessionFinished: logger.bind(tag=TAG).debug(f"会话结束~~") self._process_before_stop_play_files() @@ -626,5 +655,110 @@ class TTSProvider(TTSProviderBase): ) ) - def wav_to_opus_data_audio_raw_stream(self, raw_data_var, is_end=False, callback: Callable[[Any], Any]=None): - return self.opus_encoder.encode_pcm_to_opus_stream(raw_data_var, is_end, callback=callback) + def wav_to_opus_data_audio_raw(self, raw_data_var, is_end=False): + opus_datas = self.opus_encoder.encode_pcm_to_opus(raw_data_var, is_end) + return opus_datas + + def to_tts(self, text: str) -> list: + """非流式生成音频数据,用于生成音频及测试场景 + + Args: + text: 要转换的文本 + + Returns: + list: 音频数据列表 + """ + try: + # 创建事件循环 + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # 生成会话ID + session_id = uuid.uuid4().__str__().replace("-", "") + + # 存储音频数据 + audio_data = [] + + async def _generate_audio(): + # 创建新的WebSocket连接 + ws_header = { + "X-Api-App-Key": self.appId, + "X-Api-Access-Key": self.access_token, + "X-Api-Resource-Id": self.resource_id, + "X-Api-Connect-Id": uuid.uuid4(), + } + ws = await websockets.connect( + self.ws_url, additional_headers=ws_header, max_size=1000000000 + ) + + try: + # 启动会话 + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_StartSession, sessionId=session_id + ).as_bytes() + payload = self.get_payload_bytes( + event=EVENT_StartSession, speaker=self.voice + ) + await self.send_event(ws, header, optional, payload) + + # 发送文本 + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_TaskRequest, sessionId=session_id + ).as_bytes() + payload = self.get_payload_bytes( + event=EVENT_TaskRequest, text=text, speaker=self.voice + ) + await self.send_event(ws, header, optional, payload) + + # 发送结束会话请求 + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_FinishSession, sessionId=session_id + ).as_bytes() + payload = str.encode("{}") + await self.send_event(ws, header, optional, payload) + + # 接收音频数据 + while True: + msg = await ws.recv() + res = self.parser_response(msg) + + if ( + res.optional.event == EVENT_TTSResponse + and res.header.message_type == AUDIO_ONLY_RESPONSE + ): + opus_datas = self.wav_to_opus_data_audio_raw(res.payload) + audio_data.extend(opus_datas) + elif res.optional.event == EVENT_SessionFinished: + break + + finally: + # 清理资源 + try: + await ws.close() + except: + pass + + # 运行异步任务 + loop.run_until_complete(_generate_audio()) + loop.close() + + return audio_data + + except Exception as e: + logger.bind(tag=TAG).error(f"生成音频数据失败: {str(e)}") + return [] diff --git a/main/xiaozhi-server/core/providers/tts/index_stream.py b/main/xiaozhi-server/core/providers/tts/index_stream.py index 6f6b829c..5ea9eb36 100644 --- a/main/xiaozhi-server/core/providers/tts/index_stream.py +++ b/main/xiaozhi-server/core/providers/tts/index_stream.py @@ -3,6 +3,8 @@ import queue import asyncio import traceback import aiohttp +import requests +import time from config.logger import setup_logging from core.utils.tts import MarkdownCleaner from core.providers.tts.base import TTSProviderBase @@ -25,13 +27,15 @@ class TTSProvider(TTSProviderBase): self.api_url = config.get("api_url", "http://8.138.114.124:11996/tts") self.audio_format = "pcm" self.before_stop_play_files = [] + self.segment_count = 0 # 创建Opus编码器 需注意接口返回的采样率为24000 self.opus_encoder = opus_encoder_utils.OpusEncoderUtils( sample_rate=24000, channels=1, frame_size_ms=60 ) - # PCM缓冲区 + # 文本缓冲区和PCM缓冲区 + self.text_buffer = "" self.pcm_buffer = bytearray() def tts_text_priority_thread(self): @@ -44,6 +48,7 @@ class TTSProvider(TTSProviderBase): self.tts_stop_request = False self.processed_chars = 0 self.tts_text_buff = [] + self.segment_count = 0 self.before_stop_play_files.clear() elif ContentType.TEXT == message.content_type: self.tts_text_buff.append(message.content_detail) @@ -57,11 +62,14 @@ class TTSProvider(TTSProviderBase): ) if message.content_file and os.path.exists(message.content_file): # 先处理文件音频数据 - self._process_audio_file_stream(message.content_file, callback=lambda audio_data: self.handle_audio_file(audio_data, message.content_detail)) + file_audio = self._process_audio_file(message.content_file) + self.before_stop_play_files.append( + (file_audio, message.content_detail) + ) if message.sentence_type == SentenceType.LAST: # 处理剩余的文本 - self._process_remaining_text_stream(True) + self._process_remaining_text(True) except queue.Empty: continue @@ -70,7 +78,7 @@ class TTSProvider(TTSProviderBase): f"处理TTS文本失败: {str(e)}, 类型: {type(e).__name__}, 堆栈: {traceback.format_exc()}" ) - def _process_remaining_text_stream(self, is_last=False): + def _process_remaining_text(self, is_last=False): """处理剩余的文本并生成语音 Returns: bool: 是否成功处理了文本 @@ -135,6 +143,8 @@ class TTSProvider(TTSProviderBase): return self.pcm_buffer.clear() + opus_datas_cache = [] + self.tts_audio_queue.put((SentenceType.FIRST, [], text)) # 处理音频流数据 @@ -148,22 +158,41 @@ class TTSProvider(TTSProviderBase): while len(self.pcm_buffer) >= frame_bytes: frame = bytes(self.pcm_buffer[:frame_bytes]) del self.pcm_buffer[:frame_bytes] - - self.opus_encoder.encode_pcm_to_opus_stream( - frame, - end_of_stream=False, - callback=self.handle_opus + opus = self.opus_encoder.encode_pcm_to_opus( + frame, end_of_stream=False ) + if opus: + if self.segment_count < 10: # 前10个片段直接发送 + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus, None) + ) + self.segment_count += 1 + else: + opus_datas_cache.extend(opus) # flush 剩余不足一帧的数据 if self.pcm_buffer: - self.opus_encoder.encode_pcm_to_opus_stream( - bytes(self.pcm_buffer), - end_of_stream=True, - callback=self.handle_opus + opus = self.opus_encoder.encode_pcm_to_opus( + bytes(self.pcm_buffer), end_of_stream=True ) + if opus: + if self.segment_count < 10: # 前10个片段直接发送 + # 直接发送 + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus, None) + ) + self.segment_count += 1 + else: + # 后续片段缓存 + opus_datas_cache.extend(opus) self.pcm_buffer.clear() + # 如果不是前10个片段,发送缓存的数据 + if self.segment_count >= 10 and opus_datas_cache: + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas_cache, None) + ) + # 如果是最后一段,输出音频获取完毕 if is_last: self._process_before_stop_play_files() @@ -177,3 +206,59 @@ class TTSProvider(TTSProviderBase): await super().close() if hasattr(self, "opus_encoder"): self.opus_encoder.close() + + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景 + + Args: + text: 要转换的文本 + + Returns: + list: 返回opus编码后的音频数据列表 + """ + start_time = time.time() + text = MarkdownCleaner.clean_markdown(text) + + payload = {"text": text, "character": self.character} + + try: + with requests.post(self.api_url, json=payload, timeout=5) as response: + if response.status_code != 200: + logger.bind(tag=TAG).error( + f"TTS请求失败: {response.status_code}, {response.text}" + ) + return [] + + logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒") + + # 使用opus编码器处理PCM数据 + opus_datas = [] + pcm_data = response.content + + # 计算每帧的字节数 + frame_bytes = int( + self.opus_encoder.sample_rate + * self.opus_encoder.channels + * self.opus_encoder.frame_size_ms + / 1000 + * 2 + ) + + # 分帧处理PCM数据 + for i in range(0, len(pcm_data), frame_bytes): + frame = pcm_data[i : i + frame_bytes] + if len(frame) < frame_bytes: + # 最后一帧可能不足,用0填充 + frame = frame + b"\x00" * (frame_bytes - len(frame)) + + opus = self.opus_encoder.encode_pcm_to_opus( + frame, end_of_stream=(i + frame_bytes >= len(pcm_data)) + ) + if opus: + opus_datas.extend(opus) + + return opus_datas + + except Exception as e: + logger.bind(tag=TAG).error(f"TTS请求异常: {e}") + return [] diff --git a/main/xiaozhi-server/core/providers/tts/linkerai.py b/main/xiaozhi-server/core/providers/tts/linkerai.py index 04e83148..d1be586c 100644 --- a/main/xiaozhi-server/core/providers/tts/linkerai.py +++ b/main/xiaozhi-server/core/providers/tts/linkerai.py @@ -1,8 +1,10 @@ import os import queue -import aiohttp import asyncio import traceback +import aiohttp +import requests +import time from config.logger import setup_logging from core.utils.tts import MarkdownCleaner from core.providers.tts.base import TTSProviderBase @@ -22,15 +24,23 @@ class TTSProvider(TTSProviderBase): self.api_url = config.get("api_url") self.audio_format = "pcm" self.before_stop_play_files = [] + self.segment_count = 0 # 添加片段计数器 # 创建Opus编码器 self.opus_encoder = opus_encoder_utils.OpusEncoderUtils( sample_rate=16000, channels=1, frame_size_ms=60 ) + # 添加文本缓冲区 + self.text_buffer = "" + # PCM缓冲区 self.pcm_buffer = bytearray() + ################################################################################### + # linkerai单流式TTS重写父类的方法--开始 + ################################################################################### + def tts_text_priority_thread(self): """流式文本处理线程""" while not self.conn.stop_event.is_set(): @@ -41,6 +51,7 @@ class TTSProvider(TTSProviderBase): self.tts_stop_request = False self.processed_chars = 0 self.tts_text_buff = [] + self.segment_count = 0 self.before_stop_play_files.clear() elif ContentType.TEXT == message.content_type: self.tts_text_buff.append(message.content_detail) @@ -54,10 +65,14 @@ class TTSProvider(TTSProviderBase): ) if message.content_file and os.path.exists(message.content_file): # 先处理文件音频数据 - self._process_audio_file_stream(message.content_file, callback=lambda audio_data: self.handle_audio_file(audio_data, message.content_detail)) + file_audio = self._process_audio_file(message.content_file) + self.before_stop_play_files.append( + (file_audio, message.content_detail) + ) + if message.sentence_type == SentenceType.LAST: # 处理剩余的文本 - self._process_remaining_text_stream(True) + self._process_remaining_text(True) except queue.Empty: continue @@ -66,7 +81,7 @@ class TTSProvider(TTSProviderBase): f"处理TTS文本失败: {str(e)}, 类型: {type(e).__name__}, 堆栈: {traceback.format_exc()}" ) - def _process_remaining_text_stream(self, is_last=False): + def _process_remaining_text(self, is_last=False): """处理剩余的文本并生成语音 Returns: @@ -109,6 +124,10 @@ class TTSProvider(TTSProviderBase): finally: return None + ################################################################################### + # linkerai单流式TTS重写父类的方法--结束 + ################################################################################### + async def text_to_speak(self, text, is_last): """流式处理TTS音频,每句只推送一次音频列表""" await self._tts_request(text, is_last) @@ -157,6 +176,8 @@ class TTSProvider(TTSProviderBase): return self.pcm_buffer.clear() + opus_datas_cache = [] + self.tts_audio_queue.put((SentenceType.FIRST, [], text)) # 兼容 iter_chunked / iter_chunks / iter_any @@ -173,21 +194,41 @@ class TTSProvider(TTSProviderBase): frame = bytes(self.pcm_buffer[:frame_bytes]) del self.pcm_buffer[:frame_bytes] - self.opus_encoder.encode_pcm_to_opus_stream( - frame, - end_of_stream=False, - callback=self.handle_opus + opus = self.opus_encoder.encode_pcm_to_opus( + frame, end_of_stream=False ) + if opus: + if self.segment_count < 10: # 前10个片段直接发送 + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus, None) + ) + self.segment_count += 1 + else: + opus_datas_cache.extend(opus) # flush 剩余不足一帧的数据 if self.pcm_buffer: - self.opus_encoder.encode_pcm_to_opus_stream( - bytes(self.pcm_buffer), - end_of_stream=True, - callback=self.handle_opus + opus = self.opus_encoder.encode_pcm_to_opus( + bytes(self.pcm_buffer), end_of_stream=True ) + if opus: + if self.segment_count < 10: # 前10个片段直接发送 + # 直接发送 + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus, None) + ) + self.segment_count += 1 + else: + # 后续片段缓存 + opus_datas_cache.extend(opus) self.pcm_buffer.clear() + # 如果不是前10个片段,发送缓存的数据 + if self.segment_count >= 10 and opus_datas_cache: + self.tts_audio_queue.put( + (SentenceType.MIDDLE, opus_datas_cache, None) + ) + # 如果是最后一段,输出音频获取完毕 if is_last: self._process_before_stop_play_files() @@ -195,3 +236,73 @@ class TTSProvider(TTSProviderBase): except Exception as e: logger.bind(tag=TAG).error(f"TTS请求异常: {e}") self.tts_audio_queue.put((SentenceType.LAST, [], None)) + + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景 + + Args: + text: 要转换的文本 + + Returns: + list: 返回opus编码后的音频数据列表 + """ + start_time = time.time() + text = MarkdownCleaner.clean_markdown(text) + + params = { + "tts_text": text, + "spk_id": self.voice, + "frame_duration": 60, + "stream": False, + "target_sr": 16000, + "audio_format": self.audio_format, + "instruct_text": "请生成一段自然流畅的语音", + } + headers = { + "Authorization": f"Bearer {self.access_token}", + "Content-Type": "application/json", + } + + try: + with requests.get( + self.api_url, params=params, headers=headers, timeout=5 + ) as response: + if response.status_code != 200: + logger.bind(tag=TAG).error( + f"TTS请求失败: {response.status_code}, {response.text}" + ) + return [] + + logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒") + + # 使用opus编码器处理PCM数据 + opus_datas = [] + pcm_data = response.content + + # 计算每帧的字节数 + frame_bytes = int( + self.opus_encoder.sample_rate + * self.opus_encoder.channels + * self.opus_encoder.frame_size_ms + / 1000 + * 2 + ) + + # 分帧处理PCM数据 + for i in range(0, len(pcm_data), frame_bytes): + frame = pcm_data[i : i + frame_bytes] + if len(frame) < frame_bytes: + # 最后一帧可能不足,用0填充 + frame = frame + b"\x00" * (frame_bytes - len(frame)) + + opus = self.opus_encoder.encode_pcm_to_opus( + frame, end_of_stream=(i + frame_bytes >= len(pcm_data)) + ) + if opus: + opus_datas.extend(opus) + + return opus_datas + + except Exception as e: + logger.bind(tag=TAG).error(f"TTS请求异常: {e}") + return [] diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 4b987b71..7f1c6a23 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -8,7 +8,6 @@ import wave import websockets from core.providers.tts.base import TTSProviderBase from config.logger import setup_logging -from datetime import datetime TAG = __name__ logger = setup_logging() @@ -19,12 +18,11 @@ class TTSProvider(TTSProviderBase): super().__init__(config, delete_audio_file) self.url = config.get("url", "ws://192.168.1.10:8092/paddlespeech/tts/streaming") self.protocol = config.get("protocol", "websocket") - if config.get("private_voice"): self.spk_id = int(config.get("private_voice")) else: - self.spk_id = int(config.get("spk_id", "0")) - + self.spk_id = int(config.get("spk_id", "0")) + sample_rate = config.get("sample_rate", 24000) self.sample_rate = float(sample_rate) if sample_rate else 24000 @@ -34,21 +32,7 @@ class TTSProvider(TTSProviderBase): volume = config.get("volume", 1.0) self.volume = float(volume) if volume else 1.0 - self.delete_audio_file = config.get("delete_audio", True) - if not self.delete_audio_file: - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - save_path = config.get("save_path") - if save_path: - if not save_path.endswith('.wav'): - save_path = f"{save_path}_{timestamp}.wav" - else: - other_path = save_path[:-4] - save_path = f"{other_path}_{timestamp}.wav" - self.save_path = save_path - else: - self.save_path = f"./streaming_tts_{timestamp}.wav" - else: - self.save_path = None + self.save_path = config.get("save_path", "./streaming_tts.wav") async def pcm_to_wav(self, pcm_data: bytes, sample_rate: int = 24000, num_channels: int = 1, bits_per_sample: int = 16) -> bytes: @@ -167,12 +151,6 @@ class TTSProvider(TTSProviderBase): # 接收结束响应避免服务抛出异常 await ws.recv() - # 根据配置决定是否保存文件 - if not self.delete_audio_file and self.save_path: - with open(self.save_path, "wb") as f: - f.write(wav_data) - logger.bind(tag=TAG).info(f"音频文件已保存到: {self.save_path}") - # 返回或保存音频数据 if output_file: with open(output_file, "wb") as file_to_save: @@ -181,4 +159,4 @@ class TTSProvider(TTSProviderBase): return wav_data except Exception as e: - raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") \ No newline at end of file + raise Exception(f"Error during TTS WebSocket request: {e} while processing text: {text}") diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index 95ab8ff3..80e65b55 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -33,7 +33,7 @@ class VADProvider(VADProviderBase): int(min_silence_duration_ms) if min_silence_duration_ms else 1000 ) - # 至少要多少帧才算有语音,增加灵敏度 + # 至少要多少帧才算有语音 self.frame_window_threshold = 1 def is_vad(self, conn, opus_packet): @@ -70,7 +70,9 @@ class VADProvider(VADProviderBase): # 更新滑动窗口 conn.client_voice_window.append(is_voice) - client_have_voice = (conn.client_voice_window.count(True) >= self.frame_window_threshold) + client_have_voice = ( + conn.client_voice_window.count(True) >= self.frame_window_threshold + ) # 如果之前有声音,但本次没有声音,且与上次有声音的时间差已经超过了静默阈值,则认为已经说完一句话 if conn.client_have_voice and not client_have_voice: diff --git a/main/xiaozhi-server/core/utils/opus_encoder_utils.py b/main/xiaozhi-server/core/utils/opus_encoder_utils.py index 8ca406d3..23d26c39 100644 --- a/main/xiaozhi-server/core/utils/opus_encoder_utils.py +++ b/main/xiaozhi-server/core/utils/opus_encoder_utils.py @@ -5,8 +5,9 @@ Opus编码工具类 import logging import traceback + import numpy as np -from typing import Optional, Callable, Any +from typing import List, Optional from opuslib_next import Encoder from opuslib_next import constants @@ -55,14 +56,13 @@ class OpusEncoderUtils: self.encoder.reset_state() self.buffer = np.array([], dtype=np.int16) - def encode_pcm_to_opus_stream(self, pcm_data: bytes, end_of_stream: bool, callback: Callable[[Any], Any]): + def encode_pcm_to_opus(self, pcm_data: bytes, end_of_stream: bool) -> List[bytes]: """ - 将PCM数据编码为Opus格式,以流式方式进行处理 + 将PCM数据编码为Opus格式 Args: pcm_data: PCM字节数据 - end_of_stream: 是否为流的结束, - callback: opus处理方法 + end_of_stream: 是否为流的结束 Returns: Opus数据包列表 @@ -76,6 +76,7 @@ class OpusEncoderUtils: # 将新数据追加到缓冲区 self.buffer = np.append(self.buffer, new_samples) + opus_packets = [] offset = 0 # 处理所有完整帧 @@ -83,7 +84,7 @@ class OpusEncoderUtils: frame = self.buffer[offset : offset + self.total_frame_size] output = self._encode(frame) if output: - callback(output) + opus_packets.append(output) offset += self.total_frame_size # 保留未处理的样本 @@ -97,9 +98,11 @@ class OpusEncoderUtils: output = self._encode(last_frame) if output: - callback(output) + opus_packets.append(output) self.buffer = np.array([], dtype=np.int16) + return opus_packets + def _encode(self, frame: np.ndarray) -> Optional[bytes]: """编码一帧音频数据""" try: diff --git a/main/xiaozhi-server/core/utils/p3.py b/main/xiaozhi-server/core/utils/p3.py index 415e5366..c75b968e 100644 --- a/main/xiaozhi-server/core/utils/p3.py +++ b/main/xiaozhi-server/core/utils/p3.py @@ -1,12 +1,15 @@ -import io import struct -from typing import Callable, Any +def decode_opus_from_file(input_file): + """ + 从p3文件中解码 Opus 数据,并返回一个 Opus 数据包的列表以及总时长。 + """ + opus_datas = [] + total_frames = 0 + sample_rate = 16000 # 文件采样率 + frame_duration_ms = 60 # 帧时长 + frame_size = int(sample_rate * frame_duration_ms / 1000) -def decode_opus_from_file_stream(input_file, callback: Callable[[Any], Any]): - """ - 从p3文件中解码 Opus 数据,由 callback 处理 Opus 数据包。 - """ with open(input_file, 'rb') as f: while True: # 读取头部(4字节):[1字节类型,1字节保留,2字节长度] @@ -22,13 +25,23 @@ def decode_opus_from_file_stream(input_file, callback: Callable[[Any], Any]): if len(opus_data) != data_len: raise ValueError(f"Data length({len(opus_data)}) mismatch({data_len}) in the file.") - callback(opus_data) + opus_datas.append(opus_data) + total_frames += 1 + # 计算总时长 + total_duration = (total_frames * frame_duration_ms) / 1000.0 + return opus_datas, total_duration -def decode_opus_from_bytes_stream(input_bytes, callback: Callable[[Any], Any]): +def decode_opus_from_bytes(input_bytes): """ - 从p3二进制数据中解码 Opus 数据,由 callback 处理 Opus 数据包。 + 从p3二进制数据中解码 Opus 数据,并返回一个 Opus 数据包的列表以及总时长。 """ + import io + opus_datas = [] + total_frames = 0 + sample_rate = 16000 # 文件采样率 + frame_duration_ms = 60 # 帧时长 + frame_size = int(sample_rate * frame_duration_ms / 1000) f = io.BytesIO(input_bytes) while True: @@ -39,4 +52,8 @@ def decode_opus_from_bytes_stream(input_bytes, callback: Callable[[Any], Any]): opus_data = f.read(data_len) if len(opus_data) != data_len: raise ValueError(f"Data length({len(opus_data)}) mismatch({data_len}) in the bytes.") - callback(opus_data) + opus_datas.append(opus_data) + total_frames += 1 + + total_duration = (total_frames * frame_duration_ms) / 1000.0 + return opus_datas, total_duration \ No newline at end of file diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index a608049c..ce6f20ea 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -1,17 +1,16 @@ +import json +import socket +import subprocess import re import os -import json -import copy -import socket -import requests -import subprocess -import numpy as np -import opuslib_next +import wave from io import BytesIO from core.utils import p3 +import numpy as np +import requests +import opuslib_next from pydub import AudioSegment -from typing import Callable, Any -from core.providers.tts.dto.dto import SentenceType +import copy TAG = __name__ emoji_map = { @@ -212,7 +211,7 @@ def extract_json_from_string(input_string): return None -def audio_to_data_stream(audio_file_path, is_opus=True, callback: Callable[[Any], Any]=None) -> None: +def audio_to_data(audio_file_path, is_opus=True): # 获取文件后缀名 file_type = os.path.splitext(audio_file_path)[1] if file_type: @@ -225,29 +224,33 @@ def audio_to_data_stream(audio_file_path, is_opus=True, callback: Callable[[Any] # 转换为单声道/16kHz采样率/16位小端编码(确保与编码器匹配) audio = audio.set_channels(1).set_frame_rate(16000).set_sample_width(2) + # 音频时长(秒) + duration = len(audio) / 1000.0 + # 获取原始PCM数据(16位小端) raw_data = audio.raw_data - pcm_to_data_stream(raw_data, is_opus, callback) + return pcm_to_data(raw_data, is_opus), duration -def audio_bytes_to_data_stream(audio_bytes, file_type, is_opus, callback: Callable[[Any], Any]) -> None: +def audio_bytes_to_data(audio_bytes, file_type, is_opus=True): """ 直接用音频二进制数据转为opus/pcm数据,支持wav、mp3、p3 """ if file_type == "p3": # 直接用p3解码 - return p3.decode_opus_from_bytes_stream(audio_bytes, callback) + return p3.decode_opus_from_bytes(audio_bytes) else: # 其他格式用pydub audio = AudioSegment.from_file( BytesIO(audio_bytes), format=file_type, parameters=["-nostdin"] ) audio = audio.set_channels(1).set_frame_rate(16000).set_sample_width(2) + duration = len(audio) / 1000.0 raw_data = audio.raw_data - pcm_to_data_stream(raw_data, is_opus, callback) + return pcm_to_data(raw_data, is_opus), duration -def pcm_to_data_stream(raw_data, is_opus=True, callback: Callable[[Any], Any] = None): +def pcm_to_data(raw_data, is_opus=True): # 初始化Opus编码器 encoder = opuslib_next.Encoder(16000, 1, opuslib_next.APPLICATION_AUDIO) @@ -255,6 +258,7 @@ def pcm_to_data_stream(raw_data, is_opus=True, callback: Callable[[Any], Any] = frame_duration = 60 # 60ms per frame frame_size = int(16000 * frame_duration / 1000) # 960 samples/frame + datas = [] # 按帧处理所有音频数据(包括最后一帧可能补零) for i in range(0, len(raw_data), frame_size * 2): # 16bit=2bytes/sample # 获取当前帧的二进制数据 @@ -269,28 +273,40 @@ def pcm_to_data_stream(raw_data, is_opus=True, callback: Callable[[Any], Any] = np_frame = np.frombuffer(chunk, dtype=np.int16) # 编码Opus数据 frame_data = encoder.encode(np_frame.tobytes(), frame_size) - callback(frame_data) else: frame_data = chunk if isinstance(chunk, bytes) else bytes(chunk) - callback(frame_data) -def play_audio_response(conn, response): - """音频响应处理""" - conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], response.get("text"))) - play_audio_frames(conn, response.get("file_path")) - conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) + datas.append(frame_data) + + return datas -def play_audio_frames(conn, file_path): - """播放音频文件并处理发送帧数据""" - def handle_audio_frame(frame_data): - conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, frame_data, None)) +def opus_datas_to_wav_bytes(opus_datas, sample_rate=16000, channels=1): + """ + 将opus帧列表解码为wav字节流 + """ + decoder = opuslib_next.Decoder(sample_rate, channels) + pcm_datas = [] + + frame_duration = 60 # ms + frame_size = int(sample_rate * frame_duration / 1000) # 960 + + for opus_frame in opus_datas: + # 解码为PCM(返回bytes,2字节/采样点) + pcm = decoder.decode(opus_frame, frame_size) + pcm_datas.append(pcm) + + pcm_bytes = b"".join(pcm_datas) + + # 写入wav字节流 + wav_buffer = BytesIO() + with wave.open(wav_buffer, "wb") as wf: + wf.setnchannels(channels) + wf.setsampwidth(2) # 16bit + wf.setframerate(sample_rate) + wf.writeframes(pcm_bytes) + return wav_buffer.getvalue() - audio_to_data_stream( - file_path, - is_opus=True, - callback=handle_audio_frame - ) def check_vad_update(before_config, new_config): if ( diff --git a/main/xiaozhi-server/core/utils/wakeup_word.py b/main/xiaozhi-server/core/utils/wakeup_word.py new file mode 100644 index 00000000..ab58f290 --- /dev/null +++ b/main/xiaozhi-server/core/utils/wakeup_word.py @@ -0,0 +1,140 @@ +import os +import re +import yaml +import time +import hashlib +import portalocker +from typing import Dict + + +class FileLock: + def __init__(self, file, timeout=5): + self.file = file + self.timeout = timeout + self.start_time = None + + def __enter__(self): + self.start_time = time.time() + while True: + try: + portalocker.lock(self.file, portalocker.LOCK_EX | portalocker.LOCK_NB) + return self.file + except portalocker.LockException: + if time.time() - self.start_time > self.timeout: + raise TimeoutError("获取文件锁超时") + time.sleep(0.1) + + def __exit__(self, exc_type, exc_val, exc_tb): + portalocker.unlock(self.file) + + +class WakeupWordsConfig: + def __init__(self): + self.config_file = "data/.wakeup_words.yaml" + self.assets_dir = "config/assets/wakeup_words" + self._ensure_directories() + self._config_cache = None + self._last_load_time = 0 + self._cache_ttl = 1 # 缓存有效期(秒) + self._lock_timeout = 5 # 文件锁超时时间(秒) + + def _ensure_directories(self): + """确保必要的目录存在""" + os.makedirs(os.path.dirname(self.config_file), exist_ok=True) + os.makedirs(self.assets_dir, exist_ok=True) + + def _load_config(self) -> Dict: + """加载配置文件,使用缓存机制""" + current_time = time.time() + + # 如果缓存有效,直接返回缓存 + if ( + self._config_cache is not None + and current_time - self._last_load_time < self._cache_ttl + ): + return self._config_cache + + try: + with open(self.config_file, "a+") as f: + with FileLock(f, timeout=self._lock_timeout): + f.seek(0) + content = f.read() + config = yaml.safe_load(content) if content else {} + self._config_cache = config + self._last_load_time = current_time + return config + except (TimeoutError, IOError) as e: + print(f"加载配置文件失败: {e}") + return {} + except Exception as e: + print(f"加载配置文件时发生未知错误: {e}") + return {} + + def _save_config(self, config: Dict): + """保存配置到文件,使用文件锁保护""" + try: + with open(self.config_file, "w") as f: + with FileLock(f, timeout=self._lock_timeout): + yaml.dump(config, f, allow_unicode=True) + self._config_cache = config + self._last_load_time = time.time() + except (TimeoutError, IOError) as e: + print(f"保存配置文件失败: {e}") + raise + except Exception as e: + print(f"保存配置文件时发生未知错误: {e}") + raise + + def get_wakeup_response(self, voice: str) -> Dict: + voice = hashlib.md5(voice.encode()).hexdigest() + """获取唤醒词回复配置""" + config = self._load_config() + + if not config or voice not in config: + return None + + # 检查文件大小 + file_path = config[voice]["file_path"] + if not os.path.exists(file_path) or os.stat(file_path).st_size < (15 * 1024): + return None + + return config[voice] + + def update_wakeup_response(self, voice: str, file_path: str, text: str): + """更新唤醒词回复配置""" + try: + # 过滤表情符号 + filtered_text = re.sub(r'[\U0001F600-\U0001F64F\U0001F900-\U0001F9FF]', '', text) + + config = self._load_config() + voice_hash = hashlib.md5(voice.encode()).hexdigest() + config[voice_hash] = { + "voice": voice, + "file_path": file_path, + "time": time.time(), + "text": filtered_text, + } + self._save_config(config) + except Exception as e: + print(f"更新唤醒词回复配置失败: {e}") + raise + + def generate_file_path(self, voice: str) -> str: + """生成音频文件路径,使用voice的哈希值作为文件名""" + try: + # 生成voice的哈希值 + voice_hash = hashlib.md5(voice.encode()).hexdigest() + file_path = os.path.join(self.assets_dir, f"{voice_hash}.wav") + + # 如果文件已存在,先删除 + if os.path.exists(file_path): + try: + os.remove(file_path) + except Exception as e: + print(f"删除已存在的音频文件失败: {e}") + raise + + return file_path + except Exception as e: + print(f"生成音频文件路径失败: {e}") + raise diff --git a/main/xiaozhi-server/plugins_func/functions/play_music.py b/main/xiaozhi-server/plugins_func/functions/play_music.py index dd967d0a..2cbc4018 100644 --- a/main/xiaozhi-server/plugins_func/functions/play_music.py +++ b/main/xiaozhi-server/plugins_func/functions/play_music.py @@ -212,6 +212,7 @@ async def play_local_music(conn, specific_file=None): conn.logger.bind(tag=TAG).error(f"选定的音乐文件不存在: {music_path}") return text = _get_random_play_prompt(selected_music) + await send_stt_message(conn, text) conn.dialogue.put(Message(role="assistant", content=text)) if conn.intent_type == "intent_llm": From 152087443250703dd7d8a594da3357d4c8868200 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sat, 30 Aug 2025 23:02:50 +0800 Subject: [PATCH 14/35] =?UTF-8?q?update:=E5=8D=87=E7=BA=A7=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/xiaozhi/common/constant/Constant.java | 2 +- main/manager-mobile/src/pages/settings/index.vue | 2 +- main/xiaozhi-server/config/logger.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java index bf747600..ba6567a4 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java +++ b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java @@ -237,7 +237,7 @@ public interface Constant { /** * 版本号 */ - public static final String VERSION = "0.7.5"; + public static final String VERSION = "0.7.6"; /** * 无效固件URL diff --git a/main/manager-mobile/src/pages/settings/index.vue b/main/manager-mobile/src/pages/settings/index.vue index 41d16c00..0cecd6bf 100644 --- a/main/manager-mobile/src/pages/settings/index.vue +++ b/main/manager-mobile/src/pages/settings/index.vue @@ -222,7 +222,7 @@ function showAbout() { title: `关于${import.meta.env.VITE_APP_TITLE}`, content: `${import.meta.env.VITE_APP_TITLE}\n\n基于 Vue.js 3 + uni-app 构建的跨平台移动端管理应用,为小智ESP32智能硬件提供设备管理、智能体配置等功能。\n\n© 2025 xiaozhi-esp32-server`, title: `关于小智智控台`, - content: `小智智控台\n\n基于 Vue.js 3 + uni-app 构建的跨平台移动端管理应用,为小智ESP32智能硬件提供设备管理、智能体配置等功能。\n\n© 2025 xiaozhi-esp32-server 0.7.5`, + content: `小智智控台\n\n基于 Vue.js 3 + uni-app 构建的跨平台移动端管理应用,为小智ESP32智能硬件提供设备管理、智能体配置等功能。\n\n© 2025 xiaozhi-esp32-server 0.7.6`, showCancel: false, confirmText: '确定', }) diff --git a/main/xiaozhi-server/config/logger.py b/main/xiaozhi-server/config/logger.py index cd4d9965..5a815f06 100644 --- a/main/xiaozhi-server/config/logger.py +++ b/main/xiaozhi-server/config/logger.py @@ -5,7 +5,7 @@ from config.config_loader import load_config from config.settings import check_config_file from datetime import datetime -SERVER_VERSION = "0.7.5" +SERVER_VERSION = "0.7.6" _logger_initialized = False From ff3c0ab7ca69732168c52514060e0c65f95f8015 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sat, 30 Aug 2025 23:58:11 +0800 Subject: [PATCH 15/35] =?UTF-8?q?update:=E4=BF=AE=E5=A4=8Dmcp=20input?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E6=B1=A1=E6=9F=93=E5=85=B6=E4=BB=96input?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-web/src/components/FunctionDialog.vue | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/main/manager-web/src/components/FunctionDialog.vue b/main/manager-web/src/components/FunctionDialog.vue index 76c69c8e..f8adfef7 100644 --- a/main/manager-web/src/components/FunctionDialog.vue +++ b/main/manager-web/src/components/FunctionDialog.vue @@ -698,15 +698,10 @@ export default { font-size: 14px; height: 36px; box-sizing: border-box; - background-color: #f5f5f5; -} -::v-deep .el-input__inner { - background-color: #f5f5f5; - padding-right: 80px; -} - -.url-input { + ::v-deep .el-input__inner { + background-color: #f5f5f5 !important; + } ::v-deep .el-input__suffix { right: 0; From 87f50e52532cc9bc3bd826b44f9ad2c4125122c9 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sun, 31 Aug 2025 02:56:22 +0800 Subject: [PATCH 16/35] =?UTF-8?q?update=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9A=84=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index aa5ce96e..862e989a 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,8 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | dify 接口调用 | Dify | - | | fastgpt 接口调用 | Fastgpt | - | | coze 接口调用 | Coze | - | +| xinference 接口调用 | Xinference | - | +| homeassistant 接口调用 | HomeAssistant | - | 实际上,任何支持 openai 接口调用的 LLM 均可接入使用。 @@ -302,8 +304,8 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | 使用方式 | 支持平台 | 免费平台 | |:---:|:---:|:---:| -| 接口调用 | EdgeTTS、火山引擎豆包TTS、腾讯云、阿里云TTS、CosyVoiceSiliconflow、TTS302AI、CozeCnTTS、GizwitsTTS、ACGNTTS、OpenAITTS、灵犀流式TTS | 灵犀流式TTS、EdgeTTS、CosyVoiceSiliconflow(部分) | -| 本地服务 | FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3、MinimaxTTS | FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3、MinimaxTTS | +| 接口调用 | EdgeTTS、火山引擎豆包TTS、腾讯云、阿里云TTS、阿里云流式TTS、CosyVoiceSiliconflow、TTS302AI、CozeCnTTS、GizwitsTTS、ACGNTTS、OpenAITTS、灵犀流式TTS、MinimaxTTS、火山双流式TTS | 灵犀流式TTS、EdgeTTS、CosyVoiceSiliconflow(部分) | +| 本地服务 | FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3、Index-TTS、PaddleSpeech | Index-TTS、PaddleSpeech、FishSpeech、GPT_SOVITS_V2、GPT_SOVITS_V3 | --- @@ -320,7 +322,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ | 使用方式 | 支持平台 | 免费平台 | |:---:|:---:|:---:| | 本地使用 | FunASR、SherpaASR | FunASR、SherpaASR | -| 接口调用 | DoubaoASR、FunASRServer、TencentASR、AliyunASR | FunASRServer | +| 接口调用 | DoubaoASR、Doubao流式ASR、FunASRServer、TencentASR、AliyunASR、Aliyun流式ASR、百度ASR、OpenAI ASR | FunASRServer | --- @@ -338,6 +340,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ |:------:|:---------------:|:----:|:---------:|:--:| | Memory | mem0ai | 接口调用 | 1000次/月额度 | | | Memory | mem_local_short | 本地总结 | 免费 | | +| Memory | nomem | 无记忆模式 | 免费 | | --- @@ -347,6 +350,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/ |:------:|:-------------:|:----:|:-------:|:---------------------:| | Intent | intent_llm | 接口调用 | 根据LLM收费 | 通过大模型识别意图,通用性强 | | Intent | function_call | 接口调用 | 根据LLM收费 | 通过大模型函数调用完成意图,速度快,效果好 | +| Intent | nointent | 无意图模式 | 免费 | 不进行意图识别,直接返回对话结果 | --- From 44dd7fc58ccf234abe67a63a98712cb53f62c38e Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sun, 31 Aug 2025 07:25:14 +0800 Subject: [PATCH 17/35] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E7=BC=96?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile-web | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile-web b/Dockerfile-web index 35c25ed2..a347a88e 100644 --- a/Dockerfile-web +++ b/Dockerfile-web @@ -18,12 +18,12 @@ FROM bellsoft/liberica-runtime-container:jre-21-glibc # 安装Nginx和字体库 RUN apk update && \ - apk add --no-cache nginx bash && \ - apk add --no-cache fontconfig ttf-dejavu msttcorefonts-installer && \ + apk add --no-cache nginx bash fontconfig ttf-dejavu && \ + apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/ msttcorefonts-installer || true && \ rm -rf /var/cache/apk/* # 更新字体缓存 -RUN printf 'YES\n' | update-ms-fonts && fc-cache -f -v +RUN (printf 'YES\n' | update-ms-fonts || true) && fc-cache -f -v # 配置Nginx COPY docs/docker/nginx.conf /etc/nginx/nginx.conf From 32e6ecf00d8ac112f1acf5077bd3ede62cb302c5 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Sun, 31 Aug 2025 08:06:41 +0800 Subject: [PATCH 18/35] =?UTF-8?q?update:=E6=B7=BB=E5=8A=A0=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 13 ++++++++++++- Dockerfile-server | 11 +++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index e54cdd4e..aefa77d0 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -31,6 +31,9 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + network=host - name: Login to GitHub Container Registry uses: docker/login-action@v3 @@ -60,6 +63,10 @@ jobs: tags: | ${{ env.IS_VERSION == 'true' && format('ghcr.io/{0}:server_{1},ghcr.io/{0}:server_latest', github.repository, env.VERSION) || format('ghcr.io/{0}:server_latest', github.repository) }} platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + BUILDKIT_PROGRESS=plain # 构建 manager-api 镜像 - name: Build and push manager-web @@ -70,4 +77,8 @@ jobs: push: true tags: | ${{ env.IS_VERSION == 'true' && format('ghcr.io/{0}:web_{1},ghcr.io/{0}:web_latest', github.repository, env.VERSION) || format('ghcr.io/{0}:web_latest', github.repository) }} - platforms: linux/amd64,linux/arm64 \ No newline at end of file + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + BUILDKIT_PROGRESS=plain \ No newline at end of file diff --git a/Dockerfile-server b/Dockerfile-server index 6e87f7c0..a12fbb11 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -3,10 +3,17 @@ FROM python:3.10-slim AS builder WORKDIR /app +# 配置pip使用国内镜像源(阿里云)并设置超时和重试 +RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ && \ + pip config set global.trusted-host mirrors.aliyun.com && \ + pip config set global.timeout 120 && \ + pip config set install.retries 5 + COPY main/xiaozhi-server/requirements.txt . -# 安装Python依赖 -RUN pip install --no-cache-dir -r requirements.txt +# 安装Python依赖,使用并行下载 +RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \ + pip install --no-cache-dir -r requirements.txt --default-timeout=120 --retries 5 # 第二阶段:生产镜像 FROM python:3.10-slim From d31761e78a5724f6acbb4fab0c3a48a3e9eb5ade Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Mon, 1 Sep 2025 16:08:51 +0800 Subject: [PATCH 19/35] =?UTF-8?q?fix:=E6=B7=BB=E5=8A=A0user-agent=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=96=B0=E9=97=BB403=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins_func/functions/get_news_from_newsnow.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/plugins_func/functions/get_news_from_newsnow.py b/main/xiaozhi-server/plugins_func/functions/get_news_from_newsnow.py index 54641106..1d60aefd 100644 --- a/main/xiaozhi-server/plugins_func/functions/get_news_from_newsnow.py +++ b/main/xiaozhi-server/plugins_func/functions/get_news_from_newsnow.py @@ -125,7 +125,8 @@ def fetch_news_from_api(conn, source="thepaper"): ]["get_news_from_newsnow"].get("url"): api_url = conn.config["plugins"]["get_news_from_newsnow"]["url"] + source - response = requests.get(api_url, timeout=10) + headers = {"User-Agent": "Mozilla/5.0"} + response = requests.get(api_url, headers=headers, timeout=10) response.raise_for_status() data = response.json() @@ -144,7 +145,8 @@ def fetch_news_from_api(conn, source="thepaper"): def fetch_news_detail(url): """获取新闻详情页内容并使用MarkItDown清理HTML""" try: - response = requests.get(url, timeout=10) + headers = {"User-Agent": "Mozilla/5.0"} + response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() # 使用MarkItDown清理HTML内容 From c428a9ddee9317a6cff7753875b58cc3c4aa0a16 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 11:18:04 +0800 Subject: [PATCH 20/35] =?UTF-8?q?fix:=20=E5=94=A4=E9=86=92=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 4 + .../xiaozhi-server/core/handle/helloHandle.py | 118 ++++++++++++++- .../core/handle/intentHandler.py | 5 + .../core/handle/receiveAudioHandle.py | 18 ++- .../core/handle/sendAudioHandle.py | 33 ++++- main/xiaozhi-server/core/handle/textHandle.py | 15 +- .../xiaozhi-server/core/providers/asr/base.py | 140 +++++++----------- .../core/providers/tts/aliyun_stream.py | 139 +++++++++++++++++ .../xiaozhi-server/core/providers/tts/base.py | 73 ++++++++- .../providers/tts/huoshan_double_stream.py | 101 +++++++++++++ .../core/providers/tts/index_stream.py | 60 +++++++- .../core/providers/tts/linkerai.py | 70 +++++++++ .../core/providers/tts/paddle_speech.py | 49 +----- main/xiaozhi-server/core/utils/util.py | 28 ++++ main/xiaozhi-server/core/utils/wakeup_word.py | 140 ++++++++++++++++++ 15 files changed, 848 insertions(+), 145 deletions(-) create mode 100644 main/xiaozhi-server/core/utils/wakeup_word.py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index efcca44f..c0aa3f93 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -59,6 +59,10 @@ log: delete_audio: true # 没有语音输入多久后断开连接(秒),默认2分钟,即120秒 close_connection_no_voice_time: 120 +# TTS请求超时时间(秒) +tts_timeout: 10 +# 开启唤醒词加速 +enable_wakeup_words_response_cache: true # 开场是否回复唤醒词 enable_greeting: true # 说完话是否开启提示音 diff --git a/main/xiaozhi-server/core/handle/helloHandle.py b/main/xiaozhi-server/core/handle/helloHandle.py index 8147db1a..bb307b87 100644 --- a/main/xiaozhi-server/core/handle/helloHandle.py +++ b/main/xiaozhi-server/core/handle/helloHandle.py @@ -1,5 +1,13 @@ +import time import json +import random import asyncio +from core.utils.dialogue import Message +from core.utils.util import audio_to_data_stream +from core.providers.tts.dto.dto import SentenceType +from core.utils.wakeup_word import WakeupWordsConfig +from core.handle.sendAudioHandle import sendAudioMessage, send_stt_message +from core.utils.util import remove_punctuation_and_length, opus_datas_to_wav_bytes from core.providers.tools.device_mcp import ( MCPClient, send_mcp_initialize_message, @@ -8,6 +16,18 @@ from core.providers.tools.device_mcp import ( TAG = __name__ +WAKEUP_CONFIG = { + "refresh_time": 5, + "words": ["你好", "你好啊", "嘿,你好", "嗨"], +} + +# 创建全局的唤醒词配置管理器 +wakeup_words_config = WakeupWordsConfig() + +# 用于防止并发调用wakeupWordsResponse的锁 +_wakeup_response_lock = asyncio.Lock() + + async def handleHelloMessage(conn, msg_json): """处理hello消息""" audio_params = msg_json.get("audio_params") @@ -28,4 +48,100 @@ async def handleHelloMessage(conn, msg_json): # 发送mcp消息,获取tools列表 asyncio.create_task(send_mcp_tools_list_request(conn)) - await conn.websocket.send(json.dumps(conn.welcome_msg)) \ No newline at end of file + await conn.websocket.send(json.dumps(conn.welcome_msg)) + + +async def checkWakeupWords(conn, text): + enable_wakeup_words_response_cache = conn.config[ + "enable_wakeup_words_response_cache" + ] + + if not enable_wakeup_words_response_cache or not conn.tts: + return False + + _, filtered_text = remove_punctuation_and_length(text) + if filtered_text not in conn.config.get("wakeup_words"): + return False + + conn.just_woken_up = True + await send_stt_message(conn, text) + + # 获取当前音色 + voice = getattr(conn.tts, "voice", "default") + if not voice: + voice = "default" + + # 获取唤醒词回复配置 + response = wakeup_words_config.get_wakeup_response(voice) + if not response or not response.get("file_path"): + response = { + "voice": "default", + "file_path": "config/assets/wakeup_words.wav", + "time": 0, + "text": "哈啰啊,我是小智啦,声音好听的台湾女孩一枚,超开心认识你耶,最近在忙啥,别忘了给我来点有趣的料哦,我超爱听八卦的啦", + } + + # 获取音频数据 + opus_packets = [] + def handle_audio_frame(frame_data): + opus_packets.append(frame_data) + + audio_to_data_stream(response.get("file_path"), is_opus=True, callback=handle_audio_frame) + + # 播放唤醒词回复 + conn.client_abort = False + + conn.logger.bind(tag=TAG).info(f"播放唤醒词回复: {response.get('text')}") + await sendAudioMessage(conn, SentenceType.FIRST, opus_packets, response.get("text")) + await sendAudioMessage(conn, SentenceType.LAST, [], None) + + # 补充对话 + conn.dialogue.put(Message(role="assistant", content=response.get("text"))) + + # 检查是否需要更新唤醒词回复 + if time.time() - response.get("time", 0) > WAKEUP_CONFIG["refresh_time"]: + if not _wakeup_response_lock.locked(): + asyncio.create_task(wakeupWordsResponse(conn)) + return True + + +async def wakeupWordsResponse(conn): + if not conn.tts or not conn.llm or not conn.llm.response_no_stream: + return + + try: + # 尝试获取锁,如果获取不到就返回 + if not await _wakeup_response_lock.acquire(): + return + + # 生成唤醒词回复 + wakeup_word = random.choice(WAKEUP_CONFIG["words"]) + question = ( + "此刻用户正在和你说```" + + wakeup_word + + "```。\n请你根据以上用户的内容进行20-30字回复。要符合系统设置的角色情感和态度,不要像机器人一样说话。\n" + + "请勿对这条内容本身进行任何解释和回应,请勿返回表情符号,仅返回对用户的内容的回复。" + ) + + result = conn.llm.response_no_stream(conn.config["prompt"], question) + if not result or len(result) == 0: + return + + # 生成TTS音频 + tts_result = await asyncio.to_thread(conn.tts.to_tts, result) + if not tts_result: + return + + # 获取当前音色 + voice = getattr(conn.tts, "voice", "default") + + wav_bytes = opus_datas_to_wav_bytes(tts_result, sample_rate=16000) + file_path = wakeup_words_config.generate_file_path(voice) + with open(file_path, "wb") as f: + f.write(wav_bytes) + # 更新配置 + wakeup_words_config.update_wakeup_response(voice, file_path, result) + finally: + # 确保在任何情况下都释放锁 + if _wakeup_response_lock.locked(): + _wakeup_response_lock.release() \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/intentHandler.py b/main/xiaozhi-server/core/handle/intentHandler.py index 2ddf03d6..72424968 100644 --- a/main/xiaozhi-server/core/handle/intentHandler.py +++ b/main/xiaozhi-server/core/handle/intentHandler.py @@ -3,6 +3,7 @@ import uuid import asyncio from core.utils.dialogue import Message from core.providers.tts.dto.dto import ContentType +from core.handle.helloHandle import checkWakeupWords from plugins_func.register import Action, ActionResponse from core.handle.sendAudioHandle import send_stt_message from core.utils.util import remove_punctuation_and_length @@ -27,6 +28,10 @@ async def handle_user_intent(conn, text): if await check_direct_exit(conn, filtered_text): return True + # 检查是否是唤醒词 + if await checkWakeupWords(conn, filtered_text): + return True + if conn.intent_type == "function_call": # 使用支持function calling的聊天方法,不再进行意图分析 return False diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index 15a9e434..afe60084 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -1,5 +1,6 @@ import time import json +import asyncio from core.handle.abortHandle import handleAbortMessage from core.handle.intentHandler import handle_user_intent from core.utils.output_counter import check_device_output_limit @@ -10,12 +11,16 @@ TAG = __name__ async def handleAudioMessage(conn, audio): - # 检查是否在唤醒处理锁定期内 - if getattr(conn, 'wakeup_processing_lock', 0) > time.monotonic(): - return - # 当前片段是否有人说话 have_voice = conn.vad.is_vad(conn, audio) + # 如果设备刚刚被唤醒,短暂忽略VAD检测 + if have_voice and hasattr(conn, "just_woken_up") and conn.just_woken_up: + have_voice = False + # 设置一个短暂延迟后恢复VAD检测 + conn.asr_audio.clear() + if not hasattr(conn, "vad_resume_task") or conn.vad_resume_task.done(): + conn.vad_resume_task = asyncio.create_task(resume_vad_detection(conn)) + return if have_voice: if conn.client_is_speaking: await handleAbortMessage(conn) @@ -24,6 +29,11 @@ async def handleAudioMessage(conn, audio): # 接收音频 await conn.asr.receive_audio(conn, audio, have_voice) +async def resume_vad_detection(conn): + # 等待2秒后恢复VAD检测 + await asyncio.sleep(1) + conn.just_woken_up = False + async def startToChat(conn, text): # 检查输入是否是JSON格式(包含说话人信息) speaker_name = None diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index c61b0d0d..f482a1ef 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -30,13 +30,14 @@ async def sendAudioMessage(conn, sentenceType, audios, text): # 播放音频 -async def sendAudio(conn, audios, pre_buffer=False): +async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): """ 发送单个opus包,支持流控 Args: conn: 连接对象 opus_packet: 单个opus数据包 pre_buffer: 快速发送音频 + frame_duration: 帧时长(毫秒),匹配 Opus 编码 """ if audios is None or len(audios) == 0: return @@ -60,7 +61,6 @@ async def sendAudio(conn, audios, pre_buffer=False): "start_time": time.perf_counter(), } - frame_duration=60 flow_control = conn.audio_flow_control current_time = time.perf_counter() # 计算预期发送时间 @@ -77,6 +77,35 @@ async def sendAudio(conn, audios, pre_buffer=False): # 更新流控状态 flow_control["packet_count"] += 1 flow_control["last_send_time"] = time.perf_counter() + else: + # 流控参数优化 + start_time = time.perf_counter() + play_position = 0 + + # 执行预缓冲 + pre_buffer_frames = min(3, len(audios)) + for i in range(pre_buffer_frames): + await conn.websocket.send(audios[i]) + remaining_audios = audios[pre_buffer_frames:] + + # 播放剩余音频帧 + for opus_packet in remaining_audios: + if conn.client_abort: + break + + # 重置没有声音的状态 + conn.last_activity_time = time.time() * 1000 + + # 计算预期发送时间 + expected_time = start_time + (play_position / 1000) + current_time = time.perf_counter() + delay = expected_time - current_time + if delay > 0: + await asyncio.sleep(delay) + + await conn.websocket.send(opus_packet) + + play_position += frame_duration async def send_tts_message(conn, state, text=None): diff --git a/main/xiaozhi-server/core/handle/textHandle.py b/main/xiaozhi-server/core/handle/textHandle.py index c1e8f6be..f12b974a 100644 --- a/main/xiaozhi-server/core/handle/textHandle.py +++ b/main/xiaozhi-server/core/handle/textHandle.py @@ -3,8 +3,9 @@ import asyncio from core.utils.util import filter_sensitive_info from core.handle.abortHandle import handleAbortMessage from core.handle.helloHandle import handleHelloMessage -from core.handle.receiveAudioHandle import handleAudioMessage +from core.handle.reportHandle import enqueue_asr_report from core.providers.tools.device_mcp import handle_mcp_message +from core.handle.receiveAudioHandle import startToChat, handleAudioMessage from core.handle.sendAudioHandle import send_stt_message, send_tts_message from core.providers.tools.device_iot import handleIotDescriptors, handleIotStatus @@ -55,10 +56,16 @@ async def handleTextMessage(conn, message): await send_stt_message(conn, original_text) await send_tts_message(conn, "stop", None) conn.client_is_speaking = False + elif is_wakeup_words and enable_greeting: + conn.just_woken_up = True + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, "嘿,你好呀", []) + await startToChat(conn, "嘿,你好呀") else: - # 检测到唤醒词,开始等待后续进行声纹识别 - conn.wakeup_mode = True - conn.logger.bind(tag=TAG).info(f"检测到唤醒词~") + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, original_text, []) + # 否则需要LLM对文字内容进行答复 + await startToChat(conn, original_text) elif msg_json["type"] == "iot": conn.logger.bind(tag=TAG).info(f"收到iot消息:{message}") if "descriptors" in msg_json: diff --git a/main/xiaozhi-server/core/providers/asr/base.py b/main/xiaozhi-server/core/providers/asr/base.py index 7885261d..250a25f2 100644 --- a/main/xiaozhi-server/core/providers/asr/base.py +++ b/main/xiaozhi-server/core/providers/asr/base.py @@ -53,10 +53,6 @@ class ASRProviderBase(ABC): # 接收音频 async def receive_audio(self, conn, audio, audio_have_voice): - # 检查是否在唤醒处理锁定期内 - if getattr(conn, 'wakeup_processing_lock', 0) > time.monotonic(): - return - if conn.client_listen_mode == "auto" or conn.client_listen_mode == "realtime": have_voice = audio_have_voice else: @@ -66,14 +62,6 @@ class ASRProviderBase(ABC): if not have_voice and not conn.client_have_voice: conn.asr_audio = conn.asr_audio[-10:] return - - # 检查是否处于唤醒模式 - if getattr(conn, 'wakeup_mode', False) and len(conn.asr_audio) >= 10: - asr_audio_task = conn.asr_audio.copy() - conn.reset_vad_states() - conn.asr_audio.clear() - - await self.handle_voice_stop(conn, asr_audio_task) if conn.client_voice_stop: asr_audio_task = conn.asr_audio.copy() @@ -102,10 +90,27 @@ class ASRProviderBase(ABC): if conn.voiceprint_provider and combined_pcm_data: wav_data = self._pcm_to_wav(combined_pcm_data) - # 检查是否处于唤醒模式 - wakeup_mode = getattr(conn, 'wakeup_mode', False) + # 定义ASR任务 + def run_asr(): + start_time = time.monotonic() + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + result = loop.run_until_complete( + self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format) + ) + end_time = time.monotonic() + logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s") + return result + finally: + loop.close() + except Exception as e: + end_time = time.monotonic() + logger.bind(tag=TAG).error(f"ASR失败: {e}") + return ("", None) - # 声纹识别任务(公共逻辑) + # 定义声纹识别任务 def run_voiceprint(): if not wav_data: return None @@ -124,83 +129,48 @@ class ASRProviderBase(ABC): logger.bind(tag=TAG).error(f"声纹识别失败: {e}") return None - if wakeup_mode and conn.voiceprint_provider and wav_data: - conn.wakeup_mode = False - # 设置处理锁,防止后续音频片段重复处理 - conn.wakeup_processing_lock = time.monotonic() + 3 # 3秒锁定期 + # 使用线程池执行器并行运行 + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread_executor: + asr_future = thread_executor.submit(run_asr) - # 唤醒模式:只执行声纹识别 - with concurrent.futures.ThreadPoolExecutor(max_workers=1) as thread_executor: + if conn.voiceprint_provider and wav_data: voiceprint_future = thread_executor.submit(run_voiceprint) + + # 等待两个线程都完成 + asr_result = asr_future.result(timeout=15) voiceprint_result = voiceprint_future.result(timeout=15) - - speaker_name = voiceprint_result + + results = {"asr": asr_result, "voiceprint": voiceprint_result} + else: + asr_result = asr_future.result(timeout=15) + results = {"asr": asr_result, "voiceprint": None} + + + # 处理结果 + raw_text, _ = results.get("asr", ("", None)) + speaker_name = results.get("voiceprint", None) + + # 记录识别结果 + if raw_text: + logger.bind(tag=TAG).info(f"识别文本: {raw_text}") + if speaker_name: logger.bind(tag=TAG).info(f"识别说话人: {speaker_name}") - - fixed_text = "嘿,你好啊" - enhanced_text = self._build_enhanced_text(fixed_text, speaker_name) + + # 性能监控 + total_time = time.monotonic() - total_start_time + logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s") + + # 检查文本长度 + text_len, _ = remove_punctuation_and_length(raw_text) + self.stop_ws_connection() + + if text_len > 0: + # 构建包含说话人信息的JSON字符串 + enhanced_text = self._build_enhanced_text(raw_text, speaker_name) - # 性能监控 - total_time = time.monotonic() - total_start_time - logger.bind(tag=TAG).info(f"唤醒模式总处理耗时: {total_time:.3f}s") - + # 使用自定义模块进行上报 await startToChat(conn, enhanced_text) enqueue_asr_report(conn, enhanced_text, asr_audio_task) - else: - # 正常模式:执行声纹识别和ASR - def run_asr(): - start_time = time.monotonic() - try: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - result = loop.run_until_complete( - self.speech_to_text(asr_audio_task, conn.session_id, conn.audio_format) - ) - end_time = time.monotonic() - logger.bind(tag=TAG).info(f"ASR耗时: {end_time - start_time:.3f}s") - return result - finally: - loop.close() - except Exception as e: - end_time = time.monotonic() - logger.bind(tag=TAG).error(f"ASR失败: {e}") - return ("", None) - - # 使用线程池执行器并行运行 - with concurrent.futures.ThreadPoolExecutor(max_workers=2) as thread_executor: - asr_future = thread_executor.submit(run_asr) - - if conn.voiceprint_provider and wav_data: - voiceprint_future = thread_executor.submit(run_voiceprint) - asr_result = asr_future.result(timeout=15) - voiceprint_result = voiceprint_future.result(timeout=15) - results = {"asr": asr_result, "voiceprint": voiceprint_result} - else: - asr_result = asr_future.result(timeout=15) - results = {"asr": asr_result, "voiceprint": None} - - # 处理结果 - raw_text, _ = results.get("asr", ("", None)) - speaker_name = results.get("voiceprint", None) - - if raw_text: - logger.bind(tag=TAG).info(f"识别文本: {raw_text}") - if speaker_name: - logger.bind(tag=TAG).info(f"识别说话人: {speaker_name}") - - # 性能监控 - total_time = time.monotonic() - total_start_time - logger.bind(tag=TAG).info(f"总处理耗时: {total_time:.3f}s") - - # 检查文本长度 - text_len, _ = remove_punctuation_and_length(raw_text) - self.stop_ws_connection() - - if text_len > 0: - enhanced_text = self._build_enhanced_text(raw_text, speaker_name) - await startToChat(conn, enhanced_text) - enqueue_asr_report(conn, enhanced_text, asr_audio_task) except Exception as e: logger.bind(tag=TAG).error(f"处理语音停止失败: {e}") diff --git a/main/xiaozhi-server/core/providers/tts/aliyun_stream.py b/main/xiaozhi-server/core/providers/tts/aliyun_stream.py index 734681e2..38e70927 100644 --- a/main/xiaozhi-server/core/providers/tts/aliyun_stream.py +++ b/main/xiaozhi-server/core/providers/tts/aliyun_stream.py @@ -478,3 +478,142 @@ class TTSProvider(TTSProviderBase): finally: self._monitor_task = None + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景""" + try: + # 创建新的事件循环 + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # 生成会话ID + session_id = uuid.uuid4().hex + # 存储音频数据 + audio_data = [] + + async def _generate_audio(): + # 刷新Token(如果需要) + if self._is_token_expired(): + self._refresh_token() + + # 建立WebSocket连接 + ws = await websockets.connect( + self.ws_url, + additional_headers={"X-NLS-Token": self.token}, + ping_interval=30, + ping_timeout=10, + close_timeout=10, + ) + try: + # 发送StartSynthesis请求 + start_message_id = str(uuid.uuid4().hex) + start_request = { + "header": { + "message_id": start_message_id, + "task_id": session_id, + "namespace": "FlowingSpeechSynthesizer", + "name": "StartSynthesis", + "appkey": self.appkey, + }, + "payload": { + "voice": self.voice, + "format": self.format, + "sample_rate": self.sample_rate, + "volume": self.volume, + "speech_rate": self.speech_rate, + "pitch_rate": self.pitch_rate, + "enable_subtitle": True, + }, + } + await ws.send(json.dumps(start_request)) + + # 等待SynthesisStarted响应 + synthesis_started = False + while not synthesis_started: + msg = await ws.recv() + if isinstance(msg, str): + data = json.loads(msg) + header = data.get("header", {}) + if header.get("name") == "SynthesisStarted": + synthesis_started = True + logger.bind(tag=TAG).debug("TTS合成已启动") + elif header.get("name") == "TaskFailed": + error_info = data.get("payload", {}).get( + "error_info", {} + ) + error_code = error_info.get("error_code") + error_message = error_info.get( + "error_message", "未知错误" + ) + raise Exception( + f"启动合成失败: {error_code} - {error_message}" + ) + + # 发送文本合成请求 + filtered_text = MarkdownCleaner.clean_markdown(text) + run_message_id = str(uuid.uuid4().hex) + run_request = { + "header": { + "message_id": run_message_id, + "task_id": session_id, + "namespace": "FlowingSpeechSynthesizer", + "name": "RunSynthesis", + "appkey": self.appkey, + }, + "payload": {"text": filtered_text}, + } + await ws.send(json.dumps(run_request)) + + # 发送停止合成请求 + stop_message_id = str(uuid.uuid4().hex) + stop_request = { + "header": { + "message_id": stop_message_id, + "task_id": session_id, + "namespace": "FlowingSpeechSynthesizer", + "name": "StopSynthesis", + "appkey": self.appkey, + } + } + await ws.send(json.dumps(stop_request)) + + # 接收音频数据 + synthesis_completed = False + while not synthesis_completed: + msg = await ws.recv() + if isinstance(msg, (bytes, bytearray)): + self.opus_encoder.encode_pcm_to_opus_stream( + msg, + end_of_stream=False, + callback=lambda opus: audio_data.append(opus) + ) + elif isinstance(msg, str): + data = json.loads(msg) + header = data.get("header", {}) + event_name = header.get("name") + if event_name == "SynthesisCompleted": + synthesis_completed = True + logger.bind(tag=TAG).debug("TTS合成完成") + elif event_name == "TaskFailed": + error_info = data.get("payload", {}).get( + "error_info", {} + ) + error_code = error_info.get("error_code") + error_message = error_info.get( + "error_message", "未知错误" + ) + raise Exception( + f"合成失败: {error_code} - {error_message}" + ) + finally: + try: + await ws.close() + except: + pass + + loop.run_until_complete(_generate_audio()) + loop.close() + + return audio_data + except Exception as e: + logger.bind(tag=TAG).error(f"生成音频数据失败: {str(e)}") + return [] \ No newline at end of file diff --git a/main/xiaozhi-server/core/providers/tts/base.py b/main/xiaozhi-server/core/providers/tts/base.py index a65fe194..04a7fa36 100644 --- a/main/xiaozhi-server/core/providers/tts/base.py +++ b/main/xiaozhi-server/core/providers/tts/base.py @@ -1,21 +1,22 @@ import os import re -import queue +import time import uuid +import queue import asyncio import threading -from typing import Callable, Any +import traceback from core.utils import p3 -import time from datetime import datetime from core.utils import textUtils +from typing import Callable, Any from abc import ABC, abstractmethod from config.logger import setup_logging -from core.utils.util import audio_bytes_to_data_stream, audio_to_data_stream from core.utils.tts import MarkdownCleaner from core.utils.output_counter import add_device_output from core.handle.reportHandle import enqueue_tts_report from core.handle.sendAudioHandle import sendAudioMessage +from core.utils.util import audio_bytes_to_data_stream, audio_to_data_stream from core.providers.tts.dto.dto import ( TTSMessageDTO, SentenceType, @@ -23,8 +24,6 @@ from core.providers.tts.dto.dto import ( InterfaceType, ) -import traceback - TAG = __name__ logger = setup_logging() @@ -144,6 +143,68 @@ class TTSProviderBase(ABC): except Exception as e: logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}") return None + + def to_tts(self, text): + text = MarkdownCleaner.clean_markdown(text) + max_repeat_time = 5 + if self.delete_audio_file: + # 需要删除文件的直接转为音频数据 + while max_repeat_time > 0: + try: + audio_bytes = asyncio.run(self.text_to_speak(text, None)) + if audio_bytes: + audio_datas = [] + audio_bytes_to_data_stream( + audio_bytes, + file_type=self.audio_file_type, + is_opus=True, + callback=lambda data: audio_datas.append(data) + ) + return audio_datas + else: + max_repeat_time -= 1 + except Exception as e: + logger.bind(tag=TAG).warning( + f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}" + ) + max_repeat_time -= 1 + if max_repeat_time > 0: + logger.bind(tag=TAG).info( + f"语音生成成功: {text},重试{5 - max_repeat_time}次" + ) + else: + logger.bind(tag=TAG).error( + f"语音生成失败: {text},请检查网络或服务是否正常" + ) + return None + else: + tmp_file = self.generate_filename() + try: + while not os.path.exists(tmp_file) and max_repeat_time > 0: + try: + asyncio.run(self.text_to_speak(text, tmp_file)) + except Exception as e: + logger.bind(tag=TAG).warning( + f"语音生成失败{5 - max_repeat_time + 1}次: {text},错误: {e}" + ) + # 未执行成功,删除文件 + if os.path.exists(tmp_file): + os.remove(tmp_file) + max_repeat_time -= 1 + + if max_repeat_time > 0: + logger.bind(tag=TAG).info( + f"语音生成成功: {text}:{tmp_file},重试{5 - max_repeat_time}次" + ) + else: + logger.bind(tag=TAG).error( + f"语音生成失败: {text},请检查网络或服务是否正常" + ) + + return tmp_file + except Exception as e: + logger.bind(tag=TAG).error(f"Failed to generate TTS file: {e}") + return None @abstractmethod async def text_to_speak(self, text, output_file): diff --git a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py index 463c16fc..ff295581 100644 --- a/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py +++ b/main/xiaozhi-server/core/providers/tts/huoshan_double_stream.py @@ -628,3 +628,104 @@ class TTSProvider(TTSProviderBase): def wav_to_opus_data_audio_raw_stream(self, raw_data_var, is_end=False, callback: Callable[[Any], Any]=None): return self.opus_encoder.encode_pcm_to_opus_stream(raw_data_var, is_end, callback=callback) + + def to_tts(self, text: str) -> list: + """非流式生成音频数据,用于生成音频及测试场景 + Args: + text: 要转换的文本 + Returns: + list: 音频数据列表 + """ + try: + # 创建事件循环 + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # 生成会话ID + session_id = uuid.uuid4().__str__().replace("-", "") + + # 存储音频数据 + audio_data = [] + + async def _generate_audio(): + # 创建新的WebSocket连接 + ws_header = { + "X-Api-App-Key": self.appId, + "X-Api-Access-Key": self.access_token, + "X-Api-Resource-Id": self.resource_id, + "X-Api-Connect-Id": uuid.uuid4(), + } + ws = await websockets.connect( + self.ws_url, additional_headers=ws_header, max_size=1000000000 + ) + + try: + # 启动会话 + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_StartSession, sessionId=session_id + ).as_bytes() + payload = self.get_payload_bytes( + event=EVENT_StartSession, speaker=self.voice + ) + await self.send_event(ws, header, optional, payload) + + # 发送文本 + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_TaskRequest, sessionId=session_id + ).as_bytes() + payload = self.get_payload_bytes( + event=EVENT_TaskRequest, text=text, speaker=self.voice + ) + await self.send_event(ws, header, optional, payload) + + # 发送结束会话请求 + header = Header( + message_type=FULL_CLIENT_REQUEST, + message_type_specific_flags=MsgTypeFlagWithEvent, + serial_method=JSON, + ).as_bytes() + optional = Optional( + event=EVENT_FinishSession, sessionId=session_id + ).as_bytes() + payload = str.encode("{}") + await self.send_event(ws, header, optional, payload) + + # 接收音频数据 + while True: + msg = await ws.recv() + res = self.parser_response(msg) + + if ( + res.optional.event == EVENT_TTSResponse + and res.header.message_type == AUDIO_ONLY_RESPONSE + ): + self.wav_to_opus_data_audio_raw_stream(res.payload, callback=lambda opus_frame: audio_data.append(opus_frame)) + elif res.optional.event == EVENT_SessionFinished: + break + + finally: + # 清理资源 + try: + await ws.close() + except: + pass + + # 运行异步任务 + loop.run_until_complete(_generate_audio()) + loop.close() + + return audio_data + + except Exception as e: + logger.bind(tag=TAG).error(f"生成音频数据失败: {str(e)}") + return [] diff --git a/main/xiaozhi-server/core/providers/tts/index_stream.py b/main/xiaozhi-server/core/providers/tts/index_stream.py index 6f6b829c..8abb06f2 100644 --- a/main/xiaozhi-server/core/providers/tts/index_stream.py +++ b/main/xiaozhi-server/core/providers/tts/index_stream.py @@ -1,8 +1,10 @@ import os +import time import queue -import asyncio -import traceback import aiohttp +import asyncio +import requests +import traceback from config.logger import setup_logging from core.utils.tts import MarkdownCleaner from core.providers.tts.base import TTSProviderBase @@ -177,3 +179,57 @@ class TTSProvider(TTSProviderBase): await super().close() if hasattr(self, "opus_encoder"): self.opus_encoder.close() + + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景 + Args: + text: 要转换的文本 + Returns: + list: 返回opus编码后的音频数据列表 + """ + start_time = time.time() + text = MarkdownCleaner.clean_markdown(text) + + payload = {"text": text, "character": self.character} + + try: + with requests.post(self.api_url, json=payload, timeout=5) as response: + if response.status_code != 200: + logger.bind(tag=TAG).error( + f"TTS请求失败: {response.status_code}, {response.text}" + ) + return [] + + logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒") + + # 使用opus编码器处理PCM数据 + opus_datas = [] + pcm_data = response.content + + # 计算每帧的字节数 + frame_bytes = int( + self.opus_encoder.sample_rate + * self.opus_encoder.channels + * self.opus_encoder.frame_size_ms + / 1000 + * 2 + ) + + # 分帧处理PCM数据 + for i in range(0, len(pcm_data), frame_bytes): + frame = pcm_data[i : i + frame_bytes] + if len(frame) < frame_bytes: + # 最后一帧可能不足,用0填充 + frame = frame + b"\x00" * (frame_bytes - len(frame)) + + self.opus_encoder.encode_pcm_to_opus_stream( + frame, + end_of_stream=(i + frame_bytes >= len(pcm_data)), + callback=lambda opus: opus_datas.append(opus) + ) + + return opus_datas + + except Exception as e: + logger.bind(tag=TAG).error(f"TTS请求异常: {e}") + return [] \ No newline at end of file diff --git a/main/xiaozhi-server/core/providers/tts/linkerai.py b/main/xiaozhi-server/core/providers/tts/linkerai.py index 04e83148..9b5e946f 100644 --- a/main/xiaozhi-server/core/providers/tts/linkerai.py +++ b/main/xiaozhi-server/core/providers/tts/linkerai.py @@ -1,7 +1,9 @@ import os +import time import queue import aiohttp import asyncio +import requests import traceback from config.logger import setup_logging from core.utils.tts import MarkdownCleaner @@ -195,3 +197,71 @@ class TTSProvider(TTSProviderBase): except Exception as e: logger.bind(tag=TAG).error(f"TTS请求异常: {e}") self.tts_audio_queue.put((SentenceType.LAST, [], None)) + + def to_tts(self, text: str) -> list: + """非流式TTS处理,用于测试及保存音频文件的场景 + Args: + text: 要转换的文本 + Returns: + list: 返回opus编码后的音频数据列表 + """ + start_time = time.time() + text = MarkdownCleaner.clean_markdown(text) + + params = { + "tts_text": text, + "spk_id": self.voice, + "frame_duration": 60, + "stream": False, + "target_sr": 16000, + "audio_format": self.audio_format, + "instruct_text": "请生成一段自然流畅的语音", + } + headers = { + "Authorization": f"Bearer {self.access_token}", + "Content-Type": "application/json", + } + + try: + with requests.get( + self.api_url, params=params, headers=headers, timeout=5 + ) as response: + if response.status_code != 200: + logger.bind(tag=TAG).error( + f"TTS请求失败: {response.status_code}, {response.text}" + ) + return [] + + logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒") + + # 使用opus编码器处理PCM数据 + opus_datas = [] + pcm_data = response.content + + # 计算每帧的字节数 + frame_bytes = int( + self.opus_encoder.sample_rate + * self.opus_encoder.channels + * self.opus_encoder.frame_size_ms + / 1000 + * 2 + ) + + # 分帧处理PCM数据 + for i in range(0, len(pcm_data), frame_bytes): + frame = pcm_data[i : i + frame_bytes] + if len(frame) < frame_bytes: + # 最后一帧可能不足,用0填充 + frame = frame + b"\x00" * (frame_bytes - len(frame)) + + self.opus_encoder.encode_pcm_to_opus_stream( + frame, + end_of_stream=(i + frame_bytes >= len(pcm_data)), + callback=lambda opus: opus_datas.append(opus) + ) + + return opus_datas + + except Exception as e: + logger.bind(tag=TAG).error(f"TTS请求异常: {e}") + return [] \ No newline at end of file diff --git a/main/xiaozhi-server/core/providers/tts/paddle_speech.py b/main/xiaozhi-server/core/providers/tts/paddle_speech.py index 4b987b71..864cf505 100644 --- a/main/xiaozhi-server/core/providers/tts/paddle_speech.py +++ b/main/xiaozhi-server/core/providers/tts/paddle_speech.py @@ -1,14 +1,15 @@ -import asyncio -import json -import base64 -import aiohttp -import numpy as np import io import wave +import json +import base64 +import asyncio import websockets -from core.providers.tts.base import TTSProviderBase -from config.logger import setup_logging +import numpy as np from datetime import datetime +from config.logger import setup_logging +from core.providers.tts.base import TTSProviderBase + + TAG = __name__ logger = setup_logging() @@ -74,43 +75,9 @@ class TTSProvider(TTSProviderBase): async def text_to_speak(self, text, output_file): if self.protocol == "websocket": return await self.text_streaming(text, output_file) - elif self.protocol == "http": - return await self.text(text, output_file) else: raise ValueError("Unsupported protocol. Please use 'websocket' or 'http'.") - async def text(self, text, output_file): - request_json = { - "text": text, - "spk_id": self.spk_id, - "speed": self.speed, - "volume": self.volume, - "sample_rate": self.sample_rate, - "save_path": self.save_path - } - - try: - async with aiohttp.ClientSession() as session: - async with session.post(self.url, json=request_json) as resp: - if resp.status == 200: - resp_json = await resp.json() - if resp_json.get("success"): - data = resp_json["result"] - audio_bytes = base64.b64decode(data["audio"]) - if output_file: - with open(output_file, "wb") as file_to_save: - file_to_save.write(audio_bytes) - else: - return audio_bytes - else: - raise Exception( - f"Error: {resp_json.get('message', 'Unknown error')} while processing text: {text}") - else: - raise Exception( - f"HTTP Error: {resp.status} - {await resp.text()} while processing text: {text}") - except Exception as e: - raise Exception(f"Error during TTS HTTP request: {e} while processing text: {text}") - async def text_streaming(self, text, output_file): try: # 使用 websockets 异步连接到 WebSocket 服务器 diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index a608049c..49782bfb 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -2,6 +2,7 @@ import re import os import json import copy +import wave import socket import requests import subprocess @@ -292,6 +293,33 @@ def play_audio_frames(conn, file_path): callback=handle_audio_frame ) + +def opus_datas_to_wav_bytes(opus_datas, sample_rate=16000, channels=1): + """ + 将opus帧列表解码为wav字节流 + """ + decoder = opuslib_next.Decoder(sample_rate, channels) + pcm_datas = [] + + frame_duration = 60 # ms + frame_size = int(sample_rate * frame_duration / 1000) # 960 + + for opus_frame in opus_datas: + # 解码为PCM(返回bytes,2字节/采样点) + pcm = decoder.decode(opus_frame, frame_size) + pcm_datas.append(pcm) + + pcm_bytes = b"".join(pcm_datas) + + # 写入wav字节流 + wav_buffer = BytesIO() + with wave.open(wav_buffer, "wb") as wf: + wf.setnchannels(channels) + wf.setsampwidth(2) # 16bit + wf.setframerate(sample_rate) + wf.writeframes(pcm_bytes) + return wav_buffer.getvalue() + def check_vad_update(before_config, new_config): if ( new_config.get("selected_module") is None diff --git a/main/xiaozhi-server/core/utils/wakeup_word.py b/main/xiaozhi-server/core/utils/wakeup_word.py new file mode 100644 index 00000000..d2f4fb32 --- /dev/null +++ b/main/xiaozhi-server/core/utils/wakeup_word.py @@ -0,0 +1,140 @@ +import os +import re +import yaml +import time +import hashlib +import portalocker +from typing import Dict + + +class FileLock: + def __init__(self, file, timeout=5): + self.file = file + self.timeout = timeout + self.start_time = None + + def __enter__(self): + self.start_time = time.time() + while True: + try: + portalocker.lock(self.file, portalocker.LOCK_EX | portalocker.LOCK_NB) + return self.file + except portalocker.LockException: + if time.time() - self.start_time > self.timeout: + raise TimeoutError("获取文件锁超时") + time.sleep(0.1) + + def __exit__(self, exc_type, exc_val, exc_tb): + portalocker.unlock(self.file) + + +class WakeupWordsConfig: + def __init__(self): + self.config_file = "data/.wakeup_words.yaml" + self.assets_dir = "config/assets/wakeup_words" + self._ensure_directories() + self._config_cache = None + self._last_load_time = 0 + self._cache_ttl = 1 # 缓存有效期(秒) + self._lock_timeout = 5 # 文件锁超时时间(秒) + + def _ensure_directories(self): + """确保必要的目录存在""" + os.makedirs(os.path.dirname(self.config_file), exist_ok=True) + os.makedirs(self.assets_dir, exist_ok=True) + + def _load_config(self) -> Dict: + """加载配置文件,使用缓存机制""" + current_time = time.time() + + # 如果缓存有效,直接返回缓存 + if ( + self._config_cache is not None + and current_time - self._last_load_time < self._cache_ttl + ): + return self._config_cache + + try: + with open(self.config_file, "a+") as f: + with FileLock(f, timeout=self._lock_timeout): + f.seek(0) + content = f.read() + config = yaml.safe_load(content) if content else {} + self._config_cache = config + self._last_load_time = current_time + return config + except (TimeoutError, IOError) as e: + print(f"加载配置文件失败: {e}") + return {} + except Exception as e: + print(f"加载配置文件时发生未知错误: {e}") + return {} + + def _save_config(self, config: Dict): + """保存配置到文件,使用文件锁保护""" + try: + with open(self.config_file, "w") as f: + with FileLock(f, timeout=self._lock_timeout): + yaml.dump(config, f, allow_unicode=True) + self._config_cache = config + self._last_load_time = time.time() + except (TimeoutError, IOError) as e: + print(f"保存配置文件失败: {e}") + raise + except Exception as e: + print(f"保存配置文件时发生未知错误: {e}") + raise + + def get_wakeup_response(self, voice: str) -> Dict: + voice = hashlib.md5(voice.encode()).hexdigest() + """获取唤醒词回复配置""" + config = self._load_config() + + if not config or voice not in config: + return None + + # 检查文件大小 + file_path = config[voice]["file_path"] + if not os.path.exists(file_path) or os.stat(file_path).st_size < (15 * 1024): + return None + + return config[voice] + + def update_wakeup_response(self, voice: str, file_path: str, text: str): + """更新唤醒词回复配置""" + try: + # 过滤表情符号 + filtered_text = re.sub(r'[\U0001F600-\U0001F64F\U0001F900-\U0001F9FF]', '', text) + + config = self._load_config() + voice_hash = hashlib.md5(voice.encode()).hexdigest() + config[voice_hash] = { + "voice": voice, + "file_path": file_path, + "time": time.time(), + "text": filtered_text, + } + self._save_config(config) + except Exception as e: + print(f"更新唤醒词回复配置失败: {e}") + raise + + def generate_file_path(self, voice: str) -> str: + """生成音频文件路径,使用voice的哈希值作为文件名""" + try: + # 生成voice的哈希值 + voice_hash = hashlib.md5(voice.encode()).hexdigest() + file_path = os.path.join(self.assets_dir, f"{voice_hash}.wav") + + # 如果文件已存在,先删除 + if os.path.exists(file_path): + try: + os.remove(file_path) + except Exception as e: + print(f"删除已存在的音频文件失败: {e}") + raise + + return file_path + except Exception as e: + print(f"生成音频文件路径失败: {e}") + raise \ No newline at end of file From 0364d1199b4c447fdf3bea2d4da97ab7922e063a Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Tue, 2 Sep 2025 11:23:46 +0800 Subject: [PATCH 21/35] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E5=A4=A9?= =?UTF-8?q?=E6=B0=94=E6=8F=92=E4=BB=B6=E4=BD=BF=E7=94=A8=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Deployment_all.md | 3 +- docs/weather-integration.md | 292 ++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 docs/weather-integration.md diff --git a/docs/Deployment_all.md b/docs/Deployment_all.md index 135b7cf5..96b3d75c 100644 --- a/docs/Deployment_all.md +++ b/docs/Deployment_all.md @@ -476,7 +476,8 @@ ws://你电脑局域网的ip:8000/xiaozhi/v1/ 4、[如何部署MCP接入点](./mcp-endpoint-enable.md)
5、[如何接入MCP接入点](./mcp-endpoint-integration.md)
6、[如何开启声纹识别](./voiceprint-integration.md)
-10、[新闻插件源配置指南](./newsnow_plugin_config.md)
+7、[新闻插件源配置指南](./newsnow_plugin_config.md)
+8、[天气插件使用指南](./weather-integration.md)
## 语音克隆、本地语音部署相关教程 1、[如何部署集成index-tts本地语音](./index-stream-integration.md)
2、[如何部署集成fish-speech本地语音](./fish-speech-integration.md)
diff --git a/docs/weather-integration.md b/docs/weather-integration.md new file mode 100644 index 00000000..021b3f90 --- /dev/null +++ b/docs/weather-integration.md @@ -0,0 +1,292 @@ +# 天气插件使用指南 + +## 概述 + +天气插件 `get_weather` 是小智ESP32语音助手的核心功能之一,支持通过语音查询全国各地的天气信息。插件基于和风天气API,提供实时天气和7天天气预报功能。 + +## 功能特性 + +- **智能位置识别**:根据用户IP自动识别所在城市 +- **多地点查询**:支持全国各城市、地区天气查询 +- **详细天气信息**:包含当前天气、温度、湿度、风力等详细参数 +- **7天预报**:提供未来一周的天气趋势 +- **缓存机制**:优化查询速度,减少API调用 +- **智能解析**:支持省份、城市、地标等多种地点表达方式 + +## 配置方式 + +### 1. 通过Web管理界面配置(推荐) + +1. 登录智控台 +2. 进入"角色配置"页面 +3. 选择要配置的智能体 +4. 点击"编辑功能"按钮 +5. 在右侧参数配置区域找到"天气查询"插件 +6. 配置相关参数(天气插件API密钥、默认查询城市等) + +### 2. 配置文件方式 + +在 `config.yaml` 中配置: + +```yaml +plugins: + get_weather: + api_host: "你的和风天气API主机地址" # 和风天气API主机地址 + api_key: "你的和风天气API密钥" # 必填:API密钥 + default_location: "广州" # 默认查询城市 +``` + +## API Key 申请指南 + +### 1. 注册和风天气账号 + +1. 访问 [和风天气控制台](https://console.qweather.com/) +2. 注册账号并完成邮箱验证 +3. 登录控制台 + +### 2. 创建应用获取API Key + +1. 进入控制台后,点击右侧"项目管理" → "创建项目" +2. 填写项目信息: + - **项目名称**:如"小智语音助手" +3. 点击保存 +4. 创建完成后,在该项目中点击"创建凭据" +5. 填写凭据信息: + - **凭据名称**:如"小智语音助手" + - **身份认证方式**:选择"API Key" +6. 点击保存 +7. 在凭据中复制API Key到配置文件中 + +### 3. 获取API Host + +1. 在控制台中点击"设置" → "API Host" +2. 查看分配给你的专属API Host地址 +3. 将地址复制到配置文件的 `api_host` 字段 + +### 4. 额度说明 + +- **免费额度**:每天1000次免费调用 +- **付费计划**:超出免费额度后按调用次数计费 +- **建议**:个人使用免费额度通常足够 + +## 使用方法 + +### 基本语音指令 + +#### 1. 查询当前位置天气 +``` +用户:天气怎么样? +用户:今天天气如何? +用户:现在天气情况 +``` + +#### 2. 查询指定城市天气 +``` +用户:北京天气 +用户:上海的天气怎么样? +用户:查询杭州天气 +用户:广州今天天气如何? +``` + +#### 3. 查询省份天气(自动使用省会城市) +``` +用户:山东天气 +用户:浙江省天气怎么样? +用户:四川的天气 +``` + +#### 4. 查询地标或区域天气 +``` +用户:西湖天气 # 自动解析为杭州 +用户:外滩天气 # 自动解析为上海 +用户:天安门天气 # 自动解析为北京 +``` + +### 返回信息格式 + +插件会返回以下信息: + +``` +您查询的位置是:北京 + +当前天气: 晴,26°C + +详细参数: + · 湿度: 45% + · 风力: 3级 + · 气压: 1013hPa + · 能见度: 10km + +未来7天预报: +今天: 晴,气温 18°C~28°C +明天: 多云,气温 16°C~25°C +后天: 小雨,气温 14°C~22°C +周四: 阴,气温 15°C~20°C +周五: 晴,气温 17°C~24°C +周六: 多云,气温 19°C~26°C +周日: 晴,气温 21°C~28°C + +(如需某一天的具体天气,请告诉我日期) +``` + +## 支持的天气类型 + +插件支持以下天气现象的识别和显示: + +### 晴天类型 +- 晴 +- 多云 +- 少云 +- 晴间多云 + +### 阴雨类型 +- 阴 +- 阵雨、强阵雨 +- 雷阵雨、强雷阵雨 +- 雷阵雨伴有冰雹 +- 小雨、中雨、大雨 +- 极端降雨、暴雨、大暴雨、特大暴雨 +- 毛毛雨/细雨、冻雨 + +### 雪天类型 +- 小雪、中雪、大雪、暴雪 +- 雨夹雪、雨雪天气 +- 阵雨夹雪、阵雪 + +### 特殊天气 +- 薄雾、雾、浓雾 +- 霾、中度霾、重度霾、严重霾 +- 扬沙、浮尘、沙尘暴、强沙尘暴 + +## 智能位置解析 + +### 1. IP地址定位 +- 自动获取用户设备IP地址 +- 解析IP对应的城市信息 +- 缓存IP位置信息,提高响应速度 + +### 2. 地点名称智能匹配 +- **城市名**:直接查询该城市天气 +- **省份名**:自动使用省会城市 +- **地标名**:解析到所在城市 +- **区县名**:解析到所属市级城市 + +### 3. 默认位置机制 +- 当无法识别用户位置时,使用配置的默认城市 +- 建议设置为用户常居住的城市 + +## 缓存机制 + +### 1. IP位置缓存 +- 缓存用户IP对应的城市信息 +- 避免重复调用IP定位API +- 提高查询响应速度 + +### 2. 天气数据缓存 +- 缓存完整的天气报告 +- 缓存时间:通常为10-30分钟 +- 减少API调用次数,节省配额 + +## 故障排除 + +### 1. 查询失败 + +**现象**:提示"未找到相关的城市" + +**原因及解决方案**: +- 检查城市名称拼写是否正确 +- 尝试使用标准城市名称(如"北京"而非"北京市") +- 检查API Key是否有效 +- 确认网络连接正常 + +### 2. API调用超限 + +**现象**:返回API错误信息 + +**解决方案**: +- 检查和风天气控制台的配额使用情况 +- 考虑升级到付费计划 +- 优化缓存策略,减少API调用 + +### 3. 网络连接问题 + +**现象**:查询超时或连接失败 + +**解决方案**: +- 检查服务器网络连接 +- 确认和风天气API服务状态 +- 检查防火墙设置 + +### 4. 配置问题 + +**现象**:插件无法正常工作 + +**解决方案**: +```yaml +# 检查配置格式是否正确 +plugins: + get_weather: + api_host: "你的API主机地址" + api_key: "你的API密钥" + default_location: "默认城市" +``` + +## 高级配置 + +### 1. 自定义缓存时间 + +修改缓存管理器配置: + +```python +# 在 core/utils/cache/manager.py 中调整缓存时间 +WEATHER_CACHE_DURATION = 1800 # 30分钟 +``` + +### 2. 多语言支持 + +插件支持多语言查询: + +```yaml +# 在函数调用时指定语言 +get_weather(location="北京", lang="en_US") # 英文 +get_weather(location="北京", lang="ja_JP") # 日文 +``` + +### 3. 自定义默认位置 + +根据用户群体设置合适的默认城市: + +```yaml +plugins: + get_weather: + default_location: "上海" # 针对华东用户 + # default_location: "深圳" # 针对华南用户 + # default_location: "成都" # 针对西南用户 +``` + +## 注意事项 + +1. **API配额管理**:合理使用免费配额,避免频繁查询 +2. **地点准确性**:使用标准的城市名称获得最准确的结果 +3. **网络依赖**:功能依赖网络连接,确保服务器能访问外网 +4. **隐私保护**:IP定位信息仅用于天气查询,不会存储个人隐私 +5. **数据准确性**:天气数据来源于和风天气,准确性以官方为准 + +## 更新记录 + +- **v1.0.0**:基础天气查询功能 +- **v1.1.0**:增加IP自动定位功能 +- **v1.2.0**:优化缓存机制,提升查询速度 +- **v1.3.0**:支持7天天气预报 +- **v1.4.0**:智能地点解析,支持地标查询 + +## 相关链接 + +- [和风天气官网](https://www.qweather.com/) +- [和风天气开发文档](https://dev.qweather.com/) +- [API Key申请](https://console.qweather.com/#/apps/create-key/over) +- [项目GitHub](https://github.com/xinnan-tech/xiaozhi-esp32-server) + +--- + +*如有问题或建议,请在项目GitHub仓库提交Issue或联系开发团队。* From c3c2e7c28e565479c106d8d294005d1637df5d5a Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 12:00:04 +0800 Subject: [PATCH 22/35] =?UTF-8?q?fix:=20=20=E6=8F=90=E7=A4=BA=E5=B0=BE?= =?UTF-8?q?=E9=9F=B3=E6=92=AD=E6=94=BE=E5=92=8C=E5=8D=95=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E6=AC=A1=E5=94=A4=E9=86=92=E6=97=B6=E7=AD=89?= =?UTF-8?q?=E5=BE=85tts=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaozhi-server/core/handle/helloHandle.py | 11 ++++++++- .../core/handle/sendAudioHandle.py | 23 ++++++++----------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/main/xiaozhi-server/core/handle/helloHandle.py b/main/xiaozhi-server/core/handle/helloHandle.py index bb307b87..5b5818e2 100644 --- a/main/xiaozhi-server/core/handle/helloHandle.py +++ b/main/xiaozhi-server/core/handle/helloHandle.py @@ -56,7 +56,16 @@ async def checkWakeupWords(conn, text): "enable_wakeup_words_response_cache" ] - if not enable_wakeup_words_response_cache or not conn.tts: + # 等待tts初始化,最多等待3秒 + start_time = time.time() + while time.time() - start_time < 3: + if conn.tts: + break + await asyncio.sleep(0.1) + else: + return False + + if not enable_wakeup_words_response_cache: return False _, filtered_text = remove_punctuation_and_length(text) diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index f482a1ef..bafaf16e 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -2,6 +2,7 @@ import json import time import asyncio from core.utils import textUtils +from core.utils.util import audio_to_data_stream from core.providers.tts.dto.dto import SentenceType TAG = __name__ @@ -30,7 +31,7 @@ async def sendAudioMessage(conn, sentenceType, audios, text): # 播放音频 -async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): +async def sendAudio(conn, audios, frame_duration=60): """ 发送单个opus包,支持流控 Args: @@ -46,11 +47,6 @@ async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): if conn.client_abort: return - # 短音频直接发送(例如:提示音) - if pre_buffer: - await conn.websocket.send(audios) - return - conn.last_activity_time = time.time() * 1000 # 获取或初始化流控状态 @@ -78,7 +74,7 @@ async def sendAudio(conn, audios, pre_buffer=False, frame_duration=60): flow_control["packet_count"] += 1 flow_control["last_send_time"] = time.perf_counter() else: - # 流控参数优化 + # 提示音和唤醒音频走普通播放 start_time = time.perf_counter() play_position = 0 @@ -124,12 +120,13 @@ async def send_tts_message(conn, state, text=None): stop_tts_notify_voice = conn.config.get( "stop_tts_notify_voice", "config/assets/tts_notify.mp3" ) - conn.tts.audio_to_opus_data_stream( - stop_tts_notify_voice, - callback=lambda audio_data: asyncio.run_coroutine_threadsafe( - sendAudio(conn, audio_data, True), conn.loop - ), - ) + # 获取音频数据 + audios = [] + def handle_audio_frame(frame_data): + audios.append(frame_data) + + audio_to_data_stream(stop_tts_notify_voice, is_opus=True, callback=handle_audio_frame) + await sendAudio(conn, audios) # 清除服务端讲话状态 conn.clearSpeakStatus() From a842f0688f0ca3add677e30c110d22415d8a537d Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Tue, 2 Sep 2025 12:36:04 +0800 Subject: [PATCH 23/35] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96=E5=A4=A9=E6=B0=94?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/weather-integration.md | 263 +++--------------------------------- 1 file changed, 16 insertions(+), 247 deletions(-) diff --git a/docs/weather-integration.md b/docs/weather-integration.md index 021b3f90..c13446fc 100644 --- a/docs/weather-integration.md +++ b/docs/weather-integration.md @@ -4,38 +4,6 @@ 天气插件 `get_weather` 是小智ESP32语音助手的核心功能之一,支持通过语音查询全国各地的天气信息。插件基于和风天气API,提供实时天气和7天天气预报功能。 -## 功能特性 - -- **智能位置识别**:根据用户IP自动识别所在城市 -- **多地点查询**:支持全国各城市、地区天气查询 -- **详细天气信息**:包含当前天气、温度、湿度、风力等详细参数 -- **7天预报**:提供未来一周的天气趋势 -- **缓存机制**:优化查询速度,减少API调用 -- **智能解析**:支持省份、城市、地标等多种地点表达方式 - -## 配置方式 - -### 1. 通过Web管理界面配置(推荐) - -1. 登录智控台 -2. 进入"角色配置"页面 -3. 选择要配置的智能体 -4. 点击"编辑功能"按钮 -5. 在右侧参数配置区域找到"天气查询"插件 -6. 配置相关参数(天气插件API密钥、默认查询城市等) - -### 2. 配置文件方式 - -在 `config.yaml` 中配置: - -```yaml -plugins: - get_weather: - api_host: "你的和风天气API主机地址" # 和风天气API主机地址 - api_key: "你的和风天气API密钥" # 必填:API密钥 - default_location: "广州" # 默认查询城市 -``` - ## API Key 申请指南 ### 1. 注册和风天气账号 @@ -46,11 +14,11 @@ plugins: ### 2. 创建应用获取API Key -1. 进入控制台后,点击右侧"项目管理" → "创建项目" +1. 进入控制台后,点击右侧"项目管理"(https://console.qweather.com/project?lang=zh) → "创建项目" 2. 填写项目信息: - **项目名称**:如"小智语音助手" 3. 点击保存 -4. 创建完成后,在该项目中点击"创建凭据" +4. 项目创建完成后,在该项目中点击"创建凭据" 5. 填写凭据信息: - **凭据名称**:如"小智语音助手" - **身份认证方式**:选择"API Key" @@ -59,7 +27,7 @@ plugins: ### 3. 获取API Host -1. 在控制台中点击"设置" → "API Host" +1. 在控制台中点击"设置"(https://console.qweather.com/setting?lang=zh) → "API Host" 2. 查看分配给你的专属API Host地址 3. 将地址复制到配置文件的 `api_host` 字段 @@ -69,224 +37,25 @@ plugins: - **付费计划**:超出免费额度后按调用次数计费 - **建议**:个人使用免费额度通常足够 -## 使用方法 +## 配置方式(任选一种) -### 基本语音指令 +### 方式1. 通过Web管理界面配置(推荐) -#### 1. 查询当前位置天气 -``` -用户:天气怎么样? -用户:今天天气如何? -用户:现在天气情况 -``` +1. 登录智控台 +2. 进入"角色配置"页面 +3. 选择要配置的智能体 +4. 点击"编辑功能"按钮 +5. 在右侧参数配置区域找到"天气查询"插件 +6. 配置相关参数(天气插件API密钥、默认查询城市等) -#### 2. 查询指定城市天气 -``` -用户:北京天气 -用户:上海的天气怎么样? -用户:查询杭州天气 -用户:广州今天天气如何? -``` +### 方式2. 配置文件方式 -#### 3. 查询省份天气(自动使用省会城市) -``` -用户:山东天气 -用户:浙江省天气怎么样? -用户:四川的天气 -``` - -#### 4. 查询地标或区域天气 -``` -用户:西湖天气 # 自动解析为杭州 -用户:外滩天气 # 自动解析为上海 -用户:天安门天气 # 自动解析为北京 -``` - -### 返回信息格式 - -插件会返回以下信息: - -``` -您查询的位置是:北京 - -当前天气: 晴,26°C - -详细参数: - · 湿度: 45% - · 风力: 3级 - · 气压: 1013hPa - · 能见度: 10km - -未来7天预报: -今天: 晴,气温 18°C~28°C -明天: 多云,气温 16°C~25°C -后天: 小雨,气温 14°C~22°C -周四: 阴,气温 15°C~20°C -周五: 晴,气温 17°C~24°C -周六: 多云,气温 19°C~26°C -周日: 晴,气温 21°C~28°C - -(如需某一天的具体天气,请告诉我日期) -``` - -## 支持的天气类型 - -插件支持以下天气现象的识别和显示: - -### 晴天类型 -- 晴 -- 多云 -- 少云 -- 晴间多云 - -### 阴雨类型 -- 阴 -- 阵雨、强阵雨 -- 雷阵雨、强雷阵雨 -- 雷阵雨伴有冰雹 -- 小雨、中雨、大雨 -- 极端降雨、暴雨、大暴雨、特大暴雨 -- 毛毛雨/细雨、冻雨 - -### 雪天类型 -- 小雪、中雪、大雪、暴雪 -- 雨夹雪、雨雪天气 -- 阵雨夹雪、阵雪 - -### 特殊天气 -- 薄雾、雾、浓雾 -- 霾、中度霾、重度霾、严重霾 -- 扬沙、浮尘、沙尘暴、强沙尘暴 - -## 智能位置解析 - -### 1. IP地址定位 -- 自动获取用户设备IP地址 -- 解析IP对应的城市信息 -- 缓存IP位置信息,提高响应速度 - -### 2. 地点名称智能匹配 -- **城市名**:直接查询该城市天气 -- **省份名**:自动使用省会城市 -- **地标名**:解析到所在城市 -- **区县名**:解析到所属市级城市 - -### 3. 默认位置机制 -- 当无法识别用户位置时,使用配置的默认城市 -- 建议设置为用户常居住的城市 - -## 缓存机制 - -### 1. IP位置缓存 -- 缓存用户IP对应的城市信息 -- 避免重复调用IP定位API -- 提高查询响应速度 - -### 2. 天气数据缓存 -- 缓存完整的天气报告 -- 缓存时间:通常为10-30分钟 -- 减少API调用次数,节省配额 - -## 故障排除 - -### 1. 查询失败 - -**现象**:提示"未找到相关的城市" - -**原因及解决方案**: -- 检查城市名称拼写是否正确 -- 尝试使用标准城市名称(如"北京"而非"北京市") -- 检查API Key是否有效 -- 确认网络连接正常 - -### 2. API调用超限 - -**现象**:返回API错误信息 - -**解决方案**: -- 检查和风天气控制台的配额使用情况 -- 考虑升级到付费计划 -- 优化缓存策略,减少API调用 - -### 3. 网络连接问题 - -**现象**:查询超时或连接失败 - -**解决方案**: -- 检查服务器网络连接 -- 确认和风天气API服务状态 -- 检查防火墙设置 - -### 4. 配置问题 - -**现象**:插件无法正常工作 - -**解决方案**: -```yaml -# 检查配置格式是否正确 -plugins: - get_weather: - api_host: "你的API主机地址" - api_key: "你的API密钥" - default_location: "默认城市" -``` - -## 高级配置 - -### 1. 自定义缓存时间 - -修改缓存管理器配置: - -```python -# 在 core/utils/cache/manager.py 中调整缓存时间 -WEATHER_CACHE_DURATION = 1800 # 30分钟 -``` - -### 2. 多语言支持 - -插件支持多语言查询: - -```yaml -# 在函数调用时指定语言 -get_weather(location="北京", lang="en_US") # 英文 -get_weather(location="北京", lang="ja_JP") # 日文 -``` - -### 3. 自定义默认位置 - -根据用户群体设置合适的默认城市: +在 `config.yaml` 中配置: ```yaml plugins: get_weather: - default_location: "上海" # 针对华东用户 - # default_location: "深圳" # 针对华南用户 - # default_location: "成都" # 针对西南用户 + api_host: "你的和风天气API主机地址" # 和风天气API主机地址 + api_key: "你的和风天气API密钥" # 必填:API密钥 + default_location: "你的默认查询城市" # 默认查询城市 ``` - -## 注意事项 - -1. **API配额管理**:合理使用免费配额,避免频繁查询 -2. **地点准确性**:使用标准的城市名称获得最准确的结果 -3. **网络依赖**:功能依赖网络连接,确保服务器能访问外网 -4. **隐私保护**:IP定位信息仅用于天气查询,不会存储个人隐私 -5. **数据准确性**:天气数据来源于和风天气,准确性以官方为准 - -## 更新记录 - -- **v1.0.0**:基础天气查询功能 -- **v1.1.0**:增加IP自动定位功能 -- **v1.2.0**:优化缓存机制,提升查询速度 -- **v1.3.0**:支持7天天气预报 -- **v1.4.0**:智能地点解析,支持地标查询 - -## 相关链接 - -- [和风天气官网](https://www.qweather.com/) -- [和风天气开发文档](https://dev.qweather.com/) -- [API Key申请](https://console.qweather.com/#/apps/create-key/over) -- [项目GitHub](https://github.com/xinnan-tech/xiaozhi-esp32-server) - ---- - -*如有问题或建议,请在项目GitHub仓库提交Issue或联系开发团队。* From 0f0a0814b76c011b68e7c25e7b076a51efb77c22 Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Tue, 2 Sep 2025 12:45:51 +0800 Subject: [PATCH 24/35] =?UTF-8?q?fix=EF=BC=9A=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/weather-integration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/weather-integration.md b/docs/weather-integration.md index c13446fc..4832e093 100644 --- a/docs/weather-integration.md +++ b/docs/weather-integration.md @@ -14,7 +14,7 @@ ### 2. 创建应用获取API Key -1. 进入控制台后,点击右侧"项目管理"(https://console.qweather.com/project?lang=zh) → "创建项目" +1. 进入控制台后,点击右侧["项目管理"](https://console.qweather.com/project?lang=zh) → "创建项目" 2. 填写项目信息: - **项目名称**:如"小智语音助手" 3. 点击保存 @@ -27,7 +27,7 @@ ### 3. 获取API Host -1. 在控制台中点击"设置"(https://console.qweather.com/setting?lang=zh) → "API Host" +1. 在控制台中点击["设置"](https://console.qweather.com/setting?lang=zh) → "API Host" 2. 查看分配给你的专属API Host地址 3. 将地址复制到配置文件的 `api_host` 字段 From 5db638c075143a4ef05236e0ece78774182ea97a Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Tue, 2 Sep 2025 14:04:42 +0800 Subject: [PATCH 25/35] Update weather-integration.md --- docs/weather-integration.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/weather-integration.md b/docs/weather-integration.md index 4832e093..3b6ca2b6 100644 --- a/docs/weather-integration.md +++ b/docs/weather-integration.md @@ -23,39 +23,42 @@ - **凭据名称**:如"小智语音助手" - **身份认证方式**:选择"API Key" 6. 点击保存 -7. 在凭据中复制API Key到配置文件中 +7. 在凭据中复制`API Key`,这是第一个关键的配置信息 ### 3. 获取API Host 1. 在控制台中点击["设置"](https://console.qweather.com/setting?lang=zh) → "API Host" -2. 查看分配给你的专属API Host地址 -3. 将地址复制到配置文件的 `api_host` 字段 +2. 查看分配给你的专属`API Host`地址,这个是第二个关键的配置信息 -### 4. 额度说明 - -- **免费额度**:每天1000次免费调用 -- **付费计划**:超出免费额度后按调用次数计费 -- **建议**:个人使用免费额度通常足够 +以上操作,会得到两个重要的配置信息:`API Key`和`API Host` ## 配置方式(任选一种) -### 方式1. 通过Web管理界面配置(推荐) +### 方式1. 如果你使用了智控台部署(推荐) 1. 登录智控台 2. 进入"角色配置"页面 3. 选择要配置的智能体 4. 点击"编辑功能"按钮 5. 在右侧参数配置区域找到"天气查询"插件 -6. 配置相关参数(天气插件API密钥、默认查询城市等) +6. 勾选"天气查询" +7. 将复制过来的第一个关键配置`API Key`,填入到`天气插件 API 密钥`里 +8. 将复制过来的第二个关键配置`API Host`,填入到`开发者 API Host`里 +9. 保存配置,再保存智能体配置 -### 方式2. 配置文件方式 +### 方式2. 如果你只是单模块xiaozhi-server部署 -在 `config.yaml` 中配置: +在 `data/.config.yaml` 中配置: + +1. 将复制过来的第一个关键配置`API Key`,填入到`api_key`里 +2. 将复制过来的第二个关键配置`API Host`,填入到`api_host`里 +3. 将你所在的城市填入到`default_location`里,例如`广州` ```yaml plugins: get_weather: - api_host: "你的和风天气API主机地址" # 和风天气API主机地址 - api_key: "你的和风天气API密钥" # 必填:API密钥 - default_location: "你的默认查询城市" # 默认查询城市 + api_key: "你的和风天气API密钥" + api_host: "你的和风天气API主机地址" + default_location: "你的默认查询城市" ``` + From b9df6d4ad2948a3c70bc6999704cd6c1a061bd89 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 14:34:26 +0800 Subject: [PATCH 26/35] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaozhi-server/core/handle/helloHandle.py | 9 +--- .../core/handle/sendAudioHandle.py | 9 +--- main/xiaozhi-server/core/utils/util.py | 50 +++++++++++++++++++ 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/main/xiaozhi-server/core/handle/helloHandle.py b/main/xiaozhi-server/core/handle/helloHandle.py index 5b5818e2..75b9fcb2 100644 --- a/main/xiaozhi-server/core/handle/helloHandle.py +++ b/main/xiaozhi-server/core/handle/helloHandle.py @@ -3,7 +3,7 @@ import json import random import asyncio from core.utils.dialogue import Message -from core.utils.util import audio_to_data_stream +from core.utils.util import audio_to_data from core.providers.tts.dto.dto import SentenceType from core.utils.wakeup_word import WakeupWordsConfig from core.handle.sendAudioHandle import sendAudioMessage, send_stt_message @@ -91,12 +91,7 @@ async def checkWakeupWords(conn, text): } # 获取音频数据 - opus_packets = [] - def handle_audio_frame(frame_data): - opus_packets.append(frame_data) - - audio_to_data_stream(response.get("file_path"), is_opus=True, callback=handle_audio_frame) - + opus_packets = audio_to_data(response.get("file_path")) # 播放唤醒词回复 conn.client_abort = False diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index bafaf16e..dc7abf09 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -2,7 +2,7 @@ import json import time import asyncio from core.utils import textUtils -from core.utils.util import audio_to_data_stream +from core.utils.util import audio_to_data from core.providers.tts.dto.dto import SentenceType TAG = __name__ @@ -120,12 +120,7 @@ async def send_tts_message(conn, state, text=None): stop_tts_notify_voice = conn.config.get( "stop_tts_notify_voice", "config/assets/tts_notify.mp3" ) - # 获取音频数据 - audios = [] - def handle_audio_frame(frame_data): - audios.append(frame_data) - - audio_to_data_stream(stop_tts_notify_voice, is_opus=True, callback=handle_audio_frame) + audios = audio_to_data(stop_tts_notify_voice, is_opus=True) await sendAudio(conn, audios) # 清除服务端讲话状态 conn.clearSpeakStatus() diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 49782bfb..92a84bbb 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -230,6 +230,56 @@ def audio_to_data_stream(audio_file_path, is_opus=True, callback: Callable[[Any] raw_data = audio.raw_data pcm_to_data_stream(raw_data, is_opus, callback) +def audio_to_data(audio_file_path: str, is_opus: bool = True) -> list[bytes]: + """ + 将音频文件转换为Opus/PCM编码的帧列表 + Args: + audio_file_path: 音频文件路径 + is_opus: 是否进行Opus编码 + """ + # 获取文件后缀名 + file_type = os.path.splitext(audio_file_path)[1] + if file_type: + file_type = file_type.lstrip(".") + # 读取音频文件,-nostdin 参数:不要从标准输入读取数据,否则FFmpeg会阻塞 + audio = AudioSegment.from_file( + audio_file_path, format=file_type, parameters=["-nostdin"] + ) + + # 转换为单声道/16kHz采样率/16位小端编码(确保与编码器匹配) + audio = audio.set_channels(1).set_frame_rate(16000).set_sample_width(2) + + # 获取原始PCM数据(16位小端) + raw_data = audio.raw_data + + # 初始化Opus编码器 + encoder = opuslib_next.Encoder(16000, 1, opuslib_next.APPLICATION_AUDIO) + + # 编码参数 + frame_duration = 60 # 60ms per frame + frame_size = int(16000 * frame_duration / 1000) # 960 samples/frame + + datas = [] + # 按帧处理所有音频数据(包括最后一帧可能补零) + for i in range(0, len(raw_data), frame_size * 2): # 16bit=2bytes/sample + # 获取当前帧的二进制数据 + chunk = raw_data[i : i + frame_size * 2] + + # 如果最后一帧不足,补零 + if len(chunk) < frame_size * 2: + chunk += b"\x00" * (frame_size * 2 - len(chunk)) + + if is_opus: + # 转换为numpy数组处理 + np_frame = np.frombuffer(chunk, dtype=np.int16) + # 编码Opus数据 + frame_data = encoder.encode(np_frame.tobytes(), frame_size) + else: + frame_data = chunk if isinstance(chunk, bytes) else bytes(chunk) + + datas.append(frame_data) + + return datas def audio_bytes_to_data_stream(audio_bytes, file_type, is_opus, callback: Callable[[Any], Any]) -> None: """ From 79956ade97dd46cebe25020bba1b8001c742a48b Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 14:36:13 +0800 Subject: [PATCH 27/35] =?UTF-8?q?fix:=20=E5=8F=98=E9=87=8F=E5=90=8D?= =?UTF-8?q?=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/providers/tts/fishspeech.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/core/providers/tts/fishspeech.py b/main/xiaozhi-server/core/providers/tts/fishspeech.py index bbf19164..83b3bc3f 100644 --- a/main/xiaozhi-server/core/providers/tts/fishspeech.py +++ b/main/xiaozhi-server/core/providers/tts/fishspeech.py @@ -143,8 +143,8 @@ class TTSProvider(TTSProviderBase): data = { "text": text, "references": [ - ServeReferenceAudio(audio=audio if audio else b"", text=text) - for text, audio in zip(ref_texts, byte_audios) + ServeReferenceAudio(audio=audio if audio else b"", text=ref_text) + for ref_text, audio in zip(ref_texts, byte_audios) ], "reference_id": self.reference_id, "normalize": self.normalize, From f45bc5d9c93ed58935ef70bb666221306b6f9b6b Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 15:24:51 +0800 Subject: [PATCH 28/35] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9F=B3=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/handle/receiveAudioHandle.py | 17 +++++++++++------ main/xiaozhi-server/core/utils/util.py | 19 ------------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index afe60084..c224d9a0 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -4,7 +4,7 @@ import asyncio from core.handle.abortHandle import handleAbortMessage from core.handle.intentHandler import handle_user_intent from core.utils.output_counter import check_device_output_limit -from core.utils.util import play_audio_frames, play_audio_response +from core.utils.util import audio_to_data from core.handle.sendAudioHandle import send_stt_message, SentenceType TAG = __name__ @@ -114,10 +114,13 @@ async def no_voice_close_connect(conn, have_voice): async def max_out_size(conn): + # 播放字数限制回复 + conn.client_abort = False text = "不好意思,我现在有点事情要忙,明天这个时候我们再聊,约好了哦!明天不见不散,拜拜!" await send_stt_message(conn, text) file_path = "config/assets/max_output_size.wav" - play_audio_response(conn, {"text": text, "file_path": file_path}) + opus_packets = audio_to_data(file_path) + conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text)) conn.close_after_chat = True @@ -135,15 +138,16 @@ async def check_bind_device(conn): # 播放提示音 music_path = "config/assets/bind_code.wav" - conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], text)) - play_audio_frames(conn, music_path) + opus_packets = audio_to_data(music_path) + conn.tts.tts_audio_queue.put((SentenceType.FIRST, opus_packets, text)) # 逐个播放数字 for i in range(6): # 确保只播放6位数字 try: digit = conn.bind_code[i] num_path = f"config/assets/bind_code/{digit}.wav" - play_audio_frames(conn, num_path) + num_packets = audio_to_data(num_path) + conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, num_packets, None)) except Exception as e: conn.logger.bind(tag=TAG).error(f"播放数字音频失败: {e}") continue @@ -152,4 +156,5 @@ async def check_bind_device(conn): text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。" await send_stt_message(conn, text) music_path = "config/assets/bind_not_found.wav" - play_audio_response(conn, {"text": text, "file_path": music_path}) + opus_packets = audio_to_data(music_path) + conn.tts.tts_audio_queue.put((SentenceType.LAST, opus_packets, text)) diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 92a84bbb..26dc9fda 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -325,25 +325,6 @@ def pcm_to_data_stream(raw_data, is_opus=True, callback: Callable[[Any], Any] = frame_data = chunk if isinstance(chunk, bytes) else bytes(chunk) callback(frame_data) -def play_audio_response(conn, response): - """音频响应处理""" - conn.tts.tts_audio_queue.put((SentenceType.FIRST, [], response.get("text"))) - play_audio_frames(conn, response.get("file_path")) - conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) - - -def play_audio_frames(conn, file_path): - """播放音频文件并处理发送帧数据""" - def handle_audio_frame(frame_data): - conn.tts.tts_audio_queue.put((SentenceType.MIDDLE, frame_data, None)) - - audio_to_data_stream( - file_path, - is_opus=True, - callback=handle_audio_frame - ) - - def opus_datas_to_wav_bytes(opus_datas, sample_rate=16000, channels=1): """ 将opus帧列表解码为wav字节流 From fa3ddc2e2b103abbf3e264a369ea7fbb18179696 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 17:06:19 +0800 Subject: [PATCH 29/35] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/handle/receiveAudioHandle.py | 4 +--- main/xiaozhi-server/core/utils/util.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index c224d9a0..546d94a3 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -1,10 +1,10 @@ import time import json import asyncio +from core.utils.util import audio_to_data from core.handle.abortHandle import handleAbortMessage from core.handle.intentHandler import handle_user_intent from core.utils.output_counter import check_device_output_limit -from core.utils.util import audio_to_data from core.handle.sendAudioHandle import send_stt_message, SentenceType TAG = __name__ @@ -114,8 +114,6 @@ async def no_voice_close_connect(conn, have_voice): async def max_out_size(conn): - # 播放字数限制回复 - conn.client_abort = False text = "不好意思,我现在有点事情要忙,明天这个时候我们再聊,约好了哦!明天不见不散,拜拜!" await send_stt_message(conn, text) file_path = "config/assets/max_output_size.wav" diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index 26dc9fda..27471e58 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -12,7 +12,6 @@ from io import BytesIO from core.utils import p3 from pydub import AudioSegment from typing import Callable, Any -from core.providers.tts.dto.dto import SentenceType TAG = __name__ emoji_map = { From b88b102d38e4c86e4bda65073fbdbc3af7d06a4a Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Tue, 2 Sep 2025 17:08:56 +0800 Subject: [PATCH 30/35] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/handle/sendAudioHandle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/xiaozhi-server/core/handle/sendAudioHandle.py b/main/xiaozhi-server/core/handle/sendAudioHandle.py index dc7abf09..661c4e21 100644 --- a/main/xiaozhi-server/core/handle/sendAudioHandle.py +++ b/main/xiaozhi-server/core/handle/sendAudioHandle.py @@ -74,7 +74,7 @@ async def sendAudio(conn, audios, frame_duration=60): flow_control["packet_count"] += 1 flow_control["last_send_time"] = time.perf_counter() else: - # 提示音和唤醒音频走普通播放 + # 文件型音频走普通播放 start_time = time.perf_counter() play_position = 0 From 3cfc1cf97a38d058eba2f56ecfde8481d4b18737 Mon Sep 17 00:00:00 2001 From: Chingfeng Li Date: Wed, 3 Sep 2025 09:43:34 +0800 Subject: [PATCH 31/35] =?UTF-8?q?textHandle.py=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E8=A7=A3=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/handle/textHandle.py | 171 +----------------- .../handle/textHandler/abortMessageHandler.py | 16 ++ .../handle/textHandler/helloMessageHandler.py | 16 ++ .../handle/textHandler/iotMessageHandler.py | 20 ++ .../textHandler/listenMessageHandler.py | 63 +++++++ .../handle/textHandler/mcpMessageHandler.py | 20 ++ .../textHandler/serverMessageHandler.py | 92 ++++++++++ .../core/handle/textMessageHandler.py | 21 +++ .../core/handle/textMessageHandlerRegistry.py | 45 +++++ .../core/handle/textMessageProcessor.py | 41 +++++ .../core/handle/textMessageType.py | 11 ++ 11 files changed, 353 insertions(+), 163 deletions(-) create mode 100644 main/xiaozhi-server/core/handle/textHandler/abortMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textHandler/helloMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textHandler/iotMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textHandler/listenMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textHandler/mcpMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textHandler/serverMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textMessageHandler.py create mode 100644 main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py create mode 100644 main/xiaozhi-server/core/handle/textMessageProcessor.py create mode 100644 main/xiaozhi-server/core/handle/textMessageType.py diff --git a/main/xiaozhi-server/core/handle/textHandle.py b/main/xiaozhi-server/core/handle/textHandle.py index 1bcf4545..b5e87783 100644 --- a/main/xiaozhi-server/core/handle/textHandle.py +++ b/main/xiaozhi-server/core/handle/textHandle.py @@ -1,169 +1,14 @@ -import json -import time -from core.handle.abortHandle import handleAbortMessage -from core.handle.helloHandle import handleHelloMessage -from core.providers.tools.device_mcp import handle_mcp_message -from core.utils.util import remove_punctuation_and_length, filter_sensitive_info -from core.handle.receiveAudioHandle import startToChat, handleAudioMessage -from core.handle.sendAudioHandle import send_stt_message, send_tts_message -from core.providers.tools.device_iot import handleIotDescriptors, handleIotStatus -from core.handle.reportHandle import enqueue_asr_report -import asyncio +from core.handle.textMessageHandlerRegistry import TextMessageHandlerRegistry +from core.handle.textMessageProcessor import TextMessageProcessor TAG = __name__ +# 全局处理器注册表 +message_registry = TextMessageHandlerRegistry() + +# 创建全局消息处理器实例 +message_processor = TextMessageProcessor(message_registry) async def handleTextMessage(conn, message): """处理文本消息""" - try: - msg_json = json.loads(message) - if isinstance(msg_json, int): - conn.logger.bind(tag=TAG).info(f"收到文本消息:{message}") - await conn.websocket.send(message) - return - if msg_json["type"] == "hello": - conn.logger.bind(tag=TAG).info(f"收到hello消息:{message}") - await handleHelloMessage(conn, msg_json) - elif msg_json["type"] == "abort": - conn.logger.bind(tag=TAG).info(f"收到abort消息:{message}") - await handleAbortMessage(conn) - elif msg_json["type"] == "listen": - conn.logger.bind(tag=TAG).info(f"收到listen消息:{message}") - if "mode" in msg_json: - conn.client_listen_mode = msg_json["mode"] - conn.logger.bind(tag=TAG).debug( - f"客户端拾音模式:{conn.client_listen_mode}" - ) - if msg_json["state"] == "start": - conn.client_have_voice = True - conn.client_voice_stop = False - elif msg_json["state"] == "stop": - conn.client_have_voice = True - conn.client_voice_stop = True - if len(conn.asr_audio) > 0: - await handleAudioMessage(conn, b"") - elif msg_json["state"] == "detect": - conn.client_have_voice = False - conn.asr_audio.clear() - if "text" in msg_json: - conn.last_activity_time = time.time() * 1000 - original_text = msg_json["text"] # 保留原始文本 - filtered_len, filtered_text = remove_punctuation_and_length( - original_text - ) - - # 识别是否是唤醒词 - is_wakeup_words = filtered_text in conn.config.get("wakeup_words") - # 是否开启唤醒词回复 - enable_greeting = conn.config.get("enable_greeting", True) - - if is_wakeup_words and not enable_greeting: - # 如果是唤醒词,且关闭了唤醒词回复,就不用回答 - await send_stt_message(conn, original_text) - await send_tts_message(conn, "stop", None) - conn.client_is_speaking = False - elif is_wakeup_words: - conn.just_woken_up = True - # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) - enqueue_asr_report(conn, "嘿,你好呀", []) - await startToChat(conn, "嘿,你好呀") - else: - # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) - enqueue_asr_report(conn, original_text, []) - # 否则需要LLM对文字内容进行答复 - await startToChat(conn, original_text) - elif msg_json["type"] == "iot": - conn.logger.bind(tag=TAG).info(f"收到iot消息:{message}") - if "descriptors" in msg_json: - asyncio.create_task(handleIotDescriptors(conn, msg_json["descriptors"])) - if "states" in msg_json: - asyncio.create_task(handleIotStatus(conn, msg_json["states"])) - elif msg_json["type"] == "mcp": - conn.logger.bind(tag=TAG).info(f"收到mcp消息:{message[:100]}") - if "payload" in msg_json: - asyncio.create_task( - handle_mcp_message(conn, conn.mcp_client, msg_json["payload"]) - ) - elif msg_json["type"] == "server": - # 记录日志时过滤敏感信息 - conn.logger.bind(tag=TAG).info( - f"收到服务器消息:{filter_sensitive_info(msg_json)}" - ) - # 如果配置是从API读取的,则需要验证secret - if not conn.read_config_from_api: - return - # 获取post请求的secret - post_secret = msg_json.get("content", {}).get("secret", "") - secret = conn.config["manager-api"].get("secret", "") - # 如果secret不匹配,则返回 - if post_secret != secret: - await conn.websocket.send( - json.dumps( - { - "type": "server", - "status": "error", - "message": "服务器密钥验证失败", - } - ) - ) - return - # 动态更新配置 - if msg_json["action"] == "update_config": - try: - # 更新WebSocketServer的配置 - if not conn.server: - await conn.websocket.send( - json.dumps( - { - "type": "server", - "status": "error", - "message": "无法获取服务器实例", - "content": {"action": "update_config"}, - } - ) - ) - return - - if not await conn.server.update_config(): - await conn.websocket.send( - json.dumps( - { - "type": "server", - "status": "error", - "message": "更新服务器配置失败", - "content": {"action": "update_config"}, - } - ) - ) - return - - # 发送成功响应 - await conn.websocket.send( - json.dumps( - { - "type": "server", - "status": "success", - "message": "配置更新成功", - "content": {"action": "update_config"}, - } - ) - ) - except Exception as e: - conn.logger.bind(tag=TAG).error(f"更新配置失败: {str(e)}") - await conn.websocket.send( - json.dumps( - { - "type": "server", - "status": "error", - "message": f"更新配置失败: {str(e)}", - "content": {"action": "update_config"}, - } - ) - ) - # 重启服务器 - elif msg_json["action"] == "restart": - await conn.handle_restart(msg_json) - else: - conn.logger.bind(tag=TAG).error(f"收到未知类型消息:{message}") - except json.JSONDecodeError: - await conn.websocket.send(message) + await message_processor.process_message(conn, message) diff --git a/main/xiaozhi-server/core/handle/textHandler/abortMessageHandler.py b/main/xiaozhi-server/core/handle/textHandler/abortMessageHandler.py new file mode 100644 index 00000000..dc540d24 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textHandler/abortMessageHandler.py @@ -0,0 +1,16 @@ +from typing import Dict, Any + +from core.handle.abortHandle import handleAbortMessage +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textMessageType import TextMessageType + + +class AbortTextMessageHandler(TextMessageHandler): + """Abort消息处理器""" + + @property + def message_type(self) -> TextMessageType: + return TextMessageType.ABORT + + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + await handleAbortMessage(conn) diff --git a/main/xiaozhi-server/core/handle/textHandler/helloMessageHandler.py b/main/xiaozhi-server/core/handle/textHandler/helloMessageHandler.py new file mode 100644 index 00000000..1839814e --- /dev/null +++ b/main/xiaozhi-server/core/handle/textHandler/helloMessageHandler.py @@ -0,0 +1,16 @@ +from typing import Dict, Any + +from core.handle.helloHandle import handleHelloMessage +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textMessageType import TextMessageType + + +class HelloTextMessageHandler(TextMessageHandler): + """Hello消息处理器""" + + @property + def message_type(self) -> TextMessageType: + return TextMessageType.HELLO + + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + await handleHelloMessage(conn, msg_json) \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/textHandler/iotMessageHandler.py b/main/xiaozhi-server/core/handle/textHandler/iotMessageHandler.py new file mode 100644 index 00000000..335d08b0 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textHandler/iotMessageHandler.py @@ -0,0 +1,20 @@ +import asyncio +from typing import Dict, Any + +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textMessageType import TextMessageType +from core.providers.tools.device_iot import handleIotStatus, handleIotDescriptors + + +class IotTextMessageHandler(TextMessageHandler): + """IOT消息处理器""" + + @property + def message_type(self) -> TextMessageType: + return TextMessageType.IOT + + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + if "descriptors" in msg_json: + asyncio.create_task(handleIotDescriptors(conn, msg_json["descriptors"])) + if "states" in msg_json: + asyncio.create_task(handleIotStatus(conn, msg_json["states"])) \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/textHandler/listenMessageHandler.py b/main/xiaozhi-server/core/handle/textHandler/listenMessageHandler.py new file mode 100644 index 00000000..97286dfe --- /dev/null +++ b/main/xiaozhi-server/core/handle/textHandler/listenMessageHandler.py @@ -0,0 +1,63 @@ +import time +from typing import Dict, Any + +from core.handle.receiveAudioHandle import handleAudioMessage, startToChat +from core.handle.reportHandle import enqueue_asr_report +from core.handle.sendAudioHandle import send_stt_message, send_tts_message +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textMessageType import TextMessageType +from core.utils.util import remove_punctuation_and_length + +TAG = __name__ + +class ListenTextMessageHandler(TextMessageHandler): + """Listen消息处理器""" + + @property + def message_type(self) -> TextMessageType: + return TextMessageType.LISTEN + + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + if "mode" in msg_json: + conn.client_listen_mode = msg_json["mode"] + conn.logger.bind(tag=TAG).debug( + f"客户端拾音模式:{conn.client_listen_mode}" + ) + if msg_json["state"] == "start": + conn.client_have_voice = True + conn.client_voice_stop = False + elif msg_json["state"] == "stop": + conn.client_have_voice = True + conn.client_voice_stop = True + if len(conn.asr_audio) > 0: + await handleAudioMessage(conn, b"") + elif msg_json["state"] == "detect": + conn.client_have_voice = False + conn.asr_audio.clear() + if "text" in msg_json: + conn.last_activity_time = time.time() * 1000 + original_text = msg_json["text"] # 保留原始文本 + filtered_len, filtered_text = remove_punctuation_and_length( + original_text + ) + + # 识别是否是唤醒词 + is_wakeup_words = filtered_text in conn.config.get("wakeup_words") + # 是否开启唤醒词回复 + enable_greeting = conn.config.get("enable_greeting", True) + + if is_wakeup_words and not enable_greeting: + # 如果是唤醒词,且关闭了唤醒词回复,就不用回答 + await send_stt_message(conn, original_text) + await send_tts_message(conn, "stop", None) + conn.client_is_speaking = False + elif is_wakeup_words: + conn.just_woken_up = True + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, "嘿,你好呀", []) + await startToChat(conn, "嘿,你好呀") + else: + # 上报纯文字数据(复用ASR上报功能,但不提供音频数据) + enqueue_asr_report(conn, original_text, []) + # 否则需要LLM对文字内容进行答复 + await startToChat(conn, original_text) \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/textHandler/mcpMessageHandler.py b/main/xiaozhi-server/core/handle/textHandler/mcpMessageHandler.py new file mode 100644 index 00000000..65876f24 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textHandler/mcpMessageHandler.py @@ -0,0 +1,20 @@ +import asyncio +from typing import Dict, Any + +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textMessageType import TextMessageType +from core.providers.tools.device_mcp import handle_mcp_message + + +class McpTextMessageHandler(TextMessageHandler): + """MCP消息处理器""" + + @property + def message_type(self) -> TextMessageType: + return TextMessageType.MCP + + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + if "payload" in msg_json: + asyncio.create_task( + handle_mcp_message(conn, conn.mcp_client, msg_json["payload"]) + ) \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/textHandler/serverMessageHandler.py b/main/xiaozhi-server/core/handle/textHandler/serverMessageHandler.py new file mode 100644 index 00000000..b9a23588 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textHandler/serverMessageHandler.py @@ -0,0 +1,92 @@ +import asyncio +import json +from typing import Dict, Any + +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textMessageType import TextMessageType +from core.providers.tools.device_mcp import handle_mcp_message + +TAG = __name__ + +class ServerTextMessageHandler(TextMessageHandler): + """MCP消息处理器""" + + @property + def message_type(self) -> TextMessageType: + return TextMessageType.SERVER + + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + # 如果配置是从API读取的,则需要验证secret + if not conn.read_config_from_api: + return + # 获取post请求的secret + post_secret = msg_json.get("content", {}).get("secret", "") + secret = conn.config["manager-api"].get("secret", "") + # 如果secret不匹配,则返回 + if post_secret != secret: + await conn.websocket.send( + json.dumps( + { + "type": "server", + "status": "error", + "message": "服务器密钥验证失败", + } + ) + ) + return + # 动态更新配置 + if msg_json["action"] == "update_config": + try: + # 更新WebSocketServer的配置 + if not conn.server: + await conn.websocket.send( + json.dumps( + { + "type": "server", + "status": "error", + "message": "无法获取服务器实例", + "content": {"action": "update_config"}, + } + ) + ) + return + + if not await conn.server.update_config(): + await conn.websocket.send( + json.dumps( + { + "type": "server", + "status": "error", + "message": "更新服务器配置失败", + "content": {"action": "update_config"}, + } + ) + ) + return + + # 发送成功响应 + await conn.websocket.send( + json.dumps( + { + "type": "server", + "status": "success", + "message": "配置更新成功", + "content": {"action": "update_config"}, + } + ) + ) + except Exception as e: + conn.logger.bind(tag=TAG).error(f"更新配置失败: {str(e)}") + await conn.websocket.send( + json.dumps( + { + "type": "server", + "status": "error", + "message": f"更新配置失败: {str(e)}", + "content": {"action": "update_config"}, + } + ) + ) + # 重启服务器 + elif msg_json["action"] == "restart": + await conn.handle_restart(msg_json) \ No newline at end of file diff --git a/main/xiaozhi-server/core/handle/textMessageHandler.py b/main/xiaozhi-server/core/handle/textMessageHandler.py new file mode 100644 index 00000000..f94a0bac --- /dev/null +++ b/main/xiaozhi-server/core/handle/textMessageHandler.py @@ -0,0 +1,21 @@ +from abc import abstractmethod, ABC +from typing import Dict, Any + +from core.handle.textMessageType import TextMessageType + +TAG = __name__ + + +class TextMessageHandler(ABC): + """消息处理器抽象基类""" + + @abstractmethod + async def handle(self, conn, msg_json: Dict[str, Any]) -> None: + """处理消息的抽象方法""" + pass + + @property + @abstractmethod + def message_type(self) -> TextMessageType: + """返回处理的消息类型""" + pass diff --git a/main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py b/main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py new file mode 100644 index 00000000..e90d7231 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textMessageHandlerRegistry.py @@ -0,0 +1,45 @@ +from typing import Dict, Optional + +from core.handle.textHandler.abortMessageHandler import AbortTextMessageHandler +from core.handle.textHandler.helloMessageHandler import HelloTextMessageHandler +from core.handle.textHandler.iotMessageHandler import IotTextMessageHandler +from core.handle.textHandler.listenMessageHandler import ListenTextMessageHandler +from core.handle.textHandler.mcpMessageHandler import McpTextMessageHandler +from core.handle.textMessageHandler import TextMessageHandler +from core.handle.textHandler.serverMessageHandler import ServerTextMessageHandler + +TAG = __name__ + + +class TextMessageHandlerRegistry: + """消息处理器注册表""" + + def __init__(self): + self._handlers: Dict[str, TextMessageHandler] = {} + self._register_default_handlers() + + def _register_default_handlers(self) -> None: + """注册默认的消息处理器""" + handlers = [ + HelloTextMessageHandler(), + AbortTextMessageHandler(), + ListenTextMessageHandler(), + IotTextMessageHandler(), + McpTextMessageHandler(), + ServerTextMessageHandler(), + ] + + for handler in handlers: + self.register_handler(handler) + + def register_handler(self, handler: TextMessageHandler) -> None: + """注册消息处理器""" + self._handlers[handler.message_type.value] = handler + + def get_handler(self, message_type: str) -> Optional[TextMessageHandler]: + """获取消息处理器""" + return self._handlers.get(message_type) + + def get_supported_types(self) -> list: + """获取支持的消息类型""" + return list(self._handlers.keys()) diff --git a/main/xiaozhi-server/core/handle/textMessageProcessor.py b/main/xiaozhi-server/core/handle/textMessageProcessor.py new file mode 100644 index 00000000..0cae5e09 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textMessageProcessor.py @@ -0,0 +1,41 @@ +import json + +from core.handle.textMessageHandlerRegistry import TextMessageHandlerRegistry + +TAG = __name__ + + +class TextMessageProcessor: + """消息处理器主类""" + + def __init__(self, registry: TextMessageHandlerRegistry): + self.registry = registry + + async def process_message(self, conn, message: str) -> None: + """处理消息的主入口""" + try: + # 解析JSON消息 + msg_json = json.loads(message) + + # 处理JSON消息 + if isinstance(msg_json, dict): + message_type = msg_json.get("type") + + # 记录日志 + conn.logger.bind(tag=TAG).info(f"收到{message_type}消息:{message}") + + # 获取并执行处理器 + handler = self.registry.get_handler(message_type) + if handler: + await handler.handle(conn, msg_json) + else: + conn.logger.bind(tag=TAG).error(f"收到未知类型消息:{message}") + # 处理纯数字消息 + elif isinstance(msg_json, int): + conn.logger.bind(tag=TAG).info(f"收到数字消息:{message}") + await conn.websocket.send(message) + + except json.JSONDecodeError: + # 非JSON消息直接转发 + conn.logger.bind(tag=TAG).error(f"解析到错误的消息:{message}") + await conn.websocket.send(message) diff --git a/main/xiaozhi-server/core/handle/textMessageType.py b/main/xiaozhi-server/core/handle/textMessageType.py new file mode 100644 index 00000000..53e71b71 --- /dev/null +++ b/main/xiaozhi-server/core/handle/textMessageType.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class TextMessageType(Enum): + """消息类型枚举""" + HELLO = "hello" + ABORT = "abort" + LISTEN = "listen" + IOT = "iot" + MCP = "mcp" + SERVER = "server" From 66f382039ce92cd820aedc925eb87628c6e6eaa8 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Wed, 3 Sep 2025 10:46:24 +0800 Subject: [PATCH 32/35] =?UTF-8?q?fix:=20=E6=89=93=E6=96=AD=E6=97=B6?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=9C=AA=E5=9B=9E=E6=AD=A3=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E6=97=A0=E6=B3=95=E6=AD=A3=E7=A1=AE=E6=92=AD=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/handle/receiveAudioHandle.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/xiaozhi-server/core/handle/receiveAudioHandle.py b/main/xiaozhi-server/core/handle/receiveAudioHandle.py index 546d94a3..8db50633 100644 --- a/main/xiaozhi-server/core/handle/receiveAudioHandle.py +++ b/main/xiaozhi-server/core/handle/receiveAudioHandle.py @@ -114,6 +114,8 @@ async def no_voice_close_connect(conn, have_voice): async def max_out_size(conn): + # 播放超出最大输出字数的提示 + conn.client_abort = False text = "不好意思,我现在有点事情要忙,明天这个时候我们再聊,约好了哦!明天不见不散,拜拜!" await send_stt_message(conn, text) file_path = "config/assets/max_output_size.wav" @@ -151,6 +153,8 @@ async def check_bind_device(conn): continue conn.tts.tts_audio_queue.put((SentenceType.LAST, [], None)) else: + # 播放未绑定提示 + conn.client_abort = False text = f"没有找到该设备的版本信息,请正确配置 OTA地址,然后重新编译固件。" await send_stt_message(conn, text) music_path = "config/assets/bind_not_found.wav" From 5afa2fdc9834e1c4b2959f598c3ae7f1f6dc2820 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Wed, 3 Sep 2025 17:29:21 +0800 Subject: [PATCH 33/35] =?UTF-8?q?fix:=20=E9=99=8D=E4=BD=8E=E8=AF=AF?= =?UTF-8?q?=E5=88=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/providers/vad/silero.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index 95ab8ff3..b516d8fb 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -33,8 +33,8 @@ class VADProvider(VADProviderBase): int(min_silence_duration_ms) if min_silence_duration_ms else 1000 ) - # 至少要多少帧才算有语音,增加灵敏度 - self.frame_window_threshold = 1 + # 至少要多少帧才算有语音 + self.frame_window_threshold = 3 def is_vad(self, conn, opus_packet): try: From 2b3cacd5b0025d8fc1385c82cf673cdc32aaba47 Mon Sep 17 00:00:00 2001 From: Sakura-RanChen <1908198662@qq.com> Date: Fri, 5 Sep 2025 10:31:47 +0800 Subject: [PATCH 34/35] =?UTF-8?q?fix:=20=E6=96=B9=E6=B3=95=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/utils/opus_encoder_utils.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/main/xiaozhi-server/core/utils/opus_encoder_utils.py b/main/xiaozhi-server/core/utils/opus_encoder_utils.py index 23d26c39..ae7066ce 100644 --- a/main/xiaozhi-server/core/utils/opus_encoder_utils.py +++ b/main/xiaozhi-server/core/utils/opus_encoder_utils.py @@ -5,12 +5,10 @@ Opus编码工具类 import logging import traceback - import numpy as np -from typing import List, Optional from opuslib_next import Encoder from opuslib_next import constants - +from typing import Optional, Callable, Any class OpusEncoderUtils: """PCM到Opus的编码器""" @@ -56,13 +54,14 @@ class OpusEncoderUtils: self.encoder.reset_state() self.buffer = np.array([], dtype=np.int16) - def encode_pcm_to_opus(self, pcm_data: bytes, end_of_stream: bool) -> List[bytes]: + def encode_pcm_to_opus_stream(self, pcm_data: bytes, end_of_stream: bool, callback: Callable[[Any], Any]): """ - 将PCM数据编码为Opus格式 + 将PCM数据编码为Opus格式,以流式方式进行处理 Args: pcm_data: PCM字节数据 - end_of_stream: 是否为流的结束 + end_of_stream: 是否为流的结束, + callback: opus处理方法 Returns: Opus数据包列表 @@ -76,7 +75,6 @@ class OpusEncoderUtils: # 将新数据追加到缓冲区 self.buffer = np.append(self.buffer, new_samples) - opus_packets = [] offset = 0 # 处理所有完整帧 @@ -84,7 +82,7 @@ class OpusEncoderUtils: frame = self.buffer[offset : offset + self.total_frame_size] output = self._encode(frame) if output: - opus_packets.append(output) + callback(output) offset += self.total_frame_size # 保留未处理的样本 @@ -98,11 +96,9 @@ class OpusEncoderUtils: output = self._encode(last_frame) if output: - opus_packets.append(output) + callback(output) self.buffer = np.array([], dtype=np.int16) - return opus_packets - def _encode(self, frame: np.ndarray) -> Optional[bytes]: """编码一帧音频数据""" try: @@ -133,4 +129,4 @@ class OpusEncoderUtils: def close(self): """关闭编码器并释放资源""" # opuslib没有明确的关闭方法,Python的垃圾回收会处理 - pass + pass \ No newline at end of file From 4a802e00e47d19eeb083a224eace20fe3c8852ac Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Fri, 5 Sep 2025 13:58:26 +0800 Subject: [PATCH 35/35] =?UTF-8?q?update:=E5=8D=87=E7=BA=A7=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaozhi/common/constant/Constant.java | 2 +- .../src/pages/settings/index.vue | 49 ++++++++----------- main/xiaozhi-server/config/logger.py | 2 +- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java index ba6567a4..afd7addf 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java +++ b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java @@ -237,7 +237,7 @@ public interface Constant { /** * 版本号 */ - public static final String VERSION = "0.7.6"; + public static final String VERSION = "0.7.7"; /** * 无效固件URL diff --git a/main/manager-mobile/src/pages/settings/index.vue b/main/manager-mobile/src/pages/settings/index.vue index 0cecd6bf..8b50f928 100644 --- a/main/manager-mobile/src/pages/settings/index.vue +++ b/main/manager-mobile/src/pages/settings/index.vue @@ -56,11 +56,11 @@ function getCacheInfo() { // 验证URL格式 function validateUrl() { urlError.value = '' - + if (!baseUrlInput.value) { return } - + if (!/^https?:\/\/.+\/xiaozhi$/.test(baseUrlInput.value)) { urlError.value = '请输入有效的服务端地址(以 http 或 https 开头,并以 /xiaozhi 结尾)' } @@ -70,7 +70,7 @@ function validateUrl() { async function testServerBaseUrl() { // 先清除错误信息 urlError.value = '' - + if (!baseUrlInput.value || !/^https?:\/\/.+\/xiaozhi$/.test(baseUrlInput.value)) { return false } @@ -113,20 +113,20 @@ async function saveServerBaseUrl() { clearAllCacheAfterUrlChange() uni.showModal({ - title: '重启应用', - content: '服务端地址已保存并清空缓存,是否立即重启生效?', - confirmText: '立即重启', - cancelText: '稍后', - success: (res) => { - if (res.confirm) { - restartApp() - } - else { - toast.success('已保存,可稍后手动重启应用') - } - }, - }) - } + title: '重启应用', + content: '服务端地址已保存并清空缓存,是否立即重启生效?', + confirmText: '立即重启', + cancelText: '稍后', + success: (res) => { + if (res.confirm) { + restartApp() + } + else { + toast.success('已保存,可稍后手动重启应用') + } + }, + }) +} // 重置为 env 默认 function resetServerBaseUrl() { @@ -222,7 +222,7 @@ function showAbout() { title: `关于${import.meta.env.VITE_APP_TITLE}`, content: `${import.meta.env.VITE_APP_TITLE}\n\n基于 Vue.js 3 + uni-app 构建的跨平台移动端管理应用,为小智ESP32智能硬件提供设备管理、智能体配置等功能。\n\n© 2025 xiaozhi-esp32-server`, title: `关于小智智控台`, - content: `小智智控台\n\n基于 Vue.js 3 + uni-app 构建的跨平台移动端管理应用,为小智ESP32智能硬件提供设备管理、智能体配置等功能。\n\n© 2025 xiaozhi-esp32-server 0.7.6`, + content: `小智智控台\n\n基于 Vue.js 3 + uni-app 构建的跨平台移动端管理应用,为小智ESP32智能硬件提供设备管理、智能体配置等功能。\n\n© 2025 xiaozhi-esp32-server 0.7.7`, showCancel: false, confirmText: '确定', }) @@ -263,17 +263,10 @@ onMounted(async () => { - + input-class="text-[28rpx] text-[#232338]" @input="validateUrl" @blur="validateUrl" /> {{ urlError }} @@ -371,7 +364,7 @@ onMounted(async () => { - + diff --git a/main/xiaozhi-server/config/logger.py b/main/xiaozhi-server/config/logger.py index 5a815f06..5c916ba5 100644 --- a/main/xiaozhi-server/config/logger.py +++ b/main/xiaozhi-server/config/logger.py @@ -5,7 +5,7 @@ from config.config_loader import load_config from config.settings import check_config_file from datetime import datetime -SERVER_VERSION = "0.7.6" +SERVER_VERSION = "0.7.7" _logger_initialized = False