mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	[micro_wake_word] Version 2 (#7032)
This commit is contained in:
		| @@ -9,7 +9,7 @@ import requests | |||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
|  |  | ||||||
| from esphome.core import CORE, HexInt, EsphomeError | from esphome.core import CORE, HexInt | ||||||
|  |  | ||||||
| from esphome.components import esp32, microphone | from esphome.components import esp32, microphone | ||||||
| from esphome import automation, git, external_files | from esphome import automation, git, external_files | ||||||
| @@ -41,9 +41,15 @@ CODEOWNERS = ["@kahrendt", "@jesserockz"] | |||||||
| DEPENDENCIES = ["microphone"] | DEPENDENCIES = ["microphone"] | ||||||
| DOMAIN = "micro_wake_word" | DOMAIN = "micro_wake_word" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONF_FEATURE_STEP_SIZE = "feature_step_size" | ||||||
|  | CONF_MODELS = "models" | ||||||
|  | CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected" | ||||||
| CONF_PROBABILITY_CUTOFF = "probability_cutoff" | CONF_PROBABILITY_CUTOFF = "probability_cutoff" | ||||||
| CONF_SLIDING_WINDOW_AVERAGE_SIZE = "sliding_window_average_size" | CONF_SLIDING_WINDOW_AVERAGE_SIZE = "sliding_window_average_size" | ||||||
| CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected" | CONF_SLIDING_WINDOW_SIZE = "sliding_window_size" | ||||||
|  | CONF_TENSOR_ARENA_SIZE = "tensor_arena_size" | ||||||
|  | CONF_VAD = "vad" | ||||||
|  |  | ||||||
| TYPE_HTTP = "http" | TYPE_HTTP = "http" | ||||||
|  |  | ||||||
| @@ -98,12 +104,14 @@ GIT_SCHEMA = cv.All( | |||||||
|     _process_git_source, |     _process_git_source, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| KEY_WAKE_WORD = "wake_word" |  | ||||||
| KEY_AUTHOR = "author" | KEY_AUTHOR = "author" | ||||||
| KEY_WEBSITE = "website" |  | ||||||
| KEY_VERSION = "version" |  | ||||||
| KEY_MICRO = "micro" | KEY_MICRO = "micro" | ||||||
| KEY_MINIMUM_ESPHOME_VERSION = "minimum_esphome_version" | KEY_MINIMUM_ESPHOME_VERSION = "minimum_esphome_version" | ||||||
|  | KEY_TRAINED_LANGUAGES = "trained_languages" | ||||||
|  | KEY_VERSION = "version" | ||||||
|  | KEY_WAKE_WORD = "wake_word" | ||||||
|  | KEY_WEBSITE = "website" | ||||||
|  |  | ||||||
| MANIFEST_SCHEMA_V1 = cv.Schema( | MANIFEST_SCHEMA_V1 = cv.Schema( | ||||||
|     { |     { | ||||||
| @@ -125,6 +133,29 @@ MANIFEST_SCHEMA_V1 = cv.Schema( | |||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | MANIFEST_SCHEMA_V2 = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.Required(CONF_TYPE): "micro", | ||||||
|  |         cv.Required(CONF_MODEL): cv.string, | ||||||
|  |         cv.Required(KEY_AUTHOR): cv.string, | ||||||
|  |         cv.Required(KEY_VERSION): cv.All(cv.int_, 2), | ||||||
|  |         cv.Required(KEY_WAKE_WORD): cv.string, | ||||||
|  |         cv.Required(KEY_TRAINED_LANGUAGES): cv.ensure_list(cv.string), | ||||||
|  |         cv.Optional(KEY_WEBSITE): cv.url, | ||||||
|  |         cv.Required(KEY_MICRO): cv.Schema( | ||||||
|  |             { | ||||||
|  |                 cv.Required(CONF_FEATURE_STEP_SIZE): cv.int_range(min=0, max=30), | ||||||
|  |                 cv.Required(CONF_TENSOR_ARENA_SIZE): cv.int_, | ||||||
|  |                 cv.Required(CONF_PROBABILITY_CUTOFF): cv.float_, | ||||||
|  |                 cv.Required(CONF_SLIDING_WINDOW_SIZE): cv.positive_int, | ||||||
|  |                 cv.Required(KEY_MINIMUM_ESPHOME_VERSION): cv.All( | ||||||
|  |                     cv.version_number, cv.validate_esphome_version | ||||||
|  |                 ), | ||||||
|  |             } | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def _compute_local_file_path(config: dict) -> Path: | def _compute_local_file_path(config: dict) -> Path: | ||||||
|     url = config[CONF_URL] |     url = config[CONF_URL] | ||||||
| @@ -135,6 +166,24 @@ def _compute_local_file_path(config: dict) -> Path: | |||||||
|     return base_dir / key |     return base_dir / key | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _convert_manifest_v1_to_v2(v1_manifest): | ||||||
|  |     v2_manifest = v1_manifest.copy() | ||||||
|  |  | ||||||
|  |     v2_manifest[KEY_VERSION] = 2 | ||||||
|  |     v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_SIZE] = v1_manifest[KEY_MICRO][ | ||||||
|  |         CONF_SLIDING_WINDOW_AVERAGE_SIZE | ||||||
|  |     ] | ||||||
|  |     del v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE] | ||||||
|  |     v2_manifest[KEY_MICRO][ | ||||||
|  |         CONF_TENSOR_ARENA_SIZE | ||||||
|  |     ] = 45672  # Original Inception-based V1 manifest models require a minimum of 45672 bytes | ||||||
|  |     v2_manifest[KEY_MICRO][ | ||||||
|  |         CONF_FEATURE_STEP_SIZE | ||||||
|  |     ] = 20  # Original Inception-based V1 manifest models use a 20 ms feature step size | ||||||
|  |  | ||||||
|  |     return v2_manifest | ||||||
|  |  | ||||||
|  |  | ||||||
| def _download_file(url: str, path: Path) -> bytes: | def _download_file(url: str, path: Path) -> bytes: | ||||||
|     if not external_files.has_remote_file_changed(url, path): |     if not external_files.has_remote_file_changed(url, path): | ||||||
|         _LOGGER.debug("Remote file has not changed, skipping download") |         _LOGGER.debug("Remote file has not changed, skipping download") | ||||||
| @@ -155,6 +204,24 @@ def _download_file(url: str, path: Path) -> bytes: | |||||||
|     return req.content |     return req.content | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _validate_manifest_version(manifest_data): | ||||||
|  |     if manifest_version := manifest_data.get(KEY_VERSION): | ||||||
|  |         if manifest_version == 1: | ||||||
|  |             try: | ||||||
|  |                 MANIFEST_SCHEMA_V1(manifest_data) | ||||||
|  |             except cv.Invalid as e: | ||||||
|  |                 raise cv.Invalid(f"Invalid manifest file: {e}") from e | ||||||
|  |         elif manifest_version == 2: | ||||||
|  |             try: | ||||||
|  |                 MANIFEST_SCHEMA_V2(manifest_data) | ||||||
|  |             except cv.Invalid as e: | ||||||
|  |                 raise cv.Invalid(f"Invalid manifest file: {e}") from e | ||||||
|  |         else: | ||||||
|  |             raise cv.Invalid("Invalid manifest version") | ||||||
|  |     else: | ||||||
|  |         raise cv.Invalid("Invalid manifest file, missing 'version' key.") | ||||||
|  |  | ||||||
|  |  | ||||||
| def _process_http_source(config): | def _process_http_source(config): | ||||||
|     url = config[CONF_URL] |     url = config[CONF_URL] | ||||||
|     path = _compute_local_file_path(config) |     path = _compute_local_file_path(config) | ||||||
| @@ -167,11 +234,6 @@ def _process_http_source(config): | |||||||
|     if not isinstance(manifest_data, dict): |     if not isinstance(manifest_data, dict): | ||||||
|         raise cv.Invalid("Manifest file must contain a JSON object") |         raise cv.Invalid("Manifest file must contain a JSON object") | ||||||
|  |  | ||||||
|     try: |  | ||||||
|         MANIFEST_SCHEMA_V1(manifest_data) |  | ||||||
|     except cv.Invalid as e: |  | ||||||
|         raise cv.Invalid(f"Invalid manifest file: {e}") from e |  | ||||||
|  |  | ||||||
|     model = manifest_data[CONF_MODEL] |     model = manifest_data[CONF_MODEL] | ||||||
|     model_url = urljoin(url, model) |     model_url = urljoin(url, model) | ||||||
|  |  | ||||||
| @@ -206,7 +268,7 @@ def _validate_source_model_name(value): | |||||||
|     return MODEL_SOURCE_SCHEMA( |     return MODEL_SOURCE_SCHEMA( | ||||||
|         { |         { | ||||||
|             CONF_TYPE: TYPE_HTTP, |             CONF_TYPE: TYPE_HTTP, | ||||||
|             CONF_URL: f"https://github.com/esphome/micro-wake-word-models/raw/main/models/{value}.json", |             CONF_URL: f"https://github.com/esphome/micro-wake-word-models/raw/main/models/v2/{value}.json", | ||||||
|         } |         } | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
| @@ -260,18 +322,55 @@ MODEL_SOURCE_SCHEMA = cv.Any( | |||||||
|     msg="Not a valid model name, local path, http(s) url, or github shorthand", |     msg="Not a valid model name, local path, http(s) url, or github shorthand", | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | MODEL_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.Optional(CONF_MODEL): MODEL_SOURCE_SCHEMA, | ||||||
|  |         cv.Optional(CONF_PROBABILITY_CUTOFF): cv.percentage, | ||||||
|  |         cv.Optional(CONF_SLIDING_WINDOW_SIZE): cv.positive_int, | ||||||
|  |         cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | # Provide a default VAD model that could be overridden | ||||||
|  | VAD_MODEL_SCHEMA = MODEL_SCHEMA.extend( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.Optional( | ||||||
|  |                 CONF_MODEL, | ||||||
|  |                 default="vad", | ||||||
|  |             ): MODEL_SOURCE_SCHEMA, | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _maybe_empty_vad_schema(value): | ||||||
|  |     # Idea borrowed from uart/__init__.py's ``maybe_empty_debug`` function. Accessed 2 July 2024. | ||||||
|  |     # Loads a default VAD model without any parameters overridden. | ||||||
|  |     if value is None: | ||||||
|  |         value = {} | ||||||
|  |     return VAD_MODEL_SCHEMA(value) | ||||||
|  |  | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All( | CONFIG_SCHEMA = cv.All( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.declare_id(MicroWakeWord), |             cv.GenerateID(): cv.declare_id(MicroWakeWord), | ||||||
|             cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), |             cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), | ||||||
|             cv.Optional(CONF_PROBABILITY_CUTOFF): cv.percentage, |             cv.Required(CONF_MODELS): cv.ensure_list(MODEL_SCHEMA), | ||||||
|             cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int, |  | ||||||
|             cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( |             cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( | ||||||
|                 single=True |                 single=True | ||||||
|             ), |             ), | ||||||
|             cv.Required(CONF_MODEL): MODEL_SOURCE_SCHEMA, |             cv.Optional(CONF_VAD): _maybe_empty_vad_schema, | ||||||
|             cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), |             cv.Optional(CONF_MODEL): cv.invalid( | ||||||
|  |                 f"The {CONF_MODEL} parameter has moved to be a list element under the {CONF_MODELS} parameter." | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_PROBABILITY_CUTOFF): cv.invalid( | ||||||
|  |                 f"The {CONF_PROBABILITY_CUTOFF} parameter has moved to be a list element under the {CONF_MODELS} parameter." | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.invalid( | ||||||
|  |                 f"The {CONF_SLIDING_WINDOW_AVERAGE_SIZE} parameter has been renamed to {CONF_SLIDING_WINDOW_SIZE} and moved to be a list element under the {CONF_MODELS} parameter." | ||||||
|  |             ), | ||||||
|         } |         } | ||||||
|     ).extend(cv.COMPONENT_SCHEMA), |     ).extend(cv.COMPONENT_SCHEMA), | ||||||
|     cv.only_with_esp_idf, |     cv.only_with_esp_idf, | ||||||
| @@ -282,45 +381,20 @@ def _load_model_data(manifest_path: Path): | |||||||
|     with open(manifest_path, encoding="utf-8") as f: |     with open(manifest_path, encoding="utf-8") as f: | ||||||
|         manifest = json.load(f) |         manifest = json.load(f) | ||||||
|  |  | ||||||
|     try: |     _validate_manifest_version(manifest) | ||||||
|         MANIFEST_SCHEMA_V1(manifest) |  | ||||||
|     except cv.Invalid as e: |  | ||||||
|         raise EsphomeError(f"Invalid manifest file: {e}") from e |  | ||||||
|  |  | ||||||
|     model_path = manifest_path.parent / manifest[CONF_MODEL] |     model_path = manifest_path.parent / manifest[CONF_MODEL] | ||||||
|  |  | ||||||
|     with open(model_path, "rb") as f: |     with open(model_path, "rb") as f: | ||||||
|         model = f.read() |         model = f.read() | ||||||
|  |  | ||||||
|  |     if manifest.get(KEY_VERSION) == 1: | ||||||
|  |         manifest = _convert_manifest_v1_to_v2(manifest) | ||||||
|  |  | ||||||
|     return manifest, model |     return manifest, model | ||||||
|  |  | ||||||
|  |  | ||||||
| async def to_code(config): | def _model_config_to_manifest_data(model_config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |  | ||||||
|     await cg.register_component(var, config) |  | ||||||
|  |  | ||||||
|     mic = await cg.get_variable(config[CONF_MICROPHONE]) |  | ||||||
|     cg.add(var.set_microphone(mic)) |  | ||||||
|  |  | ||||||
|     if on_wake_word_detection_config := config.get(CONF_ON_WAKE_WORD_DETECTED): |  | ||||||
|         await automation.build_automation( |  | ||||||
|             var.get_wake_word_detected_trigger(), |  | ||||||
|             [(cg.std_string, "wake_word")], |  | ||||||
|             on_wake_word_detection_config, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     esp32.add_idf_component( |  | ||||||
|         name="esp-tflite-micro", |  | ||||||
|         repo="https://github.com/espressif/esp-tflite-micro", |  | ||||||
|         ref="v1.3.1", |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") |  | ||||||
|     cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") |  | ||||||
|     cg.add_build_flag("-DESP_NN") |  | ||||||
|  |  | ||||||
|     model_config = config.get(CONF_MODEL) |  | ||||||
|     data = [] |  | ||||||
|     if model_config[CONF_TYPE] == TYPE_GIT: |     if model_config[CONF_TYPE] == TYPE_GIT: | ||||||
|         # compute path to model file |         # compute path to model file | ||||||
|         key = f"{model_config[CONF_URL]}@{model_config.get(CONF_REF)}" |         key = f"{model_config[CONF_URL]}@{model_config.get(CONF_REF)}" | ||||||
| @@ -338,23 +412,95 @@ async def to_code(config): | |||||||
|     else: |     else: | ||||||
|         raise ValueError("Unsupported config type: {model_config[CONF_TYPE]}") |         raise ValueError("Unsupported config type: {model_config[CONF_TYPE]}") | ||||||
|  |  | ||||||
|     manifest, data = _load_model_data(file) |     return _load_model_data(file) | ||||||
|  |  | ||||||
|     rhs = [HexInt(x) for x in data] |  | ||||||
|     prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) |  | ||||||
|     cg.add(var.set_model_start(prog_arr)) |  | ||||||
|  |  | ||||||
|     probability_cutoff = config.get( | def _feature_step_size_validate(config): | ||||||
|         CONF_PROBABILITY_CUTOFF, manifest[KEY_MICRO][CONF_PROBABILITY_CUTOFF] |     features_step_size = None | ||||||
|  |  | ||||||
|  |     for model_parameters in config[CONF_MODELS]: | ||||||
|  |         model_config = model_parameters.get(CONF_MODEL) | ||||||
|  |         manifest, _ = _model_config_to_manifest_data(model_config) | ||||||
|  |  | ||||||
|  |         model_step_size = manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE] | ||||||
|  |  | ||||||
|  |         if features_step_size is None: | ||||||
|  |             features_step_size = model_step_size | ||||||
|  |         elif features_step_size != model_step_size: | ||||||
|  |             raise cv.Invalid("Cannot load models with different features step sizes.") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | FINAL_VALIDATE_SCHEMA = _feature_step_size_validate | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |  | ||||||
|  |     mic = await cg.get_variable(config[CONF_MICROPHONE]) | ||||||
|  |     cg.add(var.set_microphone(mic)) | ||||||
|  |  | ||||||
|  |     esp32.add_idf_component( | ||||||
|  |         name="esp-tflite-micro", | ||||||
|  |         repo="https://github.com/espressif/esp-tflite-micro", | ||||||
|  |         ref="v1.3.1", | ||||||
|     ) |     ) | ||||||
|     cg.add(var.set_probability_cutoff(probability_cutoff)) |  | ||||||
|     sliding_window_average_size = config.get( |  | ||||||
|         CONF_SLIDING_WINDOW_AVERAGE_SIZE, |  | ||||||
|         manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE], |  | ||||||
|     ) |  | ||||||
|     cg.add(var.set_sliding_window_average_size(sliding_window_average_size)) |  | ||||||
|  |  | ||||||
|     cg.add(var.set_wake_word(manifest[KEY_WAKE_WORD])) |     cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") | ||||||
|  |     cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") | ||||||
|  |     cg.add_build_flag("-DESP_NN") | ||||||
|  |  | ||||||
|  |     if on_wake_word_detection_config := config.get(CONF_ON_WAKE_WORD_DETECTED): | ||||||
|  |         await automation.build_automation( | ||||||
|  |             var.get_wake_word_detected_trigger(), | ||||||
|  |             [(cg.std_string, "wake_word")], | ||||||
|  |             on_wake_word_detection_config, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     if vad_model := config.get(CONF_VAD): | ||||||
|  |         cg.add_define("USE_MICRO_WAKE_WORD_VAD") | ||||||
|  |  | ||||||
|  |         # Use the general model loading code for the VAD codegen | ||||||
|  |         config[CONF_MODELS].append(vad_model) | ||||||
|  |  | ||||||
|  |     for model_parameters in config[CONF_MODELS]: | ||||||
|  |         model_config = model_parameters.get(CONF_MODEL) | ||||||
|  |         data = [] | ||||||
|  |         manifest, data = _model_config_to_manifest_data(model_config) | ||||||
|  |  | ||||||
|  |         rhs = [HexInt(x) for x in data] | ||||||
|  |         prog_arr = cg.progmem_array(model_parameters[CONF_RAW_DATA_ID], rhs) | ||||||
|  |  | ||||||
|  |         probability_cutoff = model_parameters.get( | ||||||
|  |             CONF_PROBABILITY_CUTOFF, manifest[KEY_MICRO][CONF_PROBABILITY_CUTOFF] | ||||||
|  |         ) | ||||||
|  |         sliding_window_size = model_parameters.get( | ||||||
|  |             CONF_SLIDING_WINDOW_SIZE, | ||||||
|  |             manifest[KEY_MICRO][CONF_SLIDING_WINDOW_SIZE], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         if manifest[KEY_WAKE_WORD] == "vad": | ||||||
|  |             cg.add( | ||||||
|  |                 var.add_vad_model( | ||||||
|  |                     prog_arr, | ||||||
|  |                     probability_cutoff, | ||||||
|  |                     sliding_window_size, | ||||||
|  |                     manifest[KEY_MICRO][CONF_TENSOR_ARENA_SIZE], | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             cg.add( | ||||||
|  |                 var.add_wake_word_model( | ||||||
|  |                     prog_arr, | ||||||
|  |                     probability_cutoff, | ||||||
|  |                     sliding_window_size, | ||||||
|  |                     manifest[KEY_WAKE_WORD], | ||||||
|  |                     manifest[KEY_MICRO][CONF_TENSOR_ARENA_SIZE], | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_features_step_size(manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE])) | ||||||
|  |     cg.add_library("kahrendt/ESPMicroSpeechFeatures", "1.0.0") | ||||||
|  |  | ||||||
|  |  | ||||||
| MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)}) | MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)}) | ||||||
|   | |||||||
| @@ -1,493 +0,0 @@ | |||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF |  | ||||||
|  |  | ||||||
| // Converted audio_preprocessor_int8.tflite |  | ||||||
| // From https://github.com/tensorflow/tflite-micro/tree/main/tensorflow/lite/micro/examples/micro_speech/models accessed |  | ||||||
| // January 2024 |  | ||||||
| // |  | ||||||
| // Copyright 2023 The TensorFlow Authors. All Rights Reserved. |  | ||||||
| // |  | ||||||
| // Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
| // you may not use this file except in compliance with the License. |  | ||||||
| // You may obtain a copy of the License at |  | ||||||
| // |  | ||||||
| //     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| // |  | ||||||
| // Unless required by applicable law or agreed to in writing, software |  | ||||||
| // distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
| // See the License for the specific language governing permissions and |  | ||||||
| // limitations under the License. |  | ||||||
|  |  | ||||||
| namespace esphome { |  | ||||||
| namespace micro_wake_word { |  | ||||||
|  |  | ||||||
| const unsigned char G_AUDIO_PREPROCESSOR_INT8_TFLITE[] = { |  | ||||||
|     0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, |  | ||||||
|     0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, |  | ||||||
|     0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x03, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xeb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, |  | ||||||
|     0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, |  | ||||||
|     0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, |  | ||||||
|     0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, |  | ||||||
|     0x74, 0x5f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc2, 0xf5, 0xff, 0xff, |  | ||||||
|     0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, |  | ||||||
|     0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x2d, 0x00, |  | ||||||
|     0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, |  | ||||||
|     0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, |  | ||||||
|     0x08, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6d, 0x69, 0x6e, |  | ||||||
|     0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x2e, 0x00, |  | ||||||
|     0x00, 0x00, 0x9c, 0x0d, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0xc4, 0x09, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00, 0x48, |  | ||||||
|     0x09, 0x00, 0x00, 0x34, 0x09, 0x00, 0x00, 0x20, 0x09, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0xf8, 0x08, 0x00, 0x00, |  | ||||||
|     0xec, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00, 0xc0, 0x06, 0x00, 0x00, 0x38, 0x04, 0x00, |  | ||||||
|     0x00, 0xb0, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x60, 0x01, |  | ||||||
|     0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x2c, |  | ||||||
|     0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, |  | ||||||
|     0x04, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, |  | ||||||
|     0x00, 0xdc, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xbc, 0x00, |  | ||||||
|     0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, |  | ||||||
|     0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff, |  | ||||||
|     0x04, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, |  | ||||||
|     0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, |  | ||||||
|     0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, |  | ||||||
|     0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x32, 0x2e, 0x30, 0x00, |  | ||||||
|     0x00, 0x56, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x38, 0x2e, 0x30, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xe1, 0xff, 0xff, 0xd8, 0xe1, 0xff, 0xff, 0xdc, |  | ||||||
|     0xe1, 0xff, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe4, 0xe1, 0xff, 0xff, 0xe8, 0xe1, 0xff, 0xff, 0xec, 0xe1, 0xff, 0xff, |  | ||||||
|     0xf0, 0xe1, 0xff, 0xff, 0xf4, 0xe1, 0xff, 0xff, 0xf8, 0xe1, 0xff, 0xff, 0xfc, 0xe1, 0xff, 0xff, 0x00, 0xe2, 0xff, |  | ||||||
|     0xff, 0x04, 0xe2, 0xff, 0xff, 0x08, 0xe2, 0xff, 0xff, 0x0c, 0xe2, 0xff, 0xff, 0x10, 0xe2, 0xff, 0xff, 0x14, 0xe2, |  | ||||||
|     0xff, 0xff, 0x18, 0xe2, 0xff, 0xff, 0x1c, 0xe2, 0xff, 0xff, 0x20, 0xe2, 0xff, 0xff, 0x24, 0xe2, 0xff, 0xff, 0x28, |  | ||||||
|     0xe2, 0xff, 0xff, 0x2c, 0xe2, 0xff, 0xff, 0x30, 0xe2, 0xff, 0xff, 0xd2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, |  | ||||||
|     0x04, 0x00, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0xe2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x01, 0x00, 0x00, 0xf2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xff, |  | ||||||
|     0xff, 0xff, 0x02, 0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, |  | ||||||
|     0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x22, 0xf8, 0xff, 0xff, |  | ||||||
|     0x04, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x61, 0x05, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0b, 0x41, |  | ||||||
|     0x01, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0e, 0x80, 0x05, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0xd1, 0x0c, 0x63, 0x04, 0x00, 0x00, 0x00, 0x00, 0x34, 0x0c, 0x3f, 0x04, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x81, 0x0c, 0xf7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0d, 0x77, 0x06, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x0f, |  | ||||||
|     0xa9, 0x08, 0x01, 0x02, 0x7f, 0x0b, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0e, 0xd1, 0x08, 0xdb, 0x02, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x03, 0x0d, 0x4a, 0x07, 0xad, 0x01, 0x2c, 0x0c, 0xc6, 0x06, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x45, 0x0c, 0x29, 0x07, 0x23, 0x02, 0x34, 0x0d, 0x5b, 0x08, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x0e, 0x48, |  | ||||||
|     0x0a, 0xbd, 0x05, 0x45, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x0c, 0x88, 0x08, 0x43, 0x04, |  | ||||||
|     0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0b, 0xd3, 0x07, 0xcb, 0x03, 0xd2, 0x0f, 0xe7, |  | ||||||
|     0x0b, 0x09, 0x08, 0x39, 0x04, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x0c, 0x14, 0x09, |  | ||||||
|     0x75, 0x05, 0xe2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x0e, 0xdd, 0x0a, 0x6b, 0x07, 0x03, |  | ||||||
|     0x04, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x0d, 0x09, 0x0a, 0xc9, 0x06, 0x93, 0x03, 0x65, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0d, 0x25, 0x0a, 0x12, 0x07, 0x07, 0x04, 0x05, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x0a, 0x0e, 0x17, 0x0b, 0x2c, 0x08, 0x49, 0x05, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x98, 0x0f, 0xcb, 0x0c, 0x04, 0x0a, 0x44, 0x07, 0x8b, 0x04, 0xd8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x0f, 0x87, |  | ||||||
|     0x0c, 0xe7, 0x09, 0x4e, 0x07, 0xba, 0x04, 0x2d, 0x02, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x0f, 0x23, 0x0d, 0xa7, 0x0a, |  | ||||||
|     0x30, 0x08, 0xbe, 0x05, 0x52, 0x03, 0xeb, 0x00, 0x89, 0x0e, 0x2c, 0x0c, 0xd4, 0x09, 0x81, 0x07, 0x33, 0x05, 0xe9, |  | ||||||
|     0x02, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x29, 0x0c, 0xf1, 0x09, 0xbe, 0x07, 0x90, 0x05, 0x65, 0x03, |  | ||||||
|     0x3f, 0x01, 0x1d, 0x0f, 0xff, 0x0c, 0xe5, 0x0a, 0xcf, 0x08, 0xbc, 0x06, 0xae, 0x04, 0xa3, 0x02, 0x9c, 0x00, 0x99, |  | ||||||
|     0x0e, 0x99, 0x0c, 0x9d, 0x0a, 0xa4, 0x08, 0xaf, 0x06, 0xbd, 0x04, 0xcf, 0x02, 0xe4, 0x00, 0xfc, 0x0e, 0x17, 0x0d, |  | ||||||
|     0x36, 0x0b, 0x57, 0x09, 0x7c, 0x07, 0xa4, 0x05, 0xcf, 0x03, 0xfd, 0x01, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x62, 0x0e, 0x98, 0x0c, 0xd2, 0x0a, 0x0e, 0x09, 0x4d, 0x07, 0x8f, 0x05, 0xd4, 0x03, 0x1b, 0x02, |  | ||||||
|     0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x0e, 0x00, 0x0d, 0x52, 0x0b, 0xa6, 0x09, 0xfd, 0x07, 0x56, 0x06, 0xb1, |  | ||||||
|     0x04, 0x0f, 0x03, 0x6f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x0f, 0x37, 0x0e, 0x9e, 0x0c, |  | ||||||
|     0x08, 0x0b, 0x73, 0x09, 0xe1, 0x07, 0x52, 0x06, 0xc4, 0x04, 0x38, 0x03, 0xaf, 0x01, 0x28, 0x00, 0xa3, 0x0e, 0x1f, |  | ||||||
|     0x0d, 0x9e, 0x0b, 0x1f, 0x0a, 0xa2, 0x08, 0x27, 0x07, 0xae, 0x05, 0x37, 0x04, 0xc2, 0x02, 0x4e, 0x01, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0xdd, 0x0f, 0x6d, 0x0e, 0xff, 0x0c, 0x93, 0x0b, 0x29, 0x0a, 0xc1, 0x08, 0x5a, 0x07, 0xf5, 0x05, 0x92, |  | ||||||
|     0x04, 0x30, 0x03, 0xd1, 0x01, 0x73, 0x00, 0x16, 0x0f, 0xbc, 0x0d, 0x62, 0x0c, 0x0b, 0x0b, 0xb5, 0x09, 0x61, 0x08, |  | ||||||
|     0x0e, 0x07, 0xbd, 0x05, 0x6d, 0x04, 0x1f, 0x03, 0xd3, 0x01, 0x88, 0x00, 0x3e, 0x0f, 0xf6, 0x0d, 0xaf, 0x0c, 0x6a, |  | ||||||
|     0x0b, 0x27, 0x0a, 0xe4, 0x08, 0xa3, 0x07, 0x64, 0x06, 0x26, 0x05, 0xe9, 0x03, 0xae, 0x02, 0x74, 0x01, 0x3b, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0f, 0xce, 0x0d, 0x99, 0x0c, 0x66, 0x0b, 0x34, 0x0a, 0x03, |  | ||||||
|     0x09, 0xd3, 0x07, 0xa5, 0x06, 0x78, 0x05, 0x4c, 0x04, 0x22, 0x03, 0xf8, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0xa9, 0x0f, 0x83, 0x0e, 0x5f, 0x0d, 0x3b, 0x0c, 0x19, 0x0b, 0xf8, 0x09, 0xd8, 0x08, 0xb9, 0x07, 0x9b, 0x06, 0x7e, |  | ||||||
|     0x05, 0x63, 0x04, 0x48, 0x03, 0x2f, 0x02, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfa, 0xff, 0xff, 0x04, 0x00, |  | ||||||
|     0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x04, 0xbe, 0x0e, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x01, 0x7f, 0x0a, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x2e, 0x03, 0x9c, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x03, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x7e, |  | ||||||
|     0x03, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x56, 0x07, |  | ||||||
|     0xfe, 0x0d, 0x80, 0x04, 0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x16, 0x01, 0x2e, 0x07, 0x24, 0x0d, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0xfc, 0x02, 0xb5, 0x08, 0x52, 0x0e, 0xd3, 0x03, 0x39, 0x09, 0x86, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xba, 0x03, |  | ||||||
|     0xd6, 0x08, 0xdc, 0x0d, 0xcb, 0x02, 0xa4, 0x07, 0x69, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0xb7, 0x05, 0x42, |  | ||||||
|     0x0a, 0xba, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x03, 0x77, 0x07, 0xbc, 0x0b, 0xf1, 0x0f, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x04, 0x2c, 0x08, 0x34, 0x0c, 0x2d, 0x00, 0x18, 0x04, 0xf6, |  | ||||||
|     0x07, 0xc6, 0x0b, 0x89, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0xeb, 0x06, 0x8a, 0x0a, |  | ||||||
|     0x1d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x01, 0x22, 0x05, 0x94, 0x08, 0xfc, 0x0b, 0x59, |  | ||||||
|     0x0f, 0x00, 0x00, 0x00, 0x00, 0xac, 0x02, 0xf6, 0x05, 0x36, 0x09, 0x6c, 0x0c, 0x9a, 0x0f, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0xbe, 0x02, 0xda, 0x05, 0xed, 0x08, 0xf8, 0x0b, 0xfa, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xf5, |  | ||||||
|     0x01, 0xe8, 0x04, 0xd3, 0x07, 0xb6, 0x0a, 0x92, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, |  | ||||||
|     0x34, 0x03, 0xfb, 0x05, 0xbb, 0x08, 0x74, 0x0b, 0x27, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x78, 0x03, 0x18, |  | ||||||
|     0x06, 0xb1, 0x08, 0x45, 0x0b, 0xd2, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0xdc, 0x02, 0x58, 0x05, 0xcf, 0x07, |  | ||||||
|     0x41, 0x0a, 0xad, 0x0c, 0x14, 0x0f, 0x76, 0x01, 0xd3, 0x03, 0x2b, 0x06, 0x7e, 0x08, 0xcc, 0x0a, 0x16, 0x0d, 0x5a, |  | ||||||
|     0x0f, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x01, 0xd6, 0x03, 0x0e, 0x06, 0x41, 0x08, 0x6f, 0x0a, 0x9a, 0x0c, 0xc0, 0x0e, |  | ||||||
|     0xe2, 0x00, 0x00, 0x03, 0x1a, 0x05, 0x30, 0x07, 0x43, 0x09, 0x51, 0x0b, 0x5c, 0x0d, 0x63, 0x0f, 0x66, 0x01, 0x66, |  | ||||||
|     0x03, 0x62, 0x05, 0x5b, 0x07, 0x50, 0x09, 0x42, 0x0b, 0x30, 0x0d, 0x1b, 0x0f, 0x03, 0x01, 0xe8, 0x02, 0xc9, 0x04, |  | ||||||
|     0xa8, 0x06, 0x83, 0x08, 0x5b, 0x0a, 0x30, 0x0c, 0x02, 0x0e, 0xd1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x9d, 0x01, 0x67, 0x03, 0x2d, 0x05, 0xf1, 0x06, 0xb2, 0x08, 0x70, 0x0a, 0x2b, 0x0c, 0xe4, 0x0d, 0x9a, 0x0f, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x4e, 0x01, 0xff, 0x02, 0xad, 0x04, 0x59, 0x06, 0x02, 0x08, 0xa9, 0x09, 0x4e, 0x0b, 0xf0, |  | ||||||
|     0x0c, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xc8, 0x01, 0x61, 0x03, 0xf7, 0x04, |  | ||||||
|     0x8c, 0x06, 0x1e, 0x08, 0xad, 0x09, 0x3b, 0x0b, 0xc7, 0x0c, 0x50, 0x0e, 0xd7, 0x0f, 0x5c, 0x01, 0xe0, 0x02, 0x61, |  | ||||||
|     0x04, 0xe0, 0x05, 0x5d, 0x07, 0xd8, 0x08, 0x51, 0x0a, 0xc8, 0x0b, 0x3d, 0x0d, 0xb1, 0x0e, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x22, 0x00, 0x92, 0x01, 0x00, 0x03, 0x6c, 0x04, 0xd6, 0x05, 0x3e, 0x07, 0xa5, 0x08, 0x0a, 0x0a, 0x6d, 0x0b, 0xcf, |  | ||||||
|     0x0c, 0x2e, 0x0e, 0x8c, 0x0f, 0xe9, 0x00, 0x43, 0x02, 0x9d, 0x03, 0xf4, 0x04, 0x4a, 0x06, 0x9e, 0x07, 0xf1, 0x08, |  | ||||||
|     0x42, 0x0a, 0x92, 0x0b, 0xe0, 0x0c, 0x2c, 0x0e, 0x77, 0x0f, 0xc1, 0x00, 0x09, 0x02, 0x50, 0x03, 0x95, 0x04, 0xd8, |  | ||||||
|     0x05, 0x1b, 0x07, 0x5c, 0x08, 0x9b, 0x09, 0xd9, 0x0a, 0x16, 0x0c, 0x51, 0x0d, 0x8b, 0x0e, 0xc4, 0x0f, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x31, 0x02, 0x66, 0x03, 0x99, 0x04, 0xcb, 0x05, 0xfc, 0x06, 0x2c, |  | ||||||
|     0x08, 0x5a, 0x09, 0x87, 0x0a, 0xb3, 0x0b, 0xdd, 0x0c, 0x07, 0x0e, 0x2f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, |  | ||||||
|     0x7c, 0x01, 0xa0, 0x02, 0xc4, 0x03, 0xe6, 0x04, 0x07, 0x06, 0x27, 0x07, 0x46, 0x08, 0x64, 0x09, 0x81, 0x0a, 0x9c, |  | ||||||
|     0x0b, 0xb7, 0x0c, 0xd0, 0x0d, 0xe8, 0x0e, 0x00, 0x10, 0x00, 0x00, 0x2a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, |  | ||||||
|     0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, |  | ||||||
|     0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x24, 0x00, 0x26, 0x00, 0x2a, 0x00, |  | ||||||
|     0x2e, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x5a, |  | ||||||
|     0x00, 0x62, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, 0x92, 0x00, 0x9a, 0x00, 0xa6, 0x00, |  | ||||||
|     0xb0, 0x00, 0xbc, 0x00, 0xc8, 0x00, 0xd4, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x8a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, |  | ||||||
|     0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, |  | ||||||
|     0x1c, 0x00, 0x20, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x30, 0x00, 0x34, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x44, |  | ||||||
|     0x00, 0x4c, 0x00, 0x50, 0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, |  | ||||||
|     0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xc4, 0x00, 0xd0, 0x00, 0xdc, 0x00, 0xe8, |  | ||||||
|     0x00, 0xf4, 0x00, 0x00, 0x01, 0x0c, 0x01, 0x1c, 0x01, 0x2c, 0x01, 0x00, 0x00, 0xea, 0xfd, 0xff, 0xff, 0x04, 0x00, |  | ||||||
|     0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, |  | ||||||
|     0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, |  | ||||||
|     0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, |  | ||||||
|     0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, |  | ||||||
|     0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4a, 0xfe, 0xff, 0xff, 0x04, |  | ||||||
|     0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x7c, 0x7f, 0x79, 0x7f, 0x76, 0x7f, 0xfa, 0xff, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x70, 0x7f, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7f, 0xe9, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x4b, 0x7f, 0xd0, |  | ||||||
|     0xff, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x7f, 0xa0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x7e, 0x42, 0xff, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0xfd, 0x7d, 0x86, 0xfe, 0x04, 0x00, 0x00, 0x00, 0x87, 0x7c, 0x1d, 0xfd, 0x12, 0x00, 0x00, 0x00, 0xb6, |  | ||||||
|     0x79, 0x7f, 0xfa, 0x3e, 0x00, 0x00, 0x00, 0x73, 0x74, 0xf9, 0xf5, 0xca, 0x00, 0x00, 0x00, 0x36, 0x6b, 0x33, 0xef, |  | ||||||
|     0x32, 0x02, 0x00, 0x00, 0x9b, 0x5c, 0x87, 0xe7, 0xce, 0x04, 0x00, 0x00, 0xf0, 0x48, 0xde, 0xe2, 0xa0, 0x07, 0x00, |  | ||||||
|     0x00, 0x6e, 0x33, 0x8a, 0xe4, 0xa4, 0x08, 0x00, 0x00, 0x9c, 0x20, 0x22, 0xeb, 0x4c, 0x07, 0x00, 0x00, 0x0a, 0x13, |  | ||||||
|     0x7d, 0xf2, 0x02, 0x05, 0x00, 0x00, 0x89, 0x0a, 0x17, 0xf8, 0x06, 0x03, 0x00, 0x00, 0xa6, 0x05, 0xa0, 0xfb, 0xb4, |  | ||||||
|     0x01, 0x00, 0x00, 0xfa, 0x02, 0xac, 0xfd, 0xe8, 0x00, 0x00, 0x00, 0x8e, 0x01, 0xc7, 0xfe, 0x7a, 0x00, 0x00, 0x00, |  | ||||||
|     0xcf, 0x00, 0x5c, 0xff, 0x40, 0x00, 0x00, 0x00, 0x6b, 0x00, 0xab, 0xff, 0x22, 0x00, 0x00, 0x00, 0x38, 0x00, 0xd3, |  | ||||||
|     0xff, 0x12, 0x00, 0x00, 0x00, 0x1d, 0x00, 0xea, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xf3, 0xff, 0x06, 0x00, |  | ||||||
|     0x00, 0x00, 0x08, 0x00, 0xf8, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, |  | ||||||
|     0x00, 0xfd, 0xff, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfd, 0xff, |  | ||||||
|     0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, |  | ||||||
|     0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, |  | ||||||
|     0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, |  | ||||||
|     0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4d, 0x01, 0x00, 0x00, |  | ||||||
|     0x92, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xff, 0xff, 0xff, 0x04, 0x00, |  | ||||||
|     0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, |  | ||||||
|     0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, |  | ||||||
|     0x00, 0x02, 0x00, 0x04, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x13, 0x00, 0x17, 0x00, |  | ||||||
|     0x1b, 0x00, 0x20, 0x00, 0x25, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x49, 0x00, 0x51, |  | ||||||
|     0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8d, 0x00, 0x97, 0x00, 0xa1, 0x00, |  | ||||||
|     0xac, 0x00, 0xb7, 0x00, 0xc2, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xe5, 0x00, 0xf2, 0x00, 0xff, 0x00, 0x0c, 0x01, 0x19, |  | ||||||
|     0x01, 0x27, 0x01, 0x35, 0x01, 0x43, 0x01, 0x52, 0x01, 0x61, 0x01, 0x70, 0x01, 0x7f, 0x01, 0x8f, 0x01, 0x9f, 0x01, |  | ||||||
|     0xaf, 0x01, 0xc0, 0x01, 0xd1, 0x01, 0xe2, 0x01, 0xf3, 0x01, 0x05, 0x02, 0x17, 0x02, 0x29, 0x02, 0x3c, 0x02, 0x4e, |  | ||||||
|     0x02, 0x61, 0x02, 0x75, 0x02, 0x88, 0x02, 0x9c, 0x02, 0xb0, 0x02, 0xc4, 0x02, 0xd8, 0x02, 0xed, 0x02, 0x02, 0x03, |  | ||||||
|     0x17, 0x03, 0x2c, 0x03, 0x41, 0x03, 0x57, 0x03, 0x6d, 0x03, 0x83, 0x03, 0x99, 0x03, 0xb0, 0x03, 0xc7, 0x03, 0xdd, |  | ||||||
|     0x03, 0xf4, 0x03, 0x0c, 0x04, 0x23, 0x04, 0x3b, 0x04, 0x52, 0x04, 0x6a, 0x04, 0x82, 0x04, 0x9a, 0x04, 0xb3, 0x04, |  | ||||||
|     0xcb, 0x04, 0xe4, 0x04, 0xfd, 0x04, 0x16, 0x05, 0x2f, 0x05, 0x48, 0x05, 0x61, 0x05, 0x7a, 0x05, 0x94, 0x05, 0xad, |  | ||||||
|     0x05, 0xc7, 0x05, 0xe1, 0x05, 0xfb, 0x05, 0x15, 0x06, 0x2f, 0x06, 0x49, 0x06, 0x63, 0x06, 0x7e, 0x06, 0x98, 0x06, |  | ||||||
|     0xb2, 0x06, 0xcd, 0x06, 0xe7, 0x06, 0x02, 0x07, 0x1d, 0x07, 0x37, 0x07, 0x52, 0x07, 0x6d, 0x07, 0x87, 0x07, 0xa2, |  | ||||||
|     0x07, 0xbd, 0x07, 0xd8, 0x07, 0xf3, 0x07, 0x0d, 0x08, 0x28, 0x08, 0x43, 0x08, 0x5e, 0x08, 0x79, 0x08, 0x93, 0x08, |  | ||||||
|     0xae, 0x08, 0xc9, 0x08, 0xe3, 0x08, 0xfe, 0x08, 0x19, 0x09, 0x33, 0x09, 0x4e, 0x09, 0x68, 0x09, 0x82, 0x09, 0x9d, |  | ||||||
|     0x09, 0xb7, 0x09, 0xd1, 0x09, 0xeb, 0x09, 0x05, 0x0a, 0x1f, 0x0a, 0x39, 0x0a, 0x53, 0x0a, 0x6c, 0x0a, 0x86, 0x0a, |  | ||||||
|     0x9f, 0x0a, 0xb8, 0x0a, 0xd1, 0x0a, 0xea, 0x0a, 0x03, 0x0b, 0x1c, 0x0b, 0x35, 0x0b, 0x4d, 0x0b, 0x66, 0x0b, 0x7e, |  | ||||||
|     0x0b, 0x96, 0x0b, 0xae, 0x0b, 0xc5, 0x0b, 0xdd, 0x0b, 0xf4, 0x0b, 0x0c, 0x0c, 0x23, 0x0c, 0x39, 0x0c, 0x50, 0x0c, |  | ||||||
|     0x67, 0x0c, 0x7d, 0x0c, 0x93, 0x0c, 0xa9, 0x0c, 0xbf, 0x0c, 0xd4, 0x0c, 0xe9, 0x0c, 0xfe, 0x0c, 0x13, 0x0d, 0x28, |  | ||||||
|     0x0d, 0x3c, 0x0d, 0x50, 0x0d, 0x64, 0x0d, 0x78, 0x0d, 0x8b, 0x0d, 0x9f, 0x0d, 0xb2, 0x0d, 0xc4, 0x0d, 0xd7, 0x0d, |  | ||||||
|     0xe9, 0x0d, 0xfb, 0x0d, 0x0d, 0x0e, 0x1e, 0x0e, 0x2f, 0x0e, 0x40, 0x0e, 0x51, 0x0e, 0x61, 0x0e, 0x71, 0x0e, 0x81, |  | ||||||
|     0x0e, 0x90, 0x0e, 0x9f, 0x0e, 0xae, 0x0e, 0xbd, 0x0e, 0xcb, 0x0e, 0xd9, 0x0e, 0xe7, 0x0e, 0xf4, 0x0e, 0x01, 0x0f, |  | ||||||
|     0x0e, 0x0f, 0x1b, 0x0f, 0x27, 0x0f, 0x33, 0x0f, 0x3e, 0x0f, 0x49, 0x0f, 0x54, 0x0f, 0x5f, 0x0f, 0x69, 0x0f, 0x73, |  | ||||||
|     0x0f, 0x7d, 0x0f, 0x86, 0x0f, 0x8f, 0x0f, 0x98, 0x0f, 0xa0, 0x0f, 0xa8, 0x0f, 0xaf, 0x0f, 0xb7, 0x0f, 0xbe, 0x0f, |  | ||||||
|     0xc4, 0x0f, 0xcb, 0x0f, 0xd0, 0x0f, 0xd6, 0x0f, 0xdb, 0x0f, 0xe0, 0x0f, 0xe5, 0x0f, 0xe9, 0x0f, 0xed, 0x0f, 0xf0, |  | ||||||
|     0x0f, 0xf3, 0x0f, 0xf6, 0x0f, 0xf9, 0x0f, 0xfb, 0x0f, 0xfc, 0x0f, 0xfe, 0x0f, 0xff, 0x0f, 0x00, 0x10, 0x00, 0x10, |  | ||||||
|     0x00, 0x10, 0x00, 0x10, 0xff, 0x0f, 0xfe, 0x0f, 0xfc, 0x0f, 0xfb, 0x0f, 0xf9, 0x0f, 0xf6, 0x0f, 0xf3, 0x0f, 0xf0, |  | ||||||
|     0x0f, 0xed, 0x0f, 0xe9, 0x0f, 0xe5, 0x0f, 0xe0, 0x0f, 0xdb, 0x0f, 0xd6, 0x0f, 0xd0, 0x0f, 0xcb, 0x0f, 0xc4, 0x0f, |  | ||||||
|     0xbe, 0x0f, 0xb7, 0x0f, 0xaf, 0x0f, 0xa8, 0x0f, 0xa0, 0x0f, 0x98, 0x0f, 0x8f, 0x0f, 0x86, 0x0f, 0x7d, 0x0f, 0x73, |  | ||||||
|     0x0f, 0x69, 0x0f, 0x5f, 0x0f, 0x54, 0x0f, 0x49, 0x0f, 0x3e, 0x0f, 0x33, 0x0f, 0x27, 0x0f, 0x1b, 0x0f, 0x0e, 0x0f, |  | ||||||
|     0x01, 0x0f, 0xf4, 0x0e, 0xe7, 0x0e, 0xd9, 0x0e, 0xcb, 0x0e, 0xbd, 0x0e, 0xae, 0x0e, 0x9f, 0x0e, 0x90, 0x0e, 0x81, |  | ||||||
|     0x0e, 0x71, 0x0e, 0x61, 0x0e, 0x51, 0x0e, 0x40, 0x0e, 0x2f, 0x0e, 0x1e, 0x0e, 0x0d, 0x0e, 0xfb, 0x0d, 0xe9, 0x0d, |  | ||||||
|     0xd7, 0x0d, 0xc4, 0x0d, 0xb2, 0x0d, 0x9f, 0x0d, 0x8b, 0x0d, 0x78, 0x0d, 0x64, 0x0d, 0x50, 0x0d, 0x3c, 0x0d, 0x28, |  | ||||||
|     0x0d, 0x13, 0x0d, 0xfe, 0x0c, 0xe9, 0x0c, 0xd4, 0x0c, 0xbf, 0x0c, 0xa9, 0x0c, 0x93, 0x0c, 0x7d, 0x0c, 0x67, 0x0c, |  | ||||||
|     0x50, 0x0c, 0x39, 0x0c, 0x23, 0x0c, 0x0c, 0x0c, 0xf4, 0x0b, 0xdd, 0x0b, 0xc5, 0x0b, 0xae, 0x0b, 0x96, 0x0b, 0x7e, |  | ||||||
|     0x0b, 0x66, 0x0b, 0x4d, 0x0b, 0x35, 0x0b, 0x1c, 0x0b, 0x03, 0x0b, 0xea, 0x0a, 0xd1, 0x0a, 0xb8, 0x0a, 0x9f, 0x0a, |  | ||||||
|     0x86, 0x0a, 0x6c, 0x0a, 0x53, 0x0a, 0x39, 0x0a, 0x1f, 0x0a, 0x05, 0x0a, 0xeb, 0x09, 0xd1, 0x09, 0xb7, 0x09, 0x9d, |  | ||||||
|     0x09, 0x82, 0x09, 0x68, 0x09, 0x4e, 0x09, 0x33, 0x09, 0x19, 0x09, 0xfe, 0x08, 0xe3, 0x08, 0xc9, 0x08, 0xae, 0x08, |  | ||||||
|     0x93, 0x08, 0x79, 0x08, 0x5e, 0x08, 0x43, 0x08, 0x28, 0x08, 0x0d, 0x08, 0xf3, 0x07, 0xd8, 0x07, 0xbd, 0x07, 0xa2, |  | ||||||
|     0x07, 0x87, 0x07, 0x6d, 0x07, 0x52, 0x07, 0x37, 0x07, 0x1d, 0x07, 0x02, 0x07, 0xe7, 0x06, 0xcd, 0x06, 0xb2, 0x06, |  | ||||||
|     0x98, 0x06, 0x7e, 0x06, 0x63, 0x06, 0x49, 0x06, 0x2f, 0x06, 0x15, 0x06, 0xfb, 0x05, 0xe1, 0x05, 0xc7, 0x05, 0xad, |  | ||||||
|     0x05, 0x94, 0x05, 0x7a, 0x05, 0x61, 0x05, 0x48, 0x05, 0x2f, 0x05, 0x16, 0x05, 0xfd, 0x04, 0xe4, 0x04, 0xcb, 0x04, |  | ||||||
|     0xb3, 0x04, 0x9a, 0x04, 0x82, 0x04, 0x6a, 0x04, 0x52, 0x04, 0x3b, 0x04, 0x23, 0x04, 0x0c, 0x04, 0xf4, 0x03, 0xdd, |  | ||||||
|     0x03, 0xc7, 0x03, 0xb0, 0x03, 0x99, 0x03, 0x83, 0x03, 0x6d, 0x03, 0x57, 0x03, 0x41, 0x03, 0x2c, 0x03, 0x17, 0x03, |  | ||||||
|     0x02, 0x03, 0xed, 0x02, 0xd8, 0x02, 0xc4, 0x02, 0xb0, 0x02, 0x9c, 0x02, 0x88, 0x02, 0x75, 0x02, 0x61, 0x02, 0x4e, |  | ||||||
|     0x02, 0x3c, 0x02, 0x29, 0x02, 0x17, 0x02, 0x05, 0x02, 0xf3, 0x01, 0xe2, 0x01, 0xd1, 0x01, 0xc0, 0x01, 0xaf, 0x01, |  | ||||||
|     0x9f, 0x01, 0x8f, 0x01, 0x7f, 0x01, 0x70, 0x01, 0x61, 0x01, 0x52, 0x01, 0x43, 0x01, 0x35, 0x01, 0x27, 0x01, 0x19, |  | ||||||
|     0x01, 0x0c, 0x01, 0xff, 0x00, 0xf2, 0x00, 0xe5, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xac, 0x00, |  | ||||||
|     0xa1, 0x00, 0x97, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x68, 0x00, 0x60, 0x00, 0x58, 0x00, 0x51, |  | ||||||
|     0x00, 0x49, 0x00, 0x42, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2a, 0x00, 0x25, 0x00, 0x20, 0x00, 0x1b, 0x00, |  | ||||||
|     0x17, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xee, 0xff, 0xff, 0x38, 0xee, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x4d, 0x4c, |  | ||||||
|     0x49, 0x52, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, |  | ||||||
|     0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xf4, 0x05, 0x00, 0x00, 0xf8, 0x05, 0x00, |  | ||||||
|     0x00, 0xfc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, |  | ||||||
|     0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, 0xc8, 0x04, 0x00, 0x00, 0x70, |  | ||||||
|     0x04, 0x00, 0x00, 0x4c, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x00, |  | ||||||
|     0x4c, 0x03, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, |  | ||||||
|     0x00, 0x48, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x78, 0x00, |  | ||||||
|     0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf6, 0xfa, 0xff, 0xff, 0x0c, |  | ||||||
|     0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, |  | ||||||
|     0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x16, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, |  | ||||||
|     0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, |  | ||||||
|     0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3a, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, |  | ||||||
|     0x0e, 0x00, 0x00, 0x00, 0xa6, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, 0x00, |  | ||||||
|     0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x68, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, |  | ||||||
|     0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd6, 0xfc, 0xff, 0xff, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, |  | ||||||
|     0x98, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, |  | ||||||
|     0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, |  | ||||||
|     0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xc8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x25, |  | ||||||
|     0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0xfd, 0xff, 0xff, |  | ||||||
|     0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, |  | ||||||
|     0x00, 0xf8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, |  | ||||||
|     0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, |  | ||||||
|     0x84, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, |  | ||||||
|     0x00, 0x30, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69, |  | ||||||
|     0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6c, |  | ||||||
|     0x65, 0x00, 0x02, 0x24, 0x0f, 0x02, 0x01, 0x02, 0x03, 0x40, 0x04, 0x04, 0x04, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xdc, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, |  | ||||||
|     0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x73, 0x6e, |  | ||||||
|     0x72, 0x5f, 0x73, 0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x01, 0x06, 0x04, 0x02, 0x24, 0x01, 0x01, |  | ||||||
|     0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, |  | ||||||
|     0x08, 0x00, 0x00, 0x00, 0x20, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, |  | ||||||
|     0x00, 0x0a, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, |  | ||||||
|     0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, |  | ||||||
|     0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, |  | ||||||
|     0x67, 0x00, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, |  | ||||||
|     0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, |  | ||||||
|     0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, |  | ||||||
|     0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, |  | ||||||
|     0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x73, 0x70, 0x65, 0x63, 0x74, |  | ||||||
|     0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, |  | ||||||
|     0x73, 0x00, 0x09, 0xa5, 0x88, 0x75, 0x6d, 0x59, 0x4d, 0x3a, 0x31, 0x23, 0x09, 0x00, 0x01, 0x00, 0x09, 0x00, 0x29, |  | ||||||
|     0x3c, 0xd7, 0x03, 0x00, 0x00, 0x33, 0x03, 0x28, 0x00, 0x67, 0x3e, 0x99, 0x01, 0x0a, 0x00, 0x0e, 0x00, 0x05, 0x05, |  | ||||||
|     0x69, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1b, 0x25, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, |  | ||||||
|     0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0xfe, 0xff, 0xff, 0x10, 0x00, |  | ||||||
|     0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, |  | ||||||
|     0x1d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x54, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, |  | ||||||
|     0x00, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, |  | ||||||
|     0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x28, 0x04, 0x02, 0x24, 0x01, 0x00, 0x01, |  | ||||||
|     0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, |  | ||||||
|     0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0xfe, 0xff, |  | ||||||
|     0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, |  | ||||||
|     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8c, 0xf2, 0xff, 0xff, |  | ||||||
|     0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, |  | ||||||
|     0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x00, |  | ||||||
|     0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, |  | ||||||
|     0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, |  | ||||||
|     0x00, 0xfe, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, |  | ||||||
|     0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, 0x10, |  | ||||||
|     0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, |  | ||||||
|     0x65, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x64, |  | ||||||
|     0x65, 0x78, 0x00, 0x02, 0x17, 0x0e, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf1, 0x00, 0x05, 0x00, 0x05, 0x05, |  | ||||||
|     0x06, 0x25, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, |  | ||||||
|     0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, |  | ||||||
|     0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x54, 0x00, 0x66, 0x66, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, |  | ||||||
|     0x68, 0x00, 0x02, 0x0e, 0x0d, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x02, 0x05, 0x05, 0x06, 0x25, |  | ||||||
|     0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10, |  | ||||||
|     0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, |  | ||||||
|     0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, |  | ||||||
|     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, |  | ||||||
|     0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, |  | ||||||
|     0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, |  | ||||||
|     0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73, |  | ||||||
|     0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x07, 0x01, 0x01, 0x01, 0x0c, 0x04, 0x02, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, |  | ||||||
|     0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xc4, 0x0a, |  | ||||||
|     0x00, 0x00, 0x74, 0x0a, 0x00, 0x00, 0x3c, 0x0a, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x88, |  | ||||||
|     0x09, 0x00, 0x00, 0x40, 0x09, 0x00, 0x00, 0xfc, 0x08, 0x00, 0x00, 0xb8, 0x08, 0x00, 0x00, 0x6c, 0x08, 0x00, 0x00, |  | ||||||
|     0x20, 0x08, 0x00, 0x00, 0xd4, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x3c, 0x07, 0x00, 0x00, 0xf8, 0x06, 0x00, |  | ||||||
|     0x00, 0xb8, 0x06, 0x00, 0x00, 0x84, 0x06, 0x00, 0x00, 0x50, 0x06, 0x00, 0x00, 0x1c, 0x06, 0x00, 0x00, 0xd8, 0x05, |  | ||||||
|     0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x14, 0x05, 0x00, 0x00, 0xd8, 0x04, 0x00, 0x00, 0x98, |  | ||||||
|     0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, |  | ||||||
|     0x6c, 0x03, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, |  | ||||||
|     0x00, 0xe4, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x08, 0x01, |  | ||||||
|     0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, |  | ||||||
|     0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x44, 0xf5, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, |  | ||||||
|     0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, |  | ||||||
|     0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x84, 0xf5, 0xff, 0xff, |  | ||||||
|     0x0d, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00, |  | ||||||
|     0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x7a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, |  | ||||||
|     0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0xc0, |  | ||||||
|     0xf5, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, |  | ||||||
|     0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, |  | ||||||
|     0x00, 0xbe, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x04, 0xf6, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x61, |  | ||||||
|     0x64, 0x64, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x02, 0x18, 0x00, 0x00, 0x00, 0x38, 0xf6, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, |  | ||||||
|     0x74, 0x65, 0x44, 0x69, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2a, 0xf7, 0xff, 0xff, 0x00, |  | ||||||
|     0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |  | ||||||
|     0x10, 0x00, 0x00, 0x00, 0x70, 0xf6, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x61, 0x64, 0x64, 0x00, 0x01, 0x00, 0x00, |  | ||||||
|     0x00, 0x28, 0x00, 0x00, 0x00, 0x5a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, |  | ||||||
|     0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0xa0, 0xf6, 0xff, 0xff, 0x03, |  | ||||||
|     0x00, 0x00, 0x00, 0x6d, 0x75, 0x6c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x8a, 0xf7, 0xff, 0xff, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x02, 0x14, 0x00, 0x00, 0x00, 0xd0, 0xf6, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x32, |  | ||||||
|     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xbe, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, |  | ||||||
|     0x04, 0xf7, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, |  | ||||||
|     0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, |  | ||||||
|     0x00, 0x00, 0x02, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x22, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x48, 0xf7, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, |  | ||||||
|     0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, |  | ||||||
|     0x00, 0x3a, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x00, 0x00, 0x00, 0x80, 0xf7, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x73, |  | ||||||
|     0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, |  | ||||||
|     0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, |  | ||||||
|     0x31, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x92, 0xf8, 0xff, 0xff, 0x00, 0x00, |  | ||||||
|     0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34, |  | ||||||
|     0x00, 0x00, 0x00, 0xd8, 0xf7, 0xff, 0xff, 0x27, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, |  | ||||||
|     0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, |  | ||||||
|     0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, |  | ||||||
|     0x00, 0x00, 0xe6, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1f, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0xf8, 0xff, 0xff, 0x1e, 0x00, 0x00, 0x00, |  | ||||||
|     0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, |  | ||||||
|     0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, |  | ||||||
|     0x00, 0x00, 0x32, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1e, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x78, 0xf8, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, |  | ||||||
|     0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x00, |  | ||||||
|     0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x72, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, |  | ||||||
|     0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x14, 0x00, 0x00, 0x00, 0xb8, |  | ||||||
|     0xf8, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x01, 0x01, 0x00, 0x00, 0xa6, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, |  | ||||||
|     0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xec, 0xf8, 0xff, 0xff, 0x06, 0x00, |  | ||||||
|     0x00, 0x00, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xda, |  | ||||||
|     0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x20, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, |  | ||||||
|     0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xec, 0x00, |  | ||||||
|     0x00, 0x00, 0x16, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1a, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xf9, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, |  | ||||||
|     0x43, 0x61, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x4a, 0xfa, 0xff, |  | ||||||
|     0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x0f, 0x1c, 0x00, 0x00, 0x00, 0x90, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, |  | ||||||
|     0x6c, 0x5f, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, |  | ||||||
|     0x86, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xf9, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x69, |  | ||||||
|     0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x66, 0x66, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0xbe, |  | ||||||
|     0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x04, 0xfa, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, |  | ||||||
|     0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x31, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, 0x44, 0xfa, 0xff, 0xff, |  | ||||||
|     0x15, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, |  | ||||||
|     0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x42, 0xfb, |  | ||||||
|     0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x88, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, |  | ||||||
|     0x61, 0x70, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x76, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, |  | ||||||
|     0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1c, 0x00, |  | ||||||
|     0x00, 0x00, 0xbc, 0xfa, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x77, 0x69, |  | ||||||
|     0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, |  | ||||||
|     0xb6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xfc, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, 0x6f, |  | ||||||
|     0x6e, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, |  | ||||||
|     0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, |  | ||||||
|     0x2c, 0xfb, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x16, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, |  | ||||||
|     0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, |  | ||||||
|     0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, |  | ||||||
|     0x00, 0x8c, 0xfb, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2f, 0x73, 0x68, |  | ||||||
|     0x61, 0x70, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0xfc, 0xff, 0xff, 0x00, |  | ||||||
|     0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, |  | ||||||
|     0x24, 0x00, 0x00, 0x00, 0xc8, 0xfb, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, |  | ||||||
|     0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x2f, 0x79, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0xc2, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x08, 0xfc, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, |  | ||||||
|     0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, |  | ||||||
|     0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x0a, 0xfd, |  | ||||||
|     0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x50, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, |  | ||||||
|     0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, |  | ||||||
|     0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x52, 0xfd, 0xff, 0xff, 0x00, 0x00, |  | ||||||
|     0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, |  | ||||||
|     0x00, 0x00, 0x00, 0x98, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, |  | ||||||
|     0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, |  | ||||||
|     0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x9a, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, |  | ||||||
|     0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0xe0, |  | ||||||
|     0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, |  | ||||||
|     0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00, |  | ||||||
|     0x00, 0x29, 0x00, 0x00, 0x00, 0xe2, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, |  | ||||||
|     0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x28, 0xfd, 0xff, 0xff, 0x1a, |  | ||||||
|     0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, |  | ||||||
|     0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, |  | ||||||
|     0x00, 0x2a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x70, 0xfd, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x73, |  | ||||||
|     0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, |  | ||||||
|     0x01, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x6a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, |  | ||||||
|     0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0xb0, 0xfd, |  | ||||||
|     0xff, 0xff, 0x13, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, |  | ||||||
|     0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xfe, 0xff, 0xff, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x02, 0x24, 0x00, 0x00, 0x00, 0xf0, 0xfd, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, |  | ||||||
|     0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, |  | ||||||
|     0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xee, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, |  | ||||||
|     0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x34, 0xfe, 0xff, |  | ||||||
|     0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, |  | ||||||
|     0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, |  | ||||||
|     0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x78, 0xfe, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, |  | ||||||
|     0x74, 0x5f, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, |  | ||||||
|     0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xa8, |  | ||||||
|     0xfe, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x05, 0x00, 0x00, 0x00, 0x96, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, |  | ||||||
|     0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xdc, 0xfe, 0xff, 0xff, 0x07, 0x00, |  | ||||||
|     0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x5f, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xca, |  | ||||||
|     0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, |  | ||||||
|     0x73, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x1c, 0x00, |  | ||||||
|     0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x16, |  | ||||||
|     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x07, 0x2c, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, |  | ||||||
|     0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, |  | ||||||
|     0x66, 0x72, 0x61, 0x6d, 0x65, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, |  | ||||||
|     0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, |  | ||||||
|     0xa4, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, |  | ||||||
|     0x00, 0x10, 0x01, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x50, 0x00, |  | ||||||
|     0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, |  | ||||||
|     0x00, 0x00, 0x00, 0x50, 0xfe, 0xff, 0xff, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x5c, 0xfe, 0xff, 0xff, |  | ||||||
|     0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x68, 0xfe, 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x2a, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7c, 0xfe, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x12, 0x70, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x13, |  | ||||||
|     0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, |  | ||||||
|     0x4c, 0x6f, 0x67, 0x00, 0x98, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x20, 0x0a, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x50, 0x43, 0x41, 0x4e, 0x00, 0x00, 0xb8, 0xfe, |  | ||||||
|     0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x23, 0x00, 0x00, 0x00, 0x53, |  | ||||||
|     0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x70, 0x65, 0x63, |  | ||||||
|     0x74, 0x72, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xf0, 0xfe, 0xff, |  | ||||||
|     0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1a, 0x00, 0x00, 0x00, 0x53, 0x69, |  | ||||||
|     0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x71, 0x75, 0x61, 0x72, |  | ||||||
|     0x65, 0x52, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, |  | ||||||
|     0x72, 0x42, 0x61, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x02, 0x6c, 0xff, 0xff, 0xff, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x0c, 0x00, 0x10, 0x00, 0x0f, |  | ||||||
|     0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x00, 0x35, 0x7c, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0a, |  | ||||||
|     0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x52, 0x66, 0x66, 0x74, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, |  | ||||||
|     0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x12, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, |  | ||||||
|     0x6e, 0x61, 0x6c, 0x46, 0x66, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x0c, 0x00, |  | ||||||
|     0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, |  | ||||||
|     0x00, 0x00, 0x16, 0x0c, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, |  | ||||||
|     0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, |  | ||||||
|     0x6e, 0x61, 0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x00}; |  | ||||||
|  |  | ||||||
| }  // namespace micro_wake_word |  | ||||||
| }  // namespace esphome |  | ||||||
|  |  | ||||||
| #endif  // USE_ESP_IDF |  | ||||||
| @@ -1,12 +1,5 @@ | |||||||
| #include "micro_wake_word.h" | #include "micro_wake_word.h" | ||||||
|  | #include "streaming_model.h" | ||||||
| /** |  | ||||||
|  * This is a workaround until we can figure out a way to get |  | ||||||
|  * the tflite-micro idf component code available in CI |  | ||||||
|  * |  | ||||||
|  * */ |  | ||||||
| // |  | ||||||
| #ifndef CLANG_TIDY |  | ||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
| @@ -14,13 +7,13 @@ | |||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| #include "audio_preprocessor_int8_model_data.h" | #include <frontend.h> | ||||||
|  | #include <frontend_util.h> | ||||||
|  |  | ||||||
| #include <tensorflow/lite/core/c/common.h> | #include <tensorflow/lite/core/c/common.h> | ||||||
| #include <tensorflow/lite/micro/micro_interpreter.h> | #include <tensorflow/lite/micro/micro_interpreter.h> | ||||||
| #include <tensorflow/lite/micro/micro_mutable_op_resolver.h> | #include <tensorflow/lite/micro/micro_mutable_op_resolver.h> | ||||||
|  |  | ||||||
| #include <cinttypes> |  | ||||||
| #include <cmath> | #include <cmath> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| @@ -29,9 +22,9 @@ namespace micro_wake_word { | |||||||
| static const char *const TAG = "micro_wake_word"; | static const char *const TAG = "micro_wake_word"; | ||||||
|  |  | ||||||
| static const size_t SAMPLE_RATE_HZ = 16000;  // 16 kHz | static const size_t SAMPLE_RATE_HZ = 16000;  // 16 kHz | ||||||
| static const size_t BUFFER_LENGTH = 500;     // 0.5 seconds | static const size_t BUFFER_LENGTH = 64;      // 0.064 seconds | ||||||
| static const size_t BUFFER_SIZE = SAMPLE_RATE_HZ / 1000 * BUFFER_LENGTH; | static const size_t BUFFER_SIZE = SAMPLE_RATE_HZ / 1000 * BUFFER_LENGTH; | ||||||
| static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000;  // 32ms * 16kHz / 1000ms | static const size_t INPUT_BUFFER_SIZE = 16 * SAMPLE_RATE_HZ / 1000;  // 16ms * 16kHz / 1000ms | ||||||
|  |  | ||||||
| float MicroWakeWord::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } | float MicroWakeWord::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } | ||||||
|  |  | ||||||
| @@ -56,58 +49,56 @@ static const LogString *micro_wake_word_state_to_string(State state) { | |||||||
|  |  | ||||||
| void MicroWakeWord::dump_config() { | void MicroWakeWord::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "microWakeWord:"); |   ESP_LOGCONFIG(TAG, "microWakeWord:"); | ||||||
|   ESP_LOGCONFIG(TAG, "  Wake Word: %s", this->get_wake_word().c_str()); |   ESP_LOGCONFIG(TAG, "  models:"); | ||||||
|   ESP_LOGCONFIG(TAG, "  Probability cutoff: %.3f", this->probability_cutoff_); |   for (auto &model : this->wake_word_models_) { | ||||||
|   ESP_LOGCONFIG(TAG, "  Sliding window size: %d", this->sliding_window_average_size_); |     model.log_model_config(); | ||||||
|  |   } | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |   this->vad_model_->log_model_config(); | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| void MicroWakeWord::setup() { | void MicroWakeWord::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up microWakeWord..."); |   ESP_LOGCONFIG(TAG, "Setting up microWakeWord..."); | ||||||
|  |  | ||||||
|   if (!this->initialize_models()) { |   if (!this->register_streaming_ops_(this->streaming_op_resolver_)) { | ||||||
|     ESP_LOGE(TAG, "Failed to initialize models"); |  | ||||||
|     this->mark_failed(); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ExternalRAMAllocator<int16_t> allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE); |  | ||||||
|   this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE * sizeof(int16_t)); |  | ||||||
|   if (this->input_buffer_ == nullptr) { |  | ||||||
|     ESP_LOGW(TAG, "Could not allocate input buffer"); |  | ||||||
|     this->mark_failed(); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t)); |  | ||||||
|   if (this->ring_buffer_ == nullptr) { |  | ||||||
|     ESP_LOGW(TAG, "Could not allocate ring buffer"); |  | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ESP_LOGCONFIG(TAG, "Micro Wake Word initialized"); |   ESP_LOGCONFIG(TAG, "Micro Wake Word initialized"); | ||||||
|  |  | ||||||
|  |   this->frontend_config_.window.size_ms = FEATURE_DURATION_MS; | ||||||
|  |   this->frontend_config_.window.step_size_ms = this->features_step_size_; | ||||||
|  |   this->frontend_config_.filterbank.num_channels = PREPROCESSOR_FEATURE_SIZE; | ||||||
|  |   this->frontend_config_.filterbank.lower_band_limit = 125.0; | ||||||
|  |   this->frontend_config_.filterbank.upper_band_limit = 7500.0; | ||||||
|  |   this->frontend_config_.noise_reduction.smoothing_bits = 10; | ||||||
|  |   this->frontend_config_.noise_reduction.even_smoothing = 0.025; | ||||||
|  |   this->frontend_config_.noise_reduction.odd_smoothing = 0.06; | ||||||
|  |   this->frontend_config_.noise_reduction.min_signal_remaining = 0.05; | ||||||
|  |   this->frontend_config_.pcan_gain_control.enable_pcan = 1; | ||||||
|  |   this->frontend_config_.pcan_gain_control.strength = 0.95; | ||||||
|  |   this->frontend_config_.pcan_gain_control.offset = 80.0; | ||||||
|  |   this->frontend_config_.pcan_gain_control.gain_bits = 21; | ||||||
|  |   this->frontend_config_.log_scale.enable_log = 1; | ||||||
|  |   this->frontend_config_.log_scale.scale_shift = 6; | ||||||
| } | } | ||||||
|  |  | ||||||
| int MicroWakeWord::read_microphone_() { | void MicroWakeWord::add_wake_word_model(const uint8_t *model_start, float probability_cutoff, | ||||||
|   size_t bytes_read = this->microphone_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); |                                         size_t sliding_window_average_size, const std::string &wake_word, | ||||||
|   if (bytes_read == 0) { |                                         size_t tensor_arena_size) { | ||||||
|     return 0; |   this->wake_word_models_.emplace_back(model_start, probability_cutoff, sliding_window_average_size, wake_word, | ||||||
|   } |                                        tensor_arena_size); | ||||||
|  |  | ||||||
|   size_t bytes_free = this->ring_buffer_->free(); |  | ||||||
|  |  | ||||||
|   if (bytes_free < bytes_read) { |  | ||||||
|     ESP_LOGW(TAG, |  | ||||||
|              "Not enough free bytes in ring buffer to store incoming audio data (free bytes=%d, incoming bytes=%d). " |  | ||||||
|              "Resetting the ring buffer. Wake word detection accuracy will be reduced.", |  | ||||||
|              bytes_free, bytes_read); |  | ||||||
|  |  | ||||||
|     this->ring_buffer_->reset(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  | void MicroWakeWord::add_vad_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, | ||||||
|  |                                   size_t tensor_arena_size) { | ||||||
|  |   this->vad_model_ = make_unique<VADModel>(model_start, probability_cutoff, sliding_window_size, tensor_arena_size); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| void MicroWakeWord::loop() { | void MicroWakeWord::loop() { | ||||||
|   switch (this->state_) { |   switch (this->state_) { | ||||||
|     case State::IDLE: |     case State::IDLE: | ||||||
| @@ -124,9 +115,12 @@ void MicroWakeWord::loop() { | |||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case State::DETECTING_WAKE_WORD: |     case State::DETECTING_WAKE_WORD: | ||||||
|       this->read_microphone_(); |       while (!this->has_enough_samples_()) { | ||||||
|       if (this->detect_wake_word_()) { |         this->read_microphone_(); | ||||||
|         ESP_LOGD(TAG, "Wake Word Detected"); |       } | ||||||
|  |       this->update_model_probabilities_(); | ||||||
|  |       if (this->detect_wake_words_()) { | ||||||
|  |         ESP_LOGD(TAG, "Wake Word '%s' Detected", (this->detected_wake_word_).c_str()); | ||||||
|         this->detected_ = true; |         this->detected_ = true; | ||||||
|         this->set_state_(State::STOP_MICROPHONE); |         this->set_state_(State::STOP_MICROPHONE); | ||||||
|       } |       } | ||||||
| @@ -136,13 +130,16 @@ void MicroWakeWord::loop() { | |||||||
|       this->microphone_->stop(); |       this->microphone_->stop(); | ||||||
|       this->set_state_(State::STOPPING_MICROPHONE); |       this->set_state_(State::STOPPING_MICROPHONE); | ||||||
|       this->high_freq_.stop(); |       this->high_freq_.stop(); | ||||||
|  |       this->unload_models_(); | ||||||
|  |       this->deallocate_buffers_(); | ||||||
|       break; |       break; | ||||||
|     case State::STOPPING_MICROPHONE: |     case State::STOPPING_MICROPHONE: | ||||||
|       if (this->microphone_->is_stopped()) { |       if (this->microphone_->is_stopped()) { | ||||||
|         this->set_state_(State::IDLE); |         this->set_state_(State::IDLE); | ||||||
|         if (this->detected_) { |         if (this->detected_) { | ||||||
|  |           this->wake_word_detected_trigger_->trigger(this->detected_wake_word_); | ||||||
|           this->detected_ = false; |           this->detected_ = false; | ||||||
|           this->wake_word_detected_trigger_->trigger(this->wake_word_); |           this->detected_wake_word_ = ""; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
| @@ -150,14 +147,34 @@ void MicroWakeWord::loop() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void MicroWakeWord::start() { | void MicroWakeWord::start() { | ||||||
|  |   if (!this->is_ready()) { | ||||||
|  |     ESP_LOGW(TAG, "Wake word detection can't start as the component hasn't been setup yet"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (this->is_failed()) { |   if (this->is_failed()) { | ||||||
|     ESP_LOGW(TAG, "Wake word component is marked as failed. Please check setup logs"); |     ESP_LOGW(TAG, "Wake word component is marked as failed. Please check setup logs"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (!this->load_models_() || !this->allocate_buffers_()) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to load the wake word model(s) or allocate buffers"); | ||||||
|  |     this->status_set_error(); | ||||||
|  |   } else { | ||||||
|  |     this->status_clear_error(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->status_has_error()) { | ||||||
|  |     ESP_LOGW(TAG, "Wake word component has an error. Please check logs"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (this->state_ != State::IDLE) { |   if (this->state_ != State::IDLE) { | ||||||
|     ESP_LOGW(TAG, "Wake word is already running"); |     ESP_LOGW(TAG, "Wake word is already running"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   this->reset_states_(); | ||||||
|   this->set_state_(State::START_MICROPHONE); |   this->set_state_(State::START_MICROPHONE); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -179,289 +196,218 @@ void MicroWakeWord::set_state_(State state) { | |||||||
|   this->state_ = state; |   this->state_ = state; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MicroWakeWord::initialize_models() { | size_t MicroWakeWord::read_microphone_() { | ||||||
|   ExternalRAMAllocator<uint8_t> arena_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); |   size_t bytes_read = this->microphone_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); | ||||||
|   ExternalRAMAllocator<int8_t> features_allocator(ExternalRAMAllocator<int8_t>::ALLOW_FAILURE); |   if (bytes_read == 0) { | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   size_t bytes_free = this->ring_buffer_->free(); | ||||||
|  |  | ||||||
|  |   if (bytes_free < bytes_read) { | ||||||
|  |     ESP_LOGW(TAG, | ||||||
|  |              "Not enough free bytes in ring buffer to store incoming audio data (free bytes=%d, incoming bytes=%d). " | ||||||
|  |              "Resetting the ring buffer. Wake word detection accuracy will be reduced.", | ||||||
|  |              bytes_free, bytes_read); | ||||||
|  |  | ||||||
|  |     this->ring_buffer_->reset(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool MicroWakeWord::allocate_buffers_() { | ||||||
|   ExternalRAMAllocator<int16_t> audio_samples_allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE); |   ExternalRAMAllocator<int16_t> audio_samples_allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE); | ||||||
|  |  | ||||||
|   this->streaming_tensor_arena_ = arena_allocator.allocate(STREAMING_MODEL_ARENA_SIZE); |   if (this->input_buffer_ == nullptr) { | ||||||
|   if (this->streaming_tensor_arena_ == nullptr) { |     this->input_buffer_ = audio_samples_allocator.allocate(INPUT_BUFFER_SIZE * sizeof(int16_t)); | ||||||
|     ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena."); |     if (this->input_buffer_ == nullptr) { | ||||||
|     return false; |       ESP_LOGE(TAG, "Could not allocate input buffer"); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   this->streaming_var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE); |  | ||||||
|   if (this->streaming_var_arena_ == nullptr) { |  | ||||||
|     ESP_LOGE(TAG, "Could not allocate the streaming model variable's tensor arena."); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   this->preprocessor_tensor_arena_ = arena_allocator.allocate(PREPROCESSOR_ARENA_SIZE); |  | ||||||
|   if (this->preprocessor_tensor_arena_ == nullptr) { |  | ||||||
|     ESP_LOGE(TAG, "Could not allocate the audio preprocessor model's tensor arena."); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   this->new_features_data_ = features_allocator.allocate(PREPROCESSOR_FEATURE_SIZE); |  | ||||||
|   if (this->new_features_data_ == nullptr) { |  | ||||||
|     ESP_LOGE(TAG, "Could not allocate the audio features buffer."); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   this->preprocessor_audio_buffer_ = audio_samples_allocator.allocate(SAMPLE_DURATION_COUNT); |  | ||||||
|   if (this->preprocessor_audio_buffer_ == nullptr) { |   if (this->preprocessor_audio_buffer_ == nullptr) { | ||||||
|     ESP_LOGE(TAG, "Could not allocate the audio preprocessor's buffer."); |     this->preprocessor_audio_buffer_ = audio_samples_allocator.allocate(this->new_samples_to_get_()); | ||||||
|     return false; |     if (this->preprocessor_audio_buffer_ == nullptr) { | ||||||
|  |       ESP_LOGE(TAG, "Could not allocate the audio preprocessor's buffer."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   this->preprocessor_model_ = tflite::GetModel(G_AUDIO_PREPROCESSOR_INT8_TFLITE); |   if (this->ring_buffer_ == nullptr) { | ||||||
|   if (this->preprocessor_model_->version() != TFLITE_SCHEMA_VERSION) { |     this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t)); | ||||||
|     ESP_LOGE(TAG, "Wake word's audio preprocessor model's schema is not supported"); |     if (this->ring_buffer_ == nullptr) { | ||||||
|     return false; |       ESP_LOGE(TAG, "Could not allocate ring buffer"); | ||||||
|   } |       return false; | ||||||
|  |     } | ||||||
|   this->streaming_model_ = tflite::GetModel(this->model_start_); |  | ||||||
|   if (this->streaming_model_->version() != TFLITE_SCHEMA_VERSION) { |  | ||||||
|     ESP_LOGE(TAG, "Wake word's streaming model's schema is not supported"); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static tflite::MicroMutableOpResolver<18> preprocessor_op_resolver; |  | ||||||
|   static tflite::MicroMutableOpResolver<17> streaming_op_resolver; |  | ||||||
|  |  | ||||||
|   if (!this->register_preprocessor_ops_(preprocessor_op_resolver)) |  | ||||||
|     return false; |  | ||||||
|   if (!this->register_streaming_ops_(streaming_op_resolver)) |  | ||||||
|     return false; |  | ||||||
|  |  | ||||||
|   tflite::MicroAllocator *ma = |  | ||||||
|       tflite::MicroAllocator::Create(this->streaming_var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); |  | ||||||
|   this->mrv_ = tflite::MicroResourceVariables::Create(ma, 15); |  | ||||||
|  |  | ||||||
|   static tflite::MicroInterpreter static_preprocessor_interpreter( |  | ||||||
|       this->preprocessor_model_, preprocessor_op_resolver, this->preprocessor_tensor_arena_, PREPROCESSOR_ARENA_SIZE); |  | ||||||
|  |  | ||||||
|   static tflite::MicroInterpreter static_streaming_interpreter(this->streaming_model_, streaming_op_resolver, |  | ||||||
|                                                                this->streaming_tensor_arena_, |  | ||||||
|                                                                STREAMING_MODEL_ARENA_SIZE, this->mrv_); |  | ||||||
|  |  | ||||||
|   this->preprocessor_interperter_ = &static_preprocessor_interpreter; |  | ||||||
|   this->streaming_interpreter_ = &static_streaming_interpreter; |  | ||||||
|  |  | ||||||
|   // Allocate tensors for each models. |  | ||||||
|   if (this->preprocessor_interperter_->AllocateTensors() != kTfLiteOk) { |  | ||||||
|     ESP_LOGE(TAG, "Failed to allocate tensors for the audio preprocessor"); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|   if (this->streaming_interpreter_->AllocateTensors() != kTfLiteOk) { |  | ||||||
|     ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model"); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Verify input tensor matches expected values |  | ||||||
|   TfLiteTensor *input = this->streaming_interpreter_->input(0); |  | ||||||
|   if ((input->dims->size != 3) || (input->dims->data[0] != 1) || (input->dims->data[0] != 1) || |  | ||||||
|       (input->dims->data[1] != 1) || (input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) { |  | ||||||
|     ESP_LOGE(TAG, "Wake word detection model tensor input dimensions is not 1x1x%u", input->dims->data[2]); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (input->type != kTfLiteInt8) { |  | ||||||
|     ESP_LOGE(TAG, "Wake word detection model tensor input is not int8."); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Verify output tensor matches expected values |  | ||||||
|   TfLiteTensor *output = this->streaming_interpreter_->output(0); |  | ||||||
|   if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) { |  | ||||||
|     ESP_LOGE(TAG, "Wake word detection model tensor output dimensions is not 1x1."); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (output->type != kTfLiteUInt8) { |  | ||||||
|     ESP_LOGE(TAG, "Wake word detection model tensor input is not uint8."); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0); |  | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool MicroWakeWord::update_features_() { |  | ||||||
|   // Retrieve strided audio samples |  | ||||||
|   int16_t *audio_samples = nullptr; |  | ||||||
|   if (!this->stride_audio_samples_(&audio_samples)) { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Compute the features for the newest audio samples |  | ||||||
|   if (!this->generate_single_feature_(audio_samples, SAMPLE_DURATION_COUNT, this->new_features_data_)) { |  | ||||||
|     return false; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| float MicroWakeWord::perform_streaming_inference_() { | void MicroWakeWord::deallocate_buffers_() { | ||||||
|   TfLiteTensor *input = this->streaming_interpreter_->input(0); |   ExternalRAMAllocator<int16_t> audio_samples_allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE); | ||||||
|  |   audio_samples_allocator.deallocate(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); | ||||||
|   size_t bytes_to_copy = input->bytes; |   this->input_buffer_ = nullptr; | ||||||
|  |   audio_samples_allocator.deallocate(this->preprocessor_audio_buffer_, this->new_samples_to_get_()); | ||||||
|   memcpy((void *) (tflite::GetTensorData<int8_t>(input)), (const void *) (this->new_features_data_), bytes_to_copy); |   this->preprocessor_audio_buffer_ = nullptr; | ||||||
|  |  | ||||||
|   uint32_t prior_invoke = millis(); |  | ||||||
|  |  | ||||||
|   TfLiteStatus invoke_status = this->streaming_interpreter_->Invoke(); |  | ||||||
|   if (invoke_status != kTfLiteOk) { |  | ||||||
|     ESP_LOGW(TAG, "Streaming Interpreter Invoke failed"); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ESP_LOGV(TAG, "Streaming Inference Latency=%" PRIu32 " ms", (millis() - prior_invoke)); |  | ||||||
|  |  | ||||||
|   TfLiteTensor *output = this->streaming_interpreter_->output(0); |  | ||||||
|  |  | ||||||
|   return static_cast<float>(output->data.uint8[0]) / 255.0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MicroWakeWord::detect_wake_word_() { | bool MicroWakeWord::load_models_() { | ||||||
|   // Preprocess the newest audio samples into features |   // Setup preprocesor feature generator | ||||||
|   if (!this->update_features_()) { |   if (!FrontendPopulateState(&this->frontend_config_, &this->frontend_state_, AUDIO_SAMPLE_FREQUENCY)) { | ||||||
|  |     ESP_LOGD(TAG, "Failed to populate frontend state"); | ||||||
|  |     FrontendFreeStateContents(&this->frontend_state_); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Perform inference |   // Setup streaming models | ||||||
|   float streaming_prob = this->perform_streaming_inference_(); |   for (auto &model : this->wake_word_models_) { | ||||||
|  |     if (!model.load_model(this->streaming_op_resolver_)) { | ||||||
|  |       ESP_LOGE(TAG, "Failed to initialize a wake word model."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |   if (!this->vad_model_->load_model(this->streaming_op_resolver_)) { | ||||||
|  |     ESP_LOGE(TAG, "Failed to initialize VAD model."); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|   // Add the most recent probability to the sliding window |   return true; | ||||||
|   this->recent_streaming_probabilities_[this->last_n_index_] = streaming_prob; | } | ||||||
|   ++this->last_n_index_; |  | ||||||
|   if (this->last_n_index_ == this->sliding_window_average_size_) |  | ||||||
|     this->last_n_index_ = 0; |  | ||||||
|  |  | ||||||
|   float sum = 0.0; | void MicroWakeWord::unload_models_() { | ||||||
|   for (auto &prob : this->recent_streaming_probabilities_) { |   FrontendFreeStateContents(&this->frontend_state_); | ||||||
|     sum += prob; |  | ||||||
|  |   for (auto &model : this->wake_word_models_) { | ||||||
|  |     model.unload_model(); | ||||||
|  |   } | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |   this->vad_model_->unload_model(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void MicroWakeWord::update_model_probabilities_() { | ||||||
|  |   int8_t audio_features[PREPROCESSOR_FEATURE_SIZE]; | ||||||
|  |  | ||||||
|  |   if (!this->generate_features_for_window_(audio_features)) { | ||||||
|  |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   float sliding_window_average = sum / static_cast<float>(this->sliding_window_average_size_); |   // Increase the counter since the last positive detection | ||||||
|  |  | ||||||
|   // Ensure we have enough samples since the last positive detection |  | ||||||
|   this->ignore_windows_ = std::min(this->ignore_windows_ + 1, 0); |   this->ignore_windows_ = std::min(this->ignore_windows_ + 1, 0); | ||||||
|  |  | ||||||
|  |   for (auto &model : this->wake_word_models_) { | ||||||
|  |     // Perform inference | ||||||
|  |     model.perform_streaming_inference(audio_features); | ||||||
|  |   } | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |   this->vad_model_->perform_streaming_inference(audio_features); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool MicroWakeWord::detect_wake_words_() { | ||||||
|  |   // Verify we have processed samples since the last positive detection | ||||||
|   if (this->ignore_windows_ < 0) { |   if (this->ignore_windows_ < 0) { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Detect the wake word if the sliding window average is above the cutoff | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|   if (sliding_window_average > this->probability_cutoff_) { |   bool vad_state = this->vad_model_->determine_detected(); | ||||||
|     this->ignore_windows_ = -MIN_SLICES_BEFORE_DETECTION; | #endif | ||||||
|     for (auto &prob : this->recent_streaming_probabilities_) { |  | ||||||
|       prob = 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     ESP_LOGD(TAG, "Wake word sliding average probability is %.3f and most recent probability is %.3f", |   for (auto &model : this->wake_word_models_) { | ||||||
|              sliding_window_average, streaming_prob); |     if (model.determine_detected()) { | ||||||
|     return true; | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |       if (vad_state) { | ||||||
|  | #endif | ||||||
|  |         this->detected_wake_word_ = model.get_wake_word(); | ||||||
|  |         return true; | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |       } else { | ||||||
|  |         ESP_LOGD(TAG, "Wake word model predicts %s, but VAD model doesn't.", model.get_wake_word().c_str()); | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| void MicroWakeWord::set_sliding_window_average_size(size_t size) { | bool MicroWakeWord::has_enough_samples_() { | ||||||
|   this->sliding_window_average_size_ = size; |   return this->ring_buffer_->available() >= | ||||||
|   this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0); |          (this->features_step_size_ * (AUDIO_SAMPLE_FREQUENCY / 1000)) * sizeof(int16_t); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MicroWakeWord::slice_available_() { | bool MicroWakeWord::generate_features_for_window_(int8_t features[PREPROCESSOR_FEATURE_SIZE]) { | ||||||
|   size_t available = this->ring_buffer_->available(); |   // Ensure we have enough new audio samples in the ring buffer for a full window | ||||||
|  |   if (!this->has_enough_samples_()) { | ||||||
|   return available > (NEW_SAMPLES_TO_GET * sizeof(int16_t)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) { |  | ||||||
|   if (!this->slice_available_()) { |  | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Copy the last 320 bytes (160 samples over 10 ms) from the audio buffer to the start of the audio buffer |   size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_), | ||||||
|   memcpy((void *) (this->preprocessor_audio_buffer_), (void *) (this->preprocessor_audio_buffer_ + NEW_SAMPLES_TO_GET), |                                                this->new_samples_to_get_() * sizeof(int16_t), pdMS_TO_TICKS(200)); | ||||||
|          HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t)); |  | ||||||
|  |  | ||||||
|   // Copy 640 bytes (320 samples over 20 ms) from the ring buffer into the audio buffer offset 320 bytes (160 samples |  | ||||||
|   // over 10 ms) |  | ||||||
|   size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_ + HISTORY_SAMPLES_TO_KEEP), |  | ||||||
|                                                NEW_SAMPLES_TO_GET * sizeof(int16_t), pdMS_TO_TICKS(200)); |  | ||||||
|  |  | ||||||
|   if (bytes_read == 0) { |   if (bytes_read == 0) { | ||||||
|     ESP_LOGE(TAG, "Could not read data from Ring Buffer"); |     ESP_LOGE(TAG, "Could not read data from Ring Buffer"); | ||||||
|   } else if (bytes_read < NEW_SAMPLES_TO_GET * sizeof(int16_t)) { |   } else if (bytes_read < this->new_samples_to_get_() * sizeof(int16_t)) { | ||||||
|     ESP_LOGD(TAG, "Partial Read of Data by Model"); |     ESP_LOGD(TAG, "Partial Read of Data by Model"); | ||||||
|     ESP_LOGD(TAG, "Could only read %d bytes when required %d bytes ", bytes_read, |     ESP_LOGD(TAG, "Could only read %d bytes when required %d bytes ", bytes_read, | ||||||
|              (int) (NEW_SAMPLES_TO_GET * sizeof(int16_t))); |              (int) (this->new_samples_to_get_() * sizeof(int16_t))); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   *audio_samples = this->preprocessor_audio_buffer_; |   size_t num_samples_read; | ||||||
|   return true; |   struct FrontendOutput frontend_output = FrontendProcessSamples( | ||||||
| } |       &this->frontend_state_, this->preprocessor_audio_buffer_, this->new_samples_to_get_(), &num_samples_read); | ||||||
|  |  | ||||||
| bool MicroWakeWord::generate_single_feature_(const int16_t *audio_data, const int audio_data_size, |   for (size_t i = 0; i < frontend_output.size; ++i) { | ||||||
|                                              int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]) { |     // These scaling values are set to match the TFLite audio frontend int8 output. | ||||||
|   TfLiteTensor *input = this->preprocessor_interperter_->input(0); |     // The feature pipeline outputs 16-bit signed integers in roughly a 0 to 670 | ||||||
|   TfLiteTensor *output = this->preprocessor_interperter_->output(0); |     // range. In training, these are then arbitrarily divided by 25.6 to get | ||||||
|   std::copy_n(audio_data, audio_data_size, tflite::GetTensorData<int16_t>(input)); |     // float values in the rough range of 0.0 to 26.0. This scaling is performed | ||||||
|  |     // for historical reasons, to match up with the output of other feature | ||||||
|   if (this->preprocessor_interperter_->Invoke() != kTfLiteOk) { |     // generators. | ||||||
|     ESP_LOGE(TAG, "Failed to preprocess audio for local wake word."); |     // The process is then further complicated when we quantize the model. This | ||||||
|     return false; |     // means we have to scale the 0.0 to 26.0 real values to the -128 to 127 | ||||||
|  |     // signed integer numbers. | ||||||
|  |     // All this means that to get matching values from our integer feature | ||||||
|  |     // output into the tensor input, we have to perform: | ||||||
|  |     // input = (((feature / 25.6) / 26.0) * 256) - 128 | ||||||
|  |     // To simplify this and perform it in 32-bit integer math, we rearrange to: | ||||||
|  |     // input = (feature * 256) / (25.6 * 26.0) - 128 | ||||||
|  |     constexpr int32_t value_scale = 256; | ||||||
|  |     constexpr int32_t value_div = 666;  // 666 = 25.6 * 26.0 after rounding | ||||||
|  |     int32_t value = ((frontend_output.values[i] * value_scale) + (value_div / 2)) / value_div; | ||||||
|  |     value -= 128; | ||||||
|  |     if (value < -128) { | ||||||
|  |       value = -128; | ||||||
|  |     } | ||||||
|  |     if (value > 127) { | ||||||
|  |       value = 127; | ||||||
|  |     } | ||||||
|  |     features[i] = value; | ||||||
|   } |   } | ||||||
|   std::memcpy(feature_output, tflite::GetTensorData<int8_t>(output), PREPROCESSOR_FEATURE_SIZE * sizeof(int8_t)); |  | ||||||
|  |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MicroWakeWord::register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver) { | void MicroWakeWord::reset_states_() { | ||||||
|   if (op_resolver.AddReshape() != kTfLiteOk) |   ESP_LOGD(TAG, "Resetting buffers and probabilities"); | ||||||
|     return false; |   this->ring_buffer_->reset(); | ||||||
|   if (op_resolver.AddCast() != kTfLiteOk) |   this->ignore_windows_ = -MIN_SLICES_BEFORE_DETECTION; | ||||||
|     return false; |   for (auto &model : this->wake_word_models_) { | ||||||
|   if (op_resolver.AddStridedSlice() != kTfLiteOk) |     model.reset_probabilities(); | ||||||
|     return false; |   } | ||||||
|   if (op_resolver.AddConcatenation() != kTfLiteOk) | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|     return false; |   this->vad_model_->reset_probabilities(); | ||||||
|   if (op_resolver.AddMul() != kTfLiteOk) | #endif | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddAdd() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddDiv() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddMinimum() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddMaximum() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddWindow() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddFftAutoScale() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddRfft() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddEnergy() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddFilterBank() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddFilterBankSquareRoot() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddFilterBankSpectralSubtraction() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddPCAN() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|   if (op_resolver.AddFilterBankLog() != kTfLiteOk) |  | ||||||
|     return false; |  | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> &op_resolver) { | bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<20> &op_resolver) { | ||||||
|   if (op_resolver.AddCallOnce() != kTfLiteOk) |   if (op_resolver.AddCallOnce() != kTfLiteOk) | ||||||
|     return false; |     return false; | ||||||
|   if (op_resolver.AddVarHandle() != kTfLiteOk) |   if (op_resolver.AddVarHandle() != kTfLiteOk) | ||||||
| @@ -496,6 +442,12 @@ bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> & | |||||||
|     return false; |     return false; | ||||||
|   if (op_resolver.AddMaxPool2D() != kTfLiteOk) |   if (op_resolver.AddMaxPool2D() != kTfLiteOk) | ||||||
|     return false; |     return false; | ||||||
|  |   if (op_resolver.AddPad() != kTfLiteOk) | ||||||
|  |     return false; | ||||||
|  |   if (op_resolver.AddPack() != kTfLiteOk) | ||||||
|  |     return false; | ||||||
|  |   if (op_resolver.AddSplitV() != kTfLiteOk) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| @@ -504,5 +456,3 @@ bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> & | |||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif  // USE_ESP_IDF | #endif  // USE_ESP_IDF | ||||||
|  |  | ||||||
| #endif  // CLANG_TIDY |  | ||||||
|   | |||||||
| @@ -1,21 +1,18 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * This is a workaround until we can figure out a way to get |  | ||||||
|  * the tflite-micro idf component code available in CI |  | ||||||
|  * |  | ||||||
|  * */ |  | ||||||
| // |  | ||||||
| #ifndef CLANG_TIDY |  | ||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
|  | #include "preprocessor_settings.h" | ||||||
|  | #include "streaming_model.h" | ||||||
|  |  | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/ring_buffer.h" | #include "esphome/core/ring_buffer.h" | ||||||
|  |  | ||||||
| #include "esphome/components/microphone/microphone.h" | #include "esphome/components/microphone/microphone.h" | ||||||
|  |  | ||||||
|  | #include <frontend_util.h> | ||||||
|  |  | ||||||
| #include <tensorflow/lite/core/c/common.h> | #include <tensorflow/lite/core/c/common.h> | ||||||
| #include <tensorflow/lite/micro/micro_interpreter.h> | #include <tensorflow/lite/micro/micro_interpreter.h> | ||||||
| #include <tensorflow/lite/micro/micro_mutable_op_resolver.h> | #include <tensorflow/lite/micro/micro_mutable_op_resolver.h> | ||||||
| @@ -23,35 +20,6 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace micro_wake_word { | namespace micro_wake_word { | ||||||
|  |  | ||||||
| // The following are dictated by the preprocessor model |  | ||||||
| // |  | ||||||
| // The number of features the audio preprocessor generates per slice |  | ||||||
| static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40; |  | ||||||
| // How frequently the preprocessor generates a new set of features |  | ||||||
| static const uint8_t FEATURE_STRIDE_MS = 20; |  | ||||||
| // Duration of each slice used as input into the preprocessor |  | ||||||
| static const uint8_t FEATURE_DURATION_MS = 30; |  | ||||||
| // Audio sample frequency in hertz |  | ||||||
| static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000; |  | ||||||
| // The number of old audio samples that are saved to be part of the next feature window |  | ||||||
| static const uint16_t HISTORY_SAMPLES_TO_KEEP = |  | ||||||
|     ((FEATURE_DURATION_MS - FEATURE_STRIDE_MS) * (AUDIO_SAMPLE_FREQUENCY / 1000)); |  | ||||||
| // The number of new audio samples to receive to be included with the next feature window |  | ||||||
| static const uint16_t NEW_SAMPLES_TO_GET = (FEATURE_STRIDE_MS * (AUDIO_SAMPLE_FREQUENCY / 1000)); |  | ||||||
| // The total number of audio samples included in the feature window |  | ||||||
| static const uint16_t SAMPLE_DURATION_COUNT = FEATURE_DURATION_MS * AUDIO_SAMPLE_FREQUENCY / 1000; |  | ||||||
| // Number of bytes in memory needed for the preprocessor arena |  | ||||||
| static const uint32_t PREPROCESSOR_ARENA_SIZE = 9528; |  | ||||||
|  |  | ||||||
| // The following configure the streaming wake word model |  | ||||||
| // |  | ||||||
| // The number of audio slices to process before accepting a positive detection |  | ||||||
| static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74; |  | ||||||
|  |  | ||||||
| // Number of bytes in memory needed for the streaming wake word model |  | ||||||
| static const uint32_t STREAMING_MODEL_ARENA_SIZE = 64000; |  | ||||||
| static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024; |  | ||||||
|  |  | ||||||
| enum State { | enum State { | ||||||
|   IDLE, |   IDLE, | ||||||
|   START_MICROPHONE, |   START_MICROPHONE, | ||||||
| @@ -61,6 +29,9 @@ enum State { | |||||||
|   STOPPING_MICROPHONE, |   STOPPING_MICROPHONE, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // The number of audio slices to process before accepting a positive detection | ||||||
|  | static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74; | ||||||
|  |  | ||||||
| class MicroWakeWord : public Component { | class MicroWakeWord : public Component { | ||||||
|  public: |  public: | ||||||
|   void setup() override; |   void setup() override; | ||||||
| @@ -73,28 +44,21 @@ class MicroWakeWord : public Component { | |||||||
|  |  | ||||||
|   bool is_running() const { return this->state_ != State::IDLE; } |   bool is_running() const { return this->state_ != State::IDLE; } | ||||||
|  |  | ||||||
|   bool initialize_models(); |   void set_features_step_size(uint8_t step_size) { this->features_step_size_ = step_size; } | ||||||
|  |  | ||||||
|   std::string get_wake_word() { return this->wake_word_; } |  | ||||||
|  |  | ||||||
|   // Increasing either of these will reduce the rate of false acceptances while increasing the false rejection rate |  | ||||||
|   void set_probability_cutoff(float probability_cutoff) { this->probability_cutoff_ = probability_cutoff; } |  | ||||||
|   void set_sliding_window_average_size(size_t size); |  | ||||||
|  |  | ||||||
|   void set_microphone(microphone::Microphone *microphone) { this->microphone_ = microphone; } |   void set_microphone(microphone::Microphone *microphone) { this->microphone_ = microphone; } | ||||||
|  |  | ||||||
|   Trigger<std::string> *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; } |   Trigger<std::string> *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; } | ||||||
|  |  | ||||||
|   void set_model_start(const uint8_t *model_start) { this->model_start_ = model_start; } |   void add_wake_word_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size, | ||||||
|   void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; } |                            const std::string &wake_word, size_t tensor_arena_size); | ||||||
|  |  | ||||||
|  | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|  |   void add_vad_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, | ||||||
|  |                      size_t tensor_arena_size); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void set_state_(State state); |  | ||||||
|   int read_microphone_(); |  | ||||||
|  |  | ||||||
|   const uint8_t *model_start_; |  | ||||||
|   std::string wake_word_; |  | ||||||
|  |  | ||||||
|   microphone::Microphone *microphone_{nullptr}; |   microphone::Microphone *microphone_{nullptr}; | ||||||
|   Trigger<std::string> *wake_word_detected_trigger_ = new Trigger<std::string>(); |   Trigger<std::string> *wake_word_detected_trigger_ = new Trigger<std::string>(); | ||||||
|   State state_{State::IDLE}; |   State state_{State::IDLE}; | ||||||
| @@ -102,85 +66,93 @@ class MicroWakeWord : public Component { | |||||||
|  |  | ||||||
|   std::unique_ptr<RingBuffer> ring_buffer_; |   std::unique_ptr<RingBuffer> ring_buffer_; | ||||||
|  |  | ||||||
|   int16_t *input_buffer_; |   std::vector<WakeWordModel> wake_word_models_; | ||||||
|  |  | ||||||
|   const tflite::Model *preprocessor_model_{nullptr}; | #ifdef USE_MICRO_WAKE_WORD_VAD | ||||||
|   const tflite::Model *streaming_model_{nullptr}; |   std::unique_ptr<VADModel> vad_model_; | ||||||
|   tflite::MicroInterpreter *streaming_interpreter_{nullptr}; | #endif | ||||||
|   tflite::MicroInterpreter *preprocessor_interperter_{nullptr}; |  | ||||||
|  |  | ||||||
|   std::vector<float> recent_streaming_probabilities_; |   tflite::MicroMutableOpResolver<20> streaming_op_resolver_; | ||||||
|   size_t last_n_index_{0}; |  | ||||||
|  |  | ||||||
|   float probability_cutoff_{0.5}; |   // Audio frontend handles generating spectrogram features | ||||||
|   size_t sliding_window_average_size_{10}; |   struct FrontendConfig frontend_config_; | ||||||
|  |   struct FrontendState frontend_state_; | ||||||
|  |  | ||||||
|   // When the wake word detection first starts or after the word has been detected once, we ignore this many audio |   // When the wake word detection first starts, we ignore this many audio | ||||||
|   // feature slices before accepting a positive detection again |   // feature slices before accepting a positive detection | ||||||
|   int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION}; |   int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION}; | ||||||
|  |  | ||||||
|   uint8_t *streaming_var_arena_{nullptr}; |   uint8_t features_step_size_; | ||||||
|   uint8_t *streaming_tensor_arena_{nullptr}; |  | ||||||
|   uint8_t *preprocessor_tensor_arena_{nullptr}; |  | ||||||
|   int8_t *new_features_data_{nullptr}; |  | ||||||
|  |  | ||||||
|   tflite::MicroResourceVariables *mrv_{nullptr}; |   // Stores audio read from the microphone before being added to the ring buffer. | ||||||
|  |   int16_t *input_buffer_{nullptr}; | ||||||
|   // Stores audio fed into feature generator preprocessor |   // Stores audio to be fed into the audio frontend for generating features. | ||||||
|   int16_t *preprocessor_audio_buffer_; |   int16_t *preprocessor_audio_buffer_{nullptr}; | ||||||
|  |  | ||||||
|   bool detected_{false}; |   bool detected_{false}; | ||||||
|  |   std::string detected_wake_word_{""}; | ||||||
|  |  | ||||||
|   /** Detects if wake word has been said |   void set_state_(State state); | ||||||
|  |  | ||||||
|  |   /// @brief Tests if there are enough samples in the ring buffer to generate new features. | ||||||
|  |   /// @return True if enough samples, false otherwise. | ||||||
|  |   bool has_enough_samples_(); | ||||||
|  |  | ||||||
|  |   /** Reads audio from microphone into the ring buffer | ||||||
|  |    * | ||||||
|  |    * Audio data (16000 kHz with int16 samples) is read into the input_buffer_. | ||||||
|  |    * Verifies the ring buffer has enough space for all audio data. If not, it logs | ||||||
|  |    * a warning and resets the ring buffer entirely. | ||||||
|  |    * @return Number of bytes written to the ring buffer | ||||||
|  |    */ | ||||||
|  |   size_t read_microphone_(); | ||||||
|  |  | ||||||
|  |   /// @brief Allocates memory for input_buffer_, preprocessor_audio_buffer_, and ring_buffer_ | ||||||
|  |   /// @return True if successful, false otherwise | ||||||
|  |   bool allocate_buffers_(); | ||||||
|  |  | ||||||
|  |   /// @brief Frees memory allocated for input_buffer_ and preprocessor_audio_buffer_ | ||||||
|  |   void deallocate_buffers_(); | ||||||
|  |  | ||||||
|  |   /// @brief Loads streaming models and prepares the feature generation frontend | ||||||
|  |   /// @return True if successful, false otherwise | ||||||
|  |   bool load_models_(); | ||||||
|  |  | ||||||
|  |   /// @brief Deletes each model's TFLite interpreters and frees tensor arena memory. Frees memory used by the feature | ||||||
|  |   /// generation frontend. | ||||||
|  |   void unload_models_(); | ||||||
|  |  | ||||||
|  |   /** Performs inference with each configured model | ||||||
|    * |    * | ||||||
|    * If enough audio samples are available, it will generate one slice of new features. |    * If enough audio samples are available, it will generate one slice of new features. | ||||||
|    * If the streaming model predicts the wake word, then the nonstreaming model confirms it. |    * It then loops through and performs inference with each of the loaded models. | ||||||
|    * @param ring_Buffer Ring buffer containing raw audio samples |  | ||||||
|    * @return True if the wake word is detected, false otherwise |  | ||||||
|    */ |    */ | ||||||
|   bool detect_wake_word_(); |   void update_model_probabilities_(); | ||||||
|  |  | ||||||
|   /// @brief Returns true if there are enough audio samples in the buffer to generate another slice of features |   /** Checks every model's recent probabilities to determine if the wake word has been predicted | ||||||
|   bool slice_available_(); |  | ||||||
|  |  | ||||||
|   /** Shifts previous feature slices over by one and generates a new slice of features |  | ||||||
|    * |    * | ||||||
|    * @param ring_buffer ring buffer containing raw audio samples |    * Verifies the models have processed enough new samples for accurate predictions. | ||||||
|    * @return True if a new slice of features was generated, false otherwise |    * Sets detected_wake_word_ to the wake word, if one is detected. | ||||||
|  |    * @return True if a wake word is predicted, false otherwise | ||||||
|    */ |    */ | ||||||
|   bool update_features_(); |   bool detect_wake_words_(); | ||||||
|  |  | ||||||
|   /** Generates features from audio samples |   /** Generates features for a window of audio samples | ||||||
|    * |    * | ||||||
|    * Adapted from TFLite micro speech example |    * Reads samples from the ring buffer and feeds them into the preprocessor frontend. | ||||||
|    * @param audio_data Pointer to array with the audio samples |    * Adapted from TFLite microspeech frontend. | ||||||
|    * @param audio_data_size The number of samples to use as input to the preprocessor model |    * @param features int8_t array to store the audio features | ||||||
|    * @param feature_output Array that will store the features |  | ||||||
|    * @return True if successful, false otherwise. |    * @return True if successful, false otherwise. | ||||||
|    */ |    */ | ||||||
|   bool generate_single_feature_(const int16_t *audio_data, int audio_data_size, |   bool generate_features_for_window_(int8_t features[PREPROCESSOR_FEATURE_SIZE]); | ||||||
|                                 int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]); |  | ||||||
|  |  | ||||||
|   /** Performs inference over the most recent feature slice with the streaming model |   /// @brief Resets the ring buffer, ignore_windows_, and sliding window probabilities | ||||||
|    * |   void reset_states_(); | ||||||
|    * @return Probability of the wake word between 0.0 and 1.0 |  | ||||||
|    */ |  | ||||||
|   float perform_streaming_inference_(); |  | ||||||
|  |  | ||||||
|   /** Strides the audio samples by keeping the last 10 ms of the previous slice |  | ||||||
|    * |  | ||||||
|    * Adapted from the TFLite micro speech example |  | ||||||
|    * @param ring_buffer Ring buffer containing raw audio samples |  | ||||||
|    * @param audio_samples Pointer to an array that will store the strided audio samples |  | ||||||
|    * @return True if successful, false otherwise |  | ||||||
|    */ |  | ||||||
|   bool stride_audio_samples_(int16_t **audio_samples); |  | ||||||
|  |  | ||||||
|   /// @brief Returns true if successfully registered the preprocessor's TensorFlow operations |  | ||||||
|   bool register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver); |  | ||||||
|  |  | ||||||
|   /// @brief Returns true if successfully registered the streaming model's TensorFlow operations |   /// @brief Returns true if successfully registered the streaming model's TensorFlow operations | ||||||
|   bool register_streaming_ops_(tflite::MicroMutableOpResolver<17> &op_resolver); |   bool register_streaming_ops_(tflite::MicroMutableOpResolver<20> &op_resolver); | ||||||
|  |  | ||||||
|  |   inline uint16_t new_samples_to_get_() { return (this->features_step_size_ * (AUDIO_SAMPLE_FREQUENCY / 1000)); } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<MicroWakeWord> { | template<typename... Ts> class StartAction : public Action<Ts...>, public Parented<MicroWakeWord> { | ||||||
| @@ -202,5 +174,3 @@ template<typename... Ts> class IsRunningCondition : public Condition<Ts...>, pub | |||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif  // USE_ESP_IDF | #endif  // USE_ESP_IDF | ||||||
|  |  | ||||||
| #endif  // CLANG_TIDY |  | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								esphome/components/micro_wake_word/preprocessor_settings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								esphome/components/micro_wake_word/preprocessor_settings.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
|  | #include <cstdint> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace micro_wake_word { | ||||||
|  |  | ||||||
|  | // The number of features the audio preprocessor generates per slice | ||||||
|  | static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40; | ||||||
|  | // Duration of each slice used as input into the preprocessor | ||||||
|  | static const uint8_t FEATURE_DURATION_MS = 30; | ||||||
|  | // Audio sample frequency in hertz | ||||||
|  | static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000; | ||||||
|  |  | ||||||
|  | }  // namespace micro_wake_word | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										189
									
								
								esphome/components/micro_wake_word/streaming_model.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								esphome/components/micro_wake_word/streaming_model.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | |||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
|  | #include "streaming_model.h" | ||||||
|  |  | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | static const char *const TAG = "micro_wake_word"; | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace micro_wake_word { | ||||||
|  |  | ||||||
|  | void WakeWordModel::log_model_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "    - Wake Word: %s", this->wake_word_.c_str()); | ||||||
|  |   ESP_LOGCONFIG(TAG, "      Probability cutoff: %.3f", this->probability_cutoff_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "      Sliding window size: %d", this->sliding_window_size_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void VADModel::log_model_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "    - VAD Model"); | ||||||
|  |   ESP_LOGCONFIG(TAG, "      Probability cutoff: %.3f", this->probability_cutoff_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "      Sliding window size: %d", this->sliding_window_size_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool StreamingModel::load_model(tflite::MicroMutableOpResolver<20> &op_resolver) { | ||||||
|  |   ExternalRAMAllocator<uint8_t> arena_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||||
|  |  | ||||||
|  |   if (this->tensor_arena_ == nullptr) { | ||||||
|  |     this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_); | ||||||
|  |     if (this->tensor_arena_ == nullptr) { | ||||||
|  |       ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->var_arena_ == nullptr) { | ||||||
|  |     this->var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE); | ||||||
|  |     if (this->var_arena_ == nullptr) { | ||||||
|  |       ESP_LOGE(TAG, "Could not allocate the streaming model's variable tensor arena."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     this->ma_ = tflite::MicroAllocator::Create(this->var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); | ||||||
|  |     this->mrv_ = tflite::MicroResourceVariables::Create(this->ma_, 20); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const tflite::Model *model = tflite::GetModel(this->model_start_); | ||||||
|  |   if (model->version() != TFLITE_SCHEMA_VERSION) { | ||||||
|  |     ESP_LOGE(TAG, "Streaming model's schema is not supported"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->interpreter_ == nullptr) { | ||||||
|  |     this->interpreter_ = make_unique<tflite::MicroInterpreter>( | ||||||
|  |         tflite::GetModel(this->model_start_), op_resolver, this->tensor_arena_, this->tensor_arena_size_, this->mrv_); | ||||||
|  |     if (this->interpreter_->AllocateTensors() != kTfLiteOk) { | ||||||
|  |       ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model"); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Verify input tensor matches expected values | ||||||
|  |     // Dimension 3 will represent the first layer stride, so skip it may vary | ||||||
|  |     TfLiteTensor *input = this->interpreter_->input(0); | ||||||
|  |     if ((input->dims->size != 3) || (input->dims->data[0] != 1) || | ||||||
|  |         (input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) { | ||||||
|  |       ESP_LOGE(TAG, "Streaming model tensor input dimensions has improper dimensions."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (input->type != kTfLiteInt8) { | ||||||
|  |       ESP_LOGE(TAG, "Streaming model tensor input is not int8."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Verify output tensor matches expected values | ||||||
|  |     TfLiteTensor *output = this->interpreter_->output(0); | ||||||
|  |     if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) { | ||||||
|  |       ESP_LOGE(TAG, "Streaming model tensor output dimension is not 1x1."); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (output->type != kTfLiteUInt8) { | ||||||
|  |       ESP_LOGE(TAG, "Streaming model tensor output is not uint8."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void StreamingModel::unload_model() { | ||||||
|  |   this->interpreter_.reset(); | ||||||
|  |  | ||||||
|  |   ExternalRAMAllocator<uint8_t> arena_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||||
|  |  | ||||||
|  |   arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_); | ||||||
|  |   this->tensor_arena_ = nullptr; | ||||||
|  |   arena_allocator.deallocate(this->var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); | ||||||
|  |   this->var_arena_ = nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool StreamingModel::perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]) { | ||||||
|  |   if (this->interpreter_ != nullptr) { | ||||||
|  |     TfLiteTensor *input = this->interpreter_->input(0); | ||||||
|  |  | ||||||
|  |     std::memmove( | ||||||
|  |         (int8_t *) (tflite::GetTensorData<int8_t>(input)) + PREPROCESSOR_FEATURE_SIZE * this->current_stride_step_, | ||||||
|  |         features, PREPROCESSOR_FEATURE_SIZE); | ||||||
|  |     ++this->current_stride_step_; | ||||||
|  |  | ||||||
|  |     uint8_t stride = this->interpreter_->input(0)->dims->data[1]; | ||||||
|  |  | ||||||
|  |     if (this->current_stride_step_ >= stride) { | ||||||
|  |       this->current_stride_step_ = 0; | ||||||
|  |  | ||||||
|  |       TfLiteStatus invoke_status = this->interpreter_->Invoke(); | ||||||
|  |       if (invoke_status != kTfLiteOk) { | ||||||
|  |         ESP_LOGW(TAG, "Streaming interpreter invoke failed"); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       TfLiteTensor *output = this->interpreter_->output(0); | ||||||
|  |  | ||||||
|  |       ++this->last_n_index_; | ||||||
|  |       if (this->last_n_index_ == this->sliding_window_size_) | ||||||
|  |         this->last_n_index_ = 0; | ||||||
|  |       this->recent_streaming_probabilities_[this->last_n_index_] = output->data.uint8[0];  // probability; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   ESP_LOGE(TAG, "Streaming interpreter is not initialized."); | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void StreamingModel::reset_probabilities() { | ||||||
|  |   for (auto &prob : this->recent_streaming_probabilities_) { | ||||||
|  |     prob = 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | WakeWordModel::WakeWordModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size, | ||||||
|  |                              const std::string &wake_word, size_t tensor_arena_size) { | ||||||
|  |   this->model_start_ = model_start; | ||||||
|  |   this->probability_cutoff_ = probability_cutoff; | ||||||
|  |   this->sliding_window_size_ = sliding_window_average_size; | ||||||
|  |   this->recent_streaming_probabilities_.resize(sliding_window_average_size, 0); | ||||||
|  |   this->wake_word_ = wake_word; | ||||||
|  |   this->tensor_arena_size_ = tensor_arena_size; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | bool WakeWordModel::determine_detected() { | ||||||
|  |   int32_t sum = 0; | ||||||
|  |   for (auto &prob : this->recent_streaming_probabilities_) { | ||||||
|  |     sum += prob; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   float sliding_window_average = static_cast<float>(sum) / static_cast<float>(255 * this->sliding_window_size_); | ||||||
|  |  | ||||||
|  |   // Detect the wake word if the sliding window average is above the cutoff | ||||||
|  |   if (sliding_window_average > this->probability_cutoff_) { | ||||||
|  |     ESP_LOGD(TAG, "The '%s' model sliding average probability is %.3f and most recent probability is %.3f", | ||||||
|  |              this->wake_word_.c_str(), sliding_window_average, | ||||||
|  |              this->recent_streaming_probabilities_[this->last_n_index_] / (255.0)); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VADModel::VADModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, | ||||||
|  |                    size_t tensor_arena_size) { | ||||||
|  |   this->model_start_ = model_start; | ||||||
|  |   this->probability_cutoff_ = probability_cutoff; | ||||||
|  |   this->sliding_window_size_ = sliding_window_size; | ||||||
|  |   this->recent_streaming_probabilities_.resize(sliding_window_size, 0); | ||||||
|  |   this->tensor_arena_size_ = tensor_arena_size; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | bool VADModel::determine_detected() { | ||||||
|  |   uint8_t max = 0; | ||||||
|  |   for (auto &prob : this->recent_streaming_probabilities_) { | ||||||
|  |     max = std::max(prob, max); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return max > this->probability_cutoff_; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace micro_wake_word | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										84
									
								
								esphome/components/micro_wake_word/streaming_model.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								esphome/components/micro_wake_word/streaming_model.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
|  | #include "preprocessor_settings.h" | ||||||
|  |  | ||||||
|  | #include <tensorflow/lite/core/c/common.h> | ||||||
|  | #include <tensorflow/lite/micro/micro_interpreter.h> | ||||||
|  | #include <tensorflow/lite/micro/micro_mutable_op_resolver.h> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace micro_wake_word { | ||||||
|  |  | ||||||
|  | static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024; | ||||||
|  |  | ||||||
|  | class StreamingModel { | ||||||
|  |  public: | ||||||
|  |   virtual void log_model_config() = 0; | ||||||
|  |   virtual bool determine_detected() = 0; | ||||||
|  |  | ||||||
|  |   bool perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]); | ||||||
|  |  | ||||||
|  |   /// @brief Sets all recent_streaming_probabilities to 0 | ||||||
|  |   void reset_probabilities(); | ||||||
|  |  | ||||||
|  |   /// @brief Allocates tensor and variable arenas and sets up the model interpreter | ||||||
|  |   /// @param op_resolver MicroMutableOpResolver object that must exist until the model is unloaded | ||||||
|  |   /// @return True if successful, false otherwise | ||||||
|  |   bool load_model(tflite::MicroMutableOpResolver<20> &op_resolver); | ||||||
|  |  | ||||||
|  |   /// @brief Destroys the TFLite interpreter and frees the tensor and variable arenas' memory | ||||||
|  |   void unload_model(); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint8_t current_stride_step_{0}; | ||||||
|  |  | ||||||
|  |   float probability_cutoff_; | ||||||
|  |   size_t sliding_window_size_; | ||||||
|  |   size_t last_n_index_{0}; | ||||||
|  |   size_t tensor_arena_size_; | ||||||
|  |   std::vector<uint8_t> recent_streaming_probabilities_; | ||||||
|  |  | ||||||
|  |   const uint8_t *model_start_; | ||||||
|  |   uint8_t *tensor_arena_{nullptr}; | ||||||
|  |   uint8_t *var_arena_{nullptr}; | ||||||
|  |   std::unique_ptr<tflite::MicroInterpreter> interpreter_; | ||||||
|  |   tflite::MicroResourceVariables *mrv_{nullptr}; | ||||||
|  |   tflite::MicroAllocator *ma_{nullptr}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class WakeWordModel final : public StreamingModel { | ||||||
|  |  public: | ||||||
|  |   WakeWordModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size, | ||||||
|  |                 const std::string &wake_word, size_t tensor_arena_size); | ||||||
|  |  | ||||||
|  |   void log_model_config() override; | ||||||
|  |  | ||||||
|  |   /// @brief Checks for the wake word by comparing the mean probability in the sliding window with the probability | ||||||
|  |   /// cutoff | ||||||
|  |   /// @return True if wake word is detected, false otherwise | ||||||
|  |   bool determine_detected() override; | ||||||
|  |  | ||||||
|  |   const std::string &get_wake_word() const { return this->wake_word_; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   std::string wake_word_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class VADModel final : public StreamingModel { | ||||||
|  |  public: | ||||||
|  |   VADModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, size_t tensor_arena_size); | ||||||
|  |  | ||||||
|  |   void log_model_config() override; | ||||||
|  |  | ||||||
|  |   /// @brief Checks for voice activity by comparing the max probability in the sliding window with the probability | ||||||
|  |   /// cutoff | ||||||
|  |   /// @return True if voice activity is detected, false otherwise | ||||||
|  |   bool determine_detected() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace micro_wake_word | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -86,6 +86,7 @@ | |||||||
| #define USE_ESP32_BLE_SERVER | #define USE_ESP32_BLE_SERVER | ||||||
| #define USE_ESP32_CAMERA | #define USE_ESP32_CAMERA | ||||||
| #define USE_IMPROV | #define USE_IMPROV | ||||||
|  | #define USE_MICRO_WAKE_WORD_VAD | ||||||
| #define USE_MICROPHONE | #define USE_MICROPHONE | ||||||
| #define USE_PSRAM | #define USE_PSRAM | ||||||
| #define USE_SOCKET_IMPL_BSD_SOCKETS | #define USE_SOCKET_IMPL_BSD_SOCKETS | ||||||
|   | |||||||
| @@ -142,7 +142,8 @@ platform_packages = | |||||||
| framework = espidf | framework = espidf | ||||||
| lib_deps = | lib_deps = | ||||||
|     ${common:idf.lib_deps} |     ${common:idf.lib_deps} | ||||||
|     droscy/esp_wireguard@0.4.2    ; wireguard |     droscy/esp_wireguard@0.4.2              ; wireguard | ||||||
|  |     kahrendt/ESPMicroSpeechFeatures@1.0.0   ; micro_wake_word | ||||||
| build_flags = | build_flags = | ||||||
|     ${common:idf.build_flags} |     ${common:idf.build_flags} | ||||||
|     -Wno-nonnull-compare |     -Wno-nonnull-compare | ||||||
|   | |||||||
| @@ -10,6 +10,10 @@ microphone: | |||||||
|     pdm: true |     pdm: true | ||||||
|  |  | ||||||
| micro_wake_word: | micro_wake_word: | ||||||
|   model: hey_jarvis |  | ||||||
|   on_wake_word_detected: |   on_wake_word_detected: | ||||||
|     - logger.log: "Wake word detected" |     - logger.log: "Wake word detected" | ||||||
|  |   models: | ||||||
|  |     - model: hey_jarvis | ||||||
|  |       probability_cutoff: 0.7 | ||||||
|  |     - model: okay_nabu | ||||||
|  |       sliding_window_size: 5 | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								tests/components/micro_wake_word/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/components/micro_wake_word/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <<: !include common.yaml | ||||||
		Reference in New Issue
	
	Block a user