Remove C++ sources/build scripts from HF release (keep precompiled binaries only)
Browse filesPer HF release convention, only prebuilt artifacts belong on the Hub:
- keep cpp/bin/openwakeword_ax650 / openwakeword_ax630c
- remove cpp sources (openwakeword_ax.cpp, engine_wrapper, wav_reader),
CMakeLists.txt, build/download scripts; source stays in the GitHub repo
- also drop tracked __pycache__ from scripts/
- cpp/CMakeLists.txt +0 -57
- cpp/README.md +7 -0
- cpp/build_ax630c.sh +0 -32
- cpp/build_ax650.sh +0 -32
- cpp/download_bsp.sh +0 -45
- cpp/openwakeword_ax.cpp +0 -379
- cpp/src/engine_wrapper.cpp +0 -197
- cpp/src/engine_wrapper.hpp +0 -43
- cpp/src/wav_reader.hpp +0 -80
- scripts/__pycache__/openwakeword_ax.cpython-312.pyc +0 -0
- scripts/__pycache__/openwakeword_ax650.cpython-312.pyc +0 -0
- scripts/__pycache__/runtime.cpython-312.pyc +0 -0
cpp/CMakeLists.txt
DELETED
|
@@ -1,57 +0,0 @@
|
|
| 1 |
-
cmake_minimum_required(VERSION 3.13)
|
| 2 |
-
project(openwakeword_axera_cpp LANGUAGES C CXX)
|
| 3 |
-
|
| 4 |
-
set(CMAKE_CXX_STANDARD 17)
|
| 5 |
-
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
| 6 |
-
set(CMAKE_BUILD_TYPE Release CACHE STRING "")
|
| 7 |
-
|
| 8 |
-
set(AXERA_TARGET "AX650" CACHE STRING "AXERA target: AX650 or AX630C")
|
| 9 |
-
set_property(CACHE AXERA_TARGET PROPERTY STRINGS AX650 AX630C)
|
| 10 |
-
string(TOUPPER "${AXERA_TARGET}" AXERA_TARGET)
|
| 11 |
-
if(AXERA_TARGET STREQUAL "AX650")
|
| 12 |
-
set(KWS_EXECUTABLE openwakeword_ax650)
|
| 13 |
-
set(DEFAULT_BSP_MSP_DIR
|
| 14 |
-
"${CMAKE_SOURCE_DIR}/toolchains/ax650n_bsp_sdk/msp/out")
|
| 15 |
-
elseif(AXERA_TARGET STREQUAL "AX630C")
|
| 16 |
-
set(KWS_EXECUTABLE openwakeword_ax630c)
|
| 17 |
-
set(DEFAULT_BSP_MSP_DIR
|
| 18 |
-
"${CMAKE_SOURCE_DIR}/toolchains/ax620e_bsp_sdk/msp/out/arm64_glibc")
|
| 19 |
-
else()
|
| 20 |
-
message(FATAL_ERROR "AXERA_TARGET must be AX650 or AX630C")
|
| 21 |
-
endif()
|
| 22 |
-
|
| 23 |
-
set(BSP_MSP_DIR "${DEFAULT_BSP_MSP_DIR}" CACHE PATH
|
| 24 |
-
"BSP directory containing include/ and lib/")
|
| 25 |
-
if(NOT EXISTS "${BSP_MSP_DIR}/include/ax_engine_api.h")
|
| 26 |
-
message(FATAL_ERROR
|
| 27 |
-
"${AXERA_TARGET} BSP not found: ${BSP_MSP_DIR}. "
|
| 28 |
-
"Set BSP_MSP_DIR to the directory containing include/ and lib/.")
|
| 29 |
-
endif()
|
| 30 |
-
|
| 31 |
-
add_executable(${KWS_EXECUTABLE}
|
| 32 |
-
openwakeword_ax.cpp
|
| 33 |
-
src/engine_wrapper.cpp
|
| 34 |
-
)
|
| 35 |
-
target_include_directories(${KWS_EXECUTABLE} PRIVATE
|
| 36 |
-
${CMAKE_SOURCE_DIR}
|
| 37 |
-
${CMAKE_SOURCE_DIR}/src
|
| 38 |
-
${BSP_MSP_DIR}/include
|
| 39 |
-
)
|
| 40 |
-
target_link_directories(${KWS_EXECUTABLE} PRIVATE ${BSP_MSP_DIR}/lib)
|
| 41 |
-
target_compile_definitions(${KWS_EXECUTABLE} PRIVATE
|
| 42 |
-
AXERA_TARGET_NAME="${AXERA_TARGET}"
|
| 43 |
-
)
|
| 44 |
-
target_compile_options(${KWS_EXECUTABLE} PRIVATE
|
| 45 |
-
-O3 -Wall -Wextra -Wpedantic
|
| 46 |
-
-fvisibility=hidden -fdata-sections -ffunction-sections
|
| 47 |
-
)
|
| 48 |
-
target_link_libraries(${KWS_EXECUTABLE} PRIVATE
|
| 49 |
-
ax_sys ax_engine ax_interpreter pthread dl m
|
| 50 |
-
)
|
| 51 |
-
target_link_options(${KWS_EXECUTABLE} PRIVATE
|
| 52 |
-
-Wl,--enable-new-dtags -Wl,--allow-shlib-undefined
|
| 53 |
-
)
|
| 54 |
-
set_target_properties(${KWS_EXECUTABLE} PROPERTIES
|
| 55 |
-
BUILD_WITH_INSTALL_RPATH TRUE
|
| 56 |
-
INSTALL_RPATH "$ORIGIN"
|
| 57 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/README.md
CHANGED
|
@@ -87,3 +87,10 @@ CPU mel 和 NPU 推理,不包含一次性模型加载与初始化。
|
|
| 87 |
THRESHOLD=0.6 AUDIO=audio/openwakeword/hey_mycroft_test.wav \
|
| 88 |
bash cpp/run_openwakeword_ax630c.sh
|
| 89 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
THRESHOLD=0.6 AUDIO=audio/openwakeword/hey_mycroft_test.wav \
|
| 88 |
bash cpp/run_openwakeword_ax630c.sh
|
| 89 |
```
|
| 90 |
+
|
| 91 |
+
## 源码
|
| 92 |
+
|
| 93 |
+
C++ 源码、构建脚本与 BSP 下载脚本在 GitHub 仓库:
|
| 94 |
+
https://github.com/AXERA-TECH/openWakeWord.AXERA
|
| 95 |
+
(HuggingFace 仅发布预编译二进制。)
|
| 96 |
+
|
cpp/build_ax630c.sh
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env bash
|
| 2 |
-
set -euo pipefail
|
| 3 |
-
|
| 4 |
-
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
| 5 |
-
CPP_DIR="${ROOT}/cpp"
|
| 6 |
-
TOOLCHAIN_ROOT="${TOOLCHAIN_ROOT:-${CPP_DIR}/toolchains/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu}"
|
| 7 |
-
BSP_MSP_DIR="${BSP_MSP_DIR:-${CPP_DIR}/toolchains/ax620e_bsp_sdk/msp/out/arm64_glibc}"
|
| 8 |
-
BUILD_DIR="${CPP_DIR}/build/ax630c"
|
| 9 |
-
|
| 10 |
-
if [[ ! -x "${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-g++" ]]; then
|
| 11 |
-
echo "ERROR: AArch64 toolchain not found: ${TOOLCHAIN_ROOT}" >&2
|
| 12 |
-
exit 2
|
| 13 |
-
fi
|
| 14 |
-
if [[ ! -f "${BSP_MSP_DIR}/include/ax_engine_api.h" ]]; then
|
| 15 |
-
echo "ERROR: AX630C BSP arm64_glibc directory not found: ${BSP_MSP_DIR}" >&2
|
| 16 |
-
exit 2
|
| 17 |
-
fi
|
| 18 |
-
|
| 19 |
-
mkdir -p "${BUILD_DIR}" "${CPP_DIR}/bin"
|
| 20 |
-
cmake -S "${CPP_DIR}" -B "${BUILD_DIR}" \
|
| 21 |
-
-DCMAKE_BUILD_TYPE=Release \
|
| 22 |
-
-DCMAKE_SYSTEM_NAME=Linux \
|
| 23 |
-
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
| 24 |
-
-DCMAKE_C_COMPILER="${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-gcc" \
|
| 25 |
-
-DCMAKE_CXX_COMPILER="${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-g++" \
|
| 26 |
-
-DAXERA_TARGET=AX630C \
|
| 27 |
-
-DBSP_MSP_DIR="${BSP_MSP_DIR}"
|
| 28 |
-
cmake --build "${BUILD_DIR}" -j"$(nproc)"
|
| 29 |
-
cp "${BUILD_DIR}/openwakeword_ax630c" "${CPP_DIR}/bin/"
|
| 30 |
-
"${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-strip" --strip-unneeded \
|
| 31 |
-
"${CPP_DIR}/bin/openwakeword_ax630c"
|
| 32 |
-
echo "Built ${CPP_DIR}/bin/openwakeword_ax630c"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/build_ax650.sh
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env bash
|
| 2 |
-
set -euo pipefail
|
| 3 |
-
|
| 4 |
-
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
| 5 |
-
CPP_DIR="${ROOT}/cpp"
|
| 6 |
-
TOOLCHAIN_ROOT="${TOOLCHAIN_ROOT:-${CPP_DIR}/toolchains/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu}"
|
| 7 |
-
BSP_MSP_DIR="${BSP_MSP_DIR:-${CPP_DIR}/toolchains/ax650n_bsp_sdk/msp/out}"
|
| 8 |
-
BUILD_DIR="${CPP_DIR}/build/ax650"
|
| 9 |
-
|
| 10 |
-
if [[ ! -x "${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-g++" ]]; then
|
| 11 |
-
echo "ERROR: AArch64 toolchain not found: ${TOOLCHAIN_ROOT}" >&2
|
| 12 |
-
exit 2
|
| 13 |
-
fi
|
| 14 |
-
if [[ ! -f "${BSP_MSP_DIR}/include/ax_engine_api.h" ]]; then
|
| 15 |
-
echo "ERROR: AX650 BSP msp/out not found: ${BSP_MSP_DIR}" >&2
|
| 16 |
-
exit 2
|
| 17 |
-
fi
|
| 18 |
-
|
| 19 |
-
mkdir -p "${BUILD_DIR}" "${CPP_DIR}/bin"
|
| 20 |
-
cmake -S "${CPP_DIR}" -B "${BUILD_DIR}" \
|
| 21 |
-
-DCMAKE_BUILD_TYPE=Release \
|
| 22 |
-
-DCMAKE_SYSTEM_NAME=Linux \
|
| 23 |
-
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
| 24 |
-
-DCMAKE_C_COMPILER="${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-gcc" \
|
| 25 |
-
-DCMAKE_CXX_COMPILER="${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-g++" \
|
| 26 |
-
-DAXERA_TARGET=AX650 \
|
| 27 |
-
-DBSP_MSP_DIR="${BSP_MSP_DIR}"
|
| 28 |
-
cmake --build "${BUILD_DIR}" -j"$(nproc)"
|
| 29 |
-
cp "${BUILD_DIR}/openwakeword_ax650" "${CPP_DIR}/bin/"
|
| 30 |
-
"${TOOLCHAIN_ROOT}/bin/aarch64-none-linux-gnu-strip" --strip-unneeded \
|
| 31 |
-
"${CPP_DIR}/bin/openwakeword_ax650"
|
| 32 |
-
echo "Built ${CPP_DIR}/bin/openwakeword_ax650"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/download_bsp.sh
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env bash
|
| 2 |
-
set -euo pipefail
|
| 3 |
-
|
| 4 |
-
CPP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 5 |
-
TOOLCHAINS_DIR="${CPP_DIR}/toolchains"
|
| 6 |
-
PLATFORM="${1:-all}"
|
| 7 |
-
|
| 8 |
-
download_sdk() {
|
| 9 |
-
local directory="$1"
|
| 10 |
-
local archive_name="$2"
|
| 11 |
-
local extracted_name="$3"
|
| 12 |
-
local url="$4"
|
| 13 |
-
|
| 14 |
-
if [[ -d "${TOOLCHAINS_DIR}/${directory}" ]]; then
|
| 15 |
-
echo "Found ${TOOLCHAINS_DIR}/${directory}"
|
| 16 |
-
return
|
| 17 |
-
fi
|
| 18 |
-
mkdir -p "${TOOLCHAINS_DIR}"
|
| 19 |
-
wget -c "${url}" -O "${TOOLCHAINS_DIR}/${archive_name}"
|
| 20 |
-
unzip -q "${TOOLCHAINS_DIR}/${archive_name}" -d "${TOOLCHAINS_DIR}"
|
| 21 |
-
mv "${TOOLCHAINS_DIR}/${extracted_name}" "${TOOLCHAINS_DIR}/${directory}"
|
| 22 |
-
rm -f "${TOOLCHAINS_DIR}/${archive_name}"
|
| 23 |
-
echo "Downloaded ${TOOLCHAINS_DIR}/${directory}"
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
case "${PLATFORM}" in
|
| 27 |
-
650|AX650|ax650)
|
| 28 |
-
download_sdk \
|
| 29 |
-
ax650n_bsp_sdk msp_50_3.10.2.zip msp_50_3.10.2 \
|
| 30 |
-
https://github.com/ZHEQIUSHUI/assets/releases/download/ax_3.6.2/msp_50_3.10.2.zip
|
| 31 |
-
;;
|
| 32 |
-
630C|AX630C|ax630c)
|
| 33 |
-
download_sdk \
|
| 34 |
-
ax620e_bsp_sdk msp_20e_3.0.0.zip msp_20e_3.0.0 \
|
| 35 |
-
https://github.com/ZHEQIUSHUI/assets/releases/download/ax_3.6.2/msp_20e_3.0.0.zip
|
| 36 |
-
;;
|
| 37 |
-
all)
|
| 38 |
-
bash "${BASH_SOURCE[0]}" 650
|
| 39 |
-
bash "${BASH_SOURCE[0]}" 630C
|
| 40 |
-
;;
|
| 41 |
-
*)
|
| 42 |
-
echo "Usage: bash cpp/download_bsp.sh [650|630C|all]" >&2
|
| 43 |
-
exit 2
|
| 44 |
-
;;
|
| 45 |
-
esac
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/openwakeword_ax.cpp
DELETED
|
@@ -1,379 +0,0 @@
|
|
| 1 |
-
#include <algorithm>
|
| 2 |
-
#include <array>
|
| 3 |
-
#include <chrono>
|
| 4 |
-
#include <cmath>
|
| 5 |
-
#include <cstdint>
|
| 6 |
-
#include <cstdio>
|
| 7 |
-
#include <cstring>
|
| 8 |
-
#include <fstream>
|
| 9 |
-
#include <limits>
|
| 10 |
-
#include <stdexcept>
|
| 11 |
-
#include <string>
|
| 12 |
-
#include <vector>
|
| 13 |
-
|
| 14 |
-
#include "ax_engine_api.h"
|
| 15 |
-
#include "ax_sys_api.h"
|
| 16 |
-
#include "src/engine_wrapper.hpp"
|
| 17 |
-
#include "src/wav_reader.hpp"
|
| 18 |
-
|
| 19 |
-
#ifndef AXERA_TARGET_NAME
|
| 20 |
-
#define AXERA_TARGET_NAME "AXERA"
|
| 21 |
-
#endif
|
| 22 |
-
|
| 23 |
-
namespace {
|
| 24 |
-
constexpr int kSampleRate = 16000;
|
| 25 |
-
constexpr int kChunkSamples = 1280;
|
| 26 |
-
constexpr int kHistorySamples = 480;
|
| 27 |
-
constexpr int kFftSize = 512;
|
| 28 |
-
constexpr int kSpectrumBins = 257;
|
| 29 |
-
constexpr int kMelBins = 32;
|
| 30 |
-
constexpr int kMelFrames = 8;
|
| 31 |
-
constexpr int kEmbeddingFrames = 76;
|
| 32 |
-
constexpr int kEmbeddingSize = 96;
|
| 33 |
-
constexpr int kFeatureFrames = 34;
|
| 34 |
-
using Clock = std::chrono::steady_clock;
|
| 35 |
-
|
| 36 |
-
double ElapsedSeconds(Clock::time_point begin, Clock::time_point end) {
|
| 37 |
-
return std::chrono::duration<double>(end - begin).count();
|
| 38 |
-
}
|
| 39 |
-
|
| 40 |
-
struct Args {
|
| 41 |
-
std::string models_dir = "models";
|
| 42 |
-
std::string weights = "config/openwakeword_mel_weights.bin";
|
| 43 |
-
std::string audio = "audio/openwakeword/alexa_test.wav";
|
| 44 |
-
float threshold = 0.5f;
|
| 45 |
-
};
|
| 46 |
-
|
| 47 |
-
void Usage(const char *program) {
|
| 48 |
-
std::printf(
|
| 49 |
-
"Usage: %s [--models-dir DIR] [--mel-weights FILE] [--audio WAV] "
|
| 50 |
-
"[--threshold VALUE]\n",
|
| 51 |
-
program);
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
Args ParseArgs(int argc, char **argv) {
|
| 55 |
-
Args args;
|
| 56 |
-
for (int i = 1; i < argc; ++i) {
|
| 57 |
-
const std::string key = argv[i];
|
| 58 |
-
auto value = [&]() -> std::string {
|
| 59 |
-
if (++i >= argc) throw std::runtime_error("Missing value for " + key);
|
| 60 |
-
return argv[i];
|
| 61 |
-
};
|
| 62 |
-
if (key == "--models-dir") {
|
| 63 |
-
args.models_dir = value();
|
| 64 |
-
} else if (key == "--mel-weights") {
|
| 65 |
-
args.weights = value();
|
| 66 |
-
} else if (key == "--audio") {
|
| 67 |
-
args.audio = value();
|
| 68 |
-
} else if (key == "--threshold") {
|
| 69 |
-
args.threshold = std::stof(value());
|
| 70 |
-
} else if (key == "-h" || key == "--help") {
|
| 71 |
-
Usage(argv[0]);
|
| 72 |
-
std::exit(0);
|
| 73 |
-
} else {
|
| 74 |
-
throw std::runtime_error("Unknown argument: " + key);
|
| 75 |
-
}
|
| 76 |
-
}
|
| 77 |
-
return args;
|
| 78 |
-
}
|
| 79 |
-
|
| 80 |
-
std::string Join(const std::string &left, const std::string &right) {
|
| 81 |
-
return left.empty() || left.back() == '/' ? left + right
|
| 82 |
-
: left + "/" + right;
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
class AxRuntime {
|
| 86 |
-
public:
|
| 87 |
-
AxRuntime() {
|
| 88 |
-
if (AX_SYS_Init() != 0) throw std::runtime_error("AX_SYS_Init failed");
|
| 89 |
-
sys_initialized_ = true;
|
| 90 |
-
AX_ENGINE_NPU_ATTR_T attr{};
|
| 91 |
-
if (AX_ENGINE_Init(&attr) != 0) {
|
| 92 |
-
AX_SYS_Deinit();
|
| 93 |
-
sys_initialized_ = false;
|
| 94 |
-
throw std::runtime_error("AX_ENGINE_Init failed");
|
| 95 |
-
}
|
| 96 |
-
engine_initialized_ = true;
|
| 97 |
-
}
|
| 98 |
-
~AxRuntime() {
|
| 99 |
-
if (engine_initialized_) AX_ENGINE_Deinit();
|
| 100 |
-
if (sys_initialized_) AX_SYS_Deinit();
|
| 101 |
-
}
|
| 102 |
-
|
| 103 |
-
private:
|
| 104 |
-
bool sys_initialized_ = false;
|
| 105 |
-
bool engine_initialized_ = false;
|
| 106 |
-
};
|
| 107 |
-
|
| 108 |
-
template <typename T>
|
| 109 |
-
T ReadScalar(std::istream &input) {
|
| 110 |
-
T value{};
|
| 111 |
-
input.read(reinterpret_cast<char *>(&value), sizeof(value));
|
| 112 |
-
if (!input) throw std::runtime_error("Truncated mel weight file");
|
| 113 |
-
return value;
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
struct MelWeights {
|
| 117 |
-
std::vector<float> real;
|
| 118 |
-
std::vector<float> imag;
|
| 119 |
-
std::vector<float> mel;
|
| 120 |
-
float floor = 0.0f;
|
| 121 |
-
|
| 122 |
-
static MelWeights Load(const std::string &path) {
|
| 123 |
-
std::ifstream input(path, std::ios::binary);
|
| 124 |
-
if (!input) throw std::runtime_error("Cannot open mel weights: " + path);
|
| 125 |
-
char magic[8]{};
|
| 126 |
-
input.read(magic, 8);
|
| 127 |
-
if (std::memcmp(magic, "OWWMEL1", 7) != 0 ||
|
| 128 |
-
ReadScalar<uint32_t>(input) != 1) {
|
| 129 |
-
throw std::runtime_error("Invalid openWakeWord mel weight file");
|
| 130 |
-
}
|
| 131 |
-
const uint32_t real_rows = ReadScalar<uint32_t>(input);
|
| 132 |
-
const uint32_t real_cols = ReadScalar<uint32_t>(input);
|
| 133 |
-
const uint32_t imag_rows = ReadScalar<uint32_t>(input);
|
| 134 |
-
const uint32_t imag_cols = ReadScalar<uint32_t>(input);
|
| 135 |
-
const uint32_t mel_rows = ReadScalar<uint32_t>(input);
|
| 136 |
-
const uint32_t mel_cols = ReadScalar<uint32_t>(input);
|
| 137 |
-
MelWeights result;
|
| 138 |
-
result.floor = ReadScalar<float>(input);
|
| 139 |
-
if (real_rows != kSpectrumBins || real_cols != kFftSize ||
|
| 140 |
-
imag_rows != kSpectrumBins || imag_cols != kFftSize ||
|
| 141 |
-
mel_rows != kSpectrumBins || mel_cols != kMelBins) {
|
| 142 |
-
throw std::runtime_error("Unexpected openWakeWord mel weight shapes");
|
| 143 |
-
}
|
| 144 |
-
result.real.resize(static_cast<std::size_t>(real_rows) * real_cols);
|
| 145 |
-
result.imag.resize(static_cast<std::size_t>(imag_rows) * imag_cols);
|
| 146 |
-
result.mel.resize(static_cast<std::size_t>(mel_rows) * mel_cols);
|
| 147 |
-
input.read(reinterpret_cast<char *>(result.real.data()),
|
| 148 |
-
result.real.size() * sizeof(float));
|
| 149 |
-
input.read(reinterpret_cast<char *>(result.imag.data()),
|
| 150 |
-
result.imag.size() * sizeof(float));
|
| 151 |
-
input.read(reinterpret_cast<char *>(result.mel.data()),
|
| 152 |
-
result.mel.size() * sizeof(float));
|
| 153 |
-
if (!input) throw std::runtime_error("Truncated openWakeWord mel weights");
|
| 154 |
-
return result;
|
| 155 |
-
}
|
| 156 |
-
};
|
| 157 |
-
|
| 158 |
-
std::array<float, kMelFrames * kMelBins> ComputeMel(
|
| 159 |
-
const std::array<float, kHistorySamples + kChunkSamples> &samples,
|
| 160 |
-
const MelWeights &weights) {
|
| 161 |
-
std::array<float, kMelFrames * kMelBins> result{};
|
| 162 |
-
std::array<float, kSpectrumBins> power{};
|
| 163 |
-
float max_db = -std::numeric_limits<float>::infinity();
|
| 164 |
-
for (int frame = 0; frame < kMelFrames; ++frame) {
|
| 165 |
-
const float *frame_samples = samples.data() + frame * 160;
|
| 166 |
-
for (int frequency = 0; frequency < kSpectrumBins; ++frequency) {
|
| 167 |
-
const float *real = weights.real.data() + frequency * kFftSize;
|
| 168 |
-
const float *imag = weights.imag.data() + frequency * kFftSize;
|
| 169 |
-
float real_sum = 0.0f;
|
| 170 |
-
float imag_sum = 0.0f;
|
| 171 |
-
for (int n = 0; n < kFftSize; ++n) {
|
| 172 |
-
real_sum += frame_samples[n] * real[n];
|
| 173 |
-
imag_sum += frame_samples[n] * imag[n];
|
| 174 |
-
}
|
| 175 |
-
power[frequency] = real_sum * real_sum + imag_sum * imag_sum;
|
| 176 |
-
}
|
| 177 |
-
for (int bin = 0; bin < kMelBins; ++bin) {
|
| 178 |
-
float value = 0.0f;
|
| 179 |
-
for (int frequency = 0; frequency < kSpectrumBins; ++frequency) {
|
| 180 |
-
value += power[frequency] *
|
| 181 |
-
weights.mel[frequency * kMelBins + bin];
|
| 182 |
-
}
|
| 183 |
-
value = std::max(value, weights.floor);
|
| 184 |
-
const float db = std::log(value) * 10.0f / 2.3025851249694824f;
|
| 185 |
-
result[frame * kMelBins + bin] = db;
|
| 186 |
-
max_db = std::max(max_db, db);
|
| 187 |
-
}
|
| 188 |
-
}
|
| 189 |
-
const float minimum = max_db - 80.0f;
|
| 190 |
-
for (float &value : result) {
|
| 191 |
-
value = std::max(value, minimum) / 10.0f + 2.0f;
|
| 192 |
-
}
|
| 193 |
-
return result;
|
| 194 |
-
}
|
| 195 |
-
|
| 196 |
-
struct Classifier {
|
| 197 |
-
std::string name;
|
| 198 |
-
int frames;
|
| 199 |
-
EngineWrapper engine;
|
| 200 |
-
std::vector<float> maximum;
|
| 201 |
-
};
|
| 202 |
-
|
| 203 |
-
void Run(const Args &args) {
|
| 204 |
-
const PcmWav wav = ReadPcmWav(args.audio);
|
| 205 |
-
if (wav.sample_rate != kSampleRate) {
|
| 206 |
-
throw std::runtime_error("Input WAV must use 16 kHz sample rate");
|
| 207 |
-
}
|
| 208 |
-
const double audio_seconds =
|
| 209 |
-
static_cast<double>(wav.samples.size()) / kSampleRate;
|
| 210 |
-
if (audio_seconds <= 0.0) {
|
| 211 |
-
throw std::runtime_error("Input WAV contains no samples");
|
| 212 |
-
}
|
| 213 |
-
const MelWeights weights = MelWeights::Load(args.weights);
|
| 214 |
-
const auto model_load_begin = Clock::now();
|
| 215 |
-
AxRuntime runtime;
|
| 216 |
-
|
| 217 |
-
EngineWrapper embedding;
|
| 218 |
-
if (embedding.Init(Join(args.models_dir,
|
| 219 |
-
"openwakeword__embedding_model.axmodel")) != 0) {
|
| 220 |
-
throw std::runtime_error("Failed to load embedding model");
|
| 221 |
-
}
|
| 222 |
-
const std::array<std::pair<const char *, int>, 6> definitions{{
|
| 223 |
-
{"alexa_v0.1", 16},
|
| 224 |
-
{"hey_jarvis_v0.1", 16},
|
| 225 |
-
{"hey_mycroft_v0.1", 16},
|
| 226 |
-
{"hey_rhasspy_v0.1", 16},
|
| 227 |
-
{"timer_v0.1", 34},
|
| 228 |
-
{"weather_v0.1", 22},
|
| 229 |
-
}};
|
| 230 |
-
std::array<Classifier, 6> classifiers;
|
| 231 |
-
for (std::size_t classifier_index = 0;
|
| 232 |
-
classifier_index < definitions.size(); ++classifier_index) {
|
| 233 |
-
const auto &definition = definitions[classifier_index];
|
| 234 |
-
Classifier &classifier = classifiers[classifier_index];
|
| 235 |
-
classifier.name = definition.first;
|
| 236 |
-
classifier.frames = definition.second;
|
| 237 |
-
const std::string model =
|
| 238 |
-
Join(args.models_dir, "openwakeword__" + classifier.name + ".axmodel");
|
| 239 |
-
if (classifier.engine.Init(model) != 0) {
|
| 240 |
-
throw std::runtime_error("Failed to load classifier: " + classifier.name);
|
| 241 |
-
}
|
| 242 |
-
const int output_bytes = classifier.engine.GetOutputSizeByName(
|
| 243 |
-
classifier.engine.OutputName(0));
|
| 244 |
-
if (output_bytes <= 0 || output_bytes % sizeof(float) != 0) {
|
| 245 |
-
throw std::runtime_error("Unexpected classifier output: " +
|
| 246 |
-
classifier.name);
|
| 247 |
-
}
|
| 248 |
-
classifier.maximum.assign(output_bytes / sizeof(float),
|
| 249 |
-
-std::numeric_limits<float>::infinity());
|
| 250 |
-
}
|
| 251 |
-
const double model_load_seconds =
|
| 252 |
-
ElapsedSeconds(model_load_begin, Clock::now());
|
| 253 |
-
|
| 254 |
-
std::vector<int16_t> padded = wav.samples;
|
| 255 |
-
const std::size_t remainder = padded.size() % kChunkSamples;
|
| 256 |
-
if (remainder != 0) padded.resize(padded.size() + kChunkSamples - remainder);
|
| 257 |
-
std::array<int16_t, kHistorySamples> history{};
|
| 258 |
-
std::array<float, kEmbeddingFrames * kMelBins> mel_buffer{};
|
| 259 |
-
mel_buffer.fill(1.0f);
|
| 260 |
-
std::array<float, kFeatureFrames * kEmbeddingSize> feature_buffer{};
|
| 261 |
-
int chunks = 0;
|
| 262 |
-
double feature_seconds = 0.0;
|
| 263 |
-
double npu_seconds = 0.0;
|
| 264 |
-
const auto inference_begin = Clock::now();
|
| 265 |
-
|
| 266 |
-
for (std::size_t start = 0; start < padded.size(); start += kChunkSamples) {
|
| 267 |
-
std::array<float, kHistorySamples + kChunkSamples> mel_input{};
|
| 268 |
-
for (int i = 0; i < kHistorySamples; ++i) mel_input[i] = history[i];
|
| 269 |
-
for (int i = 0; i < kChunkSamples; ++i) {
|
| 270 |
-
mel_input[kHistorySamples + i] = padded[start + i];
|
| 271 |
-
}
|
| 272 |
-
for (int i = 0; i < kHistorySamples; ++i) {
|
| 273 |
-
history[i] = padded[start + kChunkSamples - kHistorySamples + i];
|
| 274 |
-
}
|
| 275 |
-
const auto feature_begin = Clock::now();
|
| 276 |
-
const auto mel = ComputeMel(mel_input, weights);
|
| 277 |
-
feature_seconds += ElapsedSeconds(feature_begin, Clock::now());
|
| 278 |
-
std::memmove(mel_buffer.data(), mel_buffer.data() + kMelFrames * kMelBins,
|
| 279 |
-
(kEmbeddingFrames - kMelFrames) * kMelBins * sizeof(float));
|
| 280 |
-
std::memcpy(mel_buffer.data() +
|
| 281 |
-
(kEmbeddingFrames - kMelFrames) * kMelBins,
|
| 282 |
-
mel.data(), mel.size() * sizeof(float));
|
| 283 |
-
|
| 284 |
-
const std::string &embedding_input = embedding.InputName(0);
|
| 285 |
-
if (embedding.SetInputByName(embedding_input, mel_buffer.data(),
|
| 286 |
-
mel_buffer.size() * sizeof(float)) != 0) {
|
| 287 |
-
throw std::runtime_error("Failed to set embedding input");
|
| 288 |
-
}
|
| 289 |
-
const auto embedding_begin = Clock::now();
|
| 290 |
-
const int embedding_ret = embedding.RunSync();
|
| 291 |
-
npu_seconds += ElapsedSeconds(embedding_begin, Clock::now());
|
| 292 |
-
if (embedding_ret != 0) {
|
| 293 |
-
throw std::runtime_error("Embedding inference failed");
|
| 294 |
-
}
|
| 295 |
-
std::array<float, kEmbeddingSize> feature{};
|
| 296 |
-
if (embedding.GetOutputByName(embedding.OutputName(0), feature.data(),
|
| 297 |
-
feature.size() * sizeof(float)) != 0) {
|
| 298 |
-
throw std::runtime_error("Failed to read embedding output");
|
| 299 |
-
}
|
| 300 |
-
std::memmove(feature_buffer.data(), feature_buffer.data() + kEmbeddingSize,
|
| 301 |
-
(kFeatureFrames - 1) * kEmbeddingSize * sizeof(float));
|
| 302 |
-
std::memcpy(feature_buffer.data() +
|
| 303 |
-
(kFeatureFrames - 1) * kEmbeddingSize,
|
| 304 |
-
feature.data(), feature.size() * sizeof(float));
|
| 305 |
-
|
| 306 |
-
for (Classifier &classifier : classifiers) {
|
| 307 |
-
const float *input =
|
| 308 |
-
feature_buffer.data() +
|
| 309 |
-
(kFeatureFrames - classifier.frames) * kEmbeddingSize;
|
| 310 |
-
const std::string &input_name = classifier.engine.InputName(0);
|
| 311 |
-
if (classifier.engine.SetInputByName(
|
| 312 |
-
input_name, input,
|
| 313 |
-
classifier.frames * kEmbeddingSize * sizeof(float)) != 0) {
|
| 314 |
-
throw std::runtime_error("Failed to set classifier input: " +
|
| 315 |
-
classifier.name);
|
| 316 |
-
}
|
| 317 |
-
const auto classifier_begin = Clock::now();
|
| 318 |
-
const int classifier_ret = classifier.engine.RunSync();
|
| 319 |
-
npu_seconds += ElapsedSeconds(classifier_begin, Clock::now());
|
| 320 |
-
if (classifier_ret != 0) {
|
| 321 |
-
throw std::runtime_error("Classifier inference failed: " +
|
| 322 |
-
classifier.name);
|
| 323 |
-
}
|
| 324 |
-
std::vector<float> output(classifier.maximum.size());
|
| 325 |
-
if (classifier.engine.GetOutputByName(
|
| 326 |
-
classifier.engine.OutputName(0), output.data(),
|
| 327 |
-
output.size() * sizeof(float)) != 0) {
|
| 328 |
-
throw std::runtime_error("Failed to read classifier output");
|
| 329 |
-
}
|
| 330 |
-
for (std::size_t i = 0; i < output.size(); ++i) {
|
| 331 |
-
classifier.maximum[i] = std::max(classifier.maximum[i], output[i]);
|
| 332 |
-
}
|
| 333 |
-
}
|
| 334 |
-
++chunks;
|
| 335 |
-
}
|
| 336 |
-
const double inference_seconds =
|
| 337 |
-
ElapsedSeconds(inference_begin, Clock::now());
|
| 338 |
-
const double rtf = inference_seconds / audio_seconds;
|
| 339 |
-
|
| 340 |
-
std::printf("\nopenWakeWord C++ inference complete\n");
|
| 341 |
-
std::printf("target: %s\naudio: %s\nchunks: %d\nthreshold: %.3f\n",
|
| 342 |
-
AXERA_TARGET_NAME, args.audio.c_str(), chunks, args.threshold);
|
| 343 |
-
bool detected = false;
|
| 344 |
-
for (const Classifier &classifier : classifiers) {
|
| 345 |
-
std::printf("%-22s", classifier.name.c_str());
|
| 346 |
-
float score = -std::numeric_limits<float>::infinity();
|
| 347 |
-
for (std::size_t i = 0; i < classifier.maximum.size(); ++i) {
|
| 348 |
-
const float value = classifier.maximum[i];
|
| 349 |
-
std::printf(" %.6f", value);
|
| 350 |
-
if (classifier.name != "timer_v0.1" || i != 0) {
|
| 351 |
-
score = std::max(score, value);
|
| 352 |
-
}
|
| 353 |
-
}
|
| 354 |
-
if (score >= args.threshold) {
|
| 355 |
-
std::printf(" WAKEUP");
|
| 356 |
-
detected = true;
|
| 357 |
-
}
|
| 358 |
-
std::printf("\n");
|
| 359 |
-
}
|
| 360 |
-
std::printf("detected: %s\n", detected ? "true" : "false");
|
| 361 |
-
std::printf("audio_seconds: %.6f\n", audio_seconds);
|
| 362 |
-
std::printf("feature_seconds: %.6f\n", feature_seconds);
|
| 363 |
-
std::printf("npu_seconds: %.6f\n", npu_seconds);
|
| 364 |
-
std::printf("model_load_seconds: %.6f\n", model_load_seconds);
|
| 365 |
-
std::printf("inference_seconds: %.6f\n", inference_seconds);
|
| 366 |
-
std::printf("rtf: %.6f\n", rtf);
|
| 367 |
-
}
|
| 368 |
-
} // namespace
|
| 369 |
-
|
| 370 |
-
int main(int argc, char **argv) {
|
| 371 |
-
try {
|
| 372 |
-
const Args args = ParseArgs(argc, argv);
|
| 373 |
-
Run(args);
|
| 374 |
-
return 0;
|
| 375 |
-
} catch (const std::exception &error) {
|
| 376 |
-
std::fprintf(stderr, "ERROR: %s\n", error.what());
|
| 377 |
-
return 1;
|
| 378 |
-
}
|
| 379 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/src/engine_wrapper.cpp
DELETED
|
@@ -1,197 +0,0 @@
|
|
| 1 |
-
#include "engine_wrapper.hpp"
|
| 2 |
-
|
| 3 |
-
#include <cstdio>
|
| 4 |
-
#include <cstring>
|
| 5 |
-
#include <fstream>
|
| 6 |
-
#include <vector>
|
| 7 |
-
|
| 8 |
-
#include "ax_sys_api.h"
|
| 9 |
-
|
| 10 |
-
namespace {
|
| 11 |
-
constexpr int kAlignment = 128;
|
| 12 |
-
|
| 13 |
-
void FreeIo(AX_ENGINE_IO_T *io) {
|
| 14 |
-
for (AX_U32 i = 0; io->pInputs && i < io->nInputSize; ++i) {
|
| 15 |
-
if (io->pInputs[i].pVirAddr) {
|
| 16 |
-
AX_SYS_MemFree(io->pInputs[i].phyAddr, io->pInputs[i].pVirAddr);
|
| 17 |
-
}
|
| 18 |
-
}
|
| 19 |
-
for (AX_U32 i = 0; io->pOutputs && i < io->nOutputSize; ++i) {
|
| 20 |
-
if (io->pOutputs[i].pVirAddr) {
|
| 21 |
-
AX_SYS_MemFree(io->pOutputs[i].phyAddr, io->pOutputs[i].pVirAddr);
|
| 22 |
-
}
|
| 23 |
-
}
|
| 24 |
-
delete[] io->pInputs;
|
| 25 |
-
delete[] io->pOutputs;
|
| 26 |
-
std::memset(io, 0, sizeof(*io));
|
| 27 |
-
}
|
| 28 |
-
} // namespace
|
| 29 |
-
|
| 30 |
-
EngineWrapper::~EngineWrapper() { Release(); }
|
| 31 |
-
|
| 32 |
-
int EngineWrapper::Init(const std::string &path) {
|
| 33 |
-
std::ifstream input(path, std::ios::binary);
|
| 34 |
-
if (!input) {
|
| 35 |
-
std::fprintf(stderr, "Failed to read model: %s\n", path.c_str());
|
| 36 |
-
return -1;
|
| 37 |
-
}
|
| 38 |
-
input.seekg(0, std::ios::end);
|
| 39 |
-
const std::streamoff model_size = input.tellg();
|
| 40 |
-
if (model_size <= 0 || model_size > static_cast<std::streamoff>(UINT32_MAX)) {
|
| 41 |
-
std::fprintf(stderr, "Invalid model size: %s\n", path.c_str());
|
| 42 |
-
return -1;
|
| 43 |
-
}
|
| 44 |
-
input.seekg(0, std::ios::beg);
|
| 45 |
-
std::vector<char> model(static_cast<std::size_t>(model_size));
|
| 46 |
-
input.read(model.data(), model_size);
|
| 47 |
-
if (!input) {
|
| 48 |
-
std::fprintf(stderr, "Failed to read model: %s\n", path.c_str());
|
| 49 |
-
return -1;
|
| 50 |
-
}
|
| 51 |
-
const AX_S32 create_ret = AX_ENGINE_CreateHandle(
|
| 52 |
-
&handle_, model.data(), static_cast<AX_U32>(model.size()));
|
| 53 |
-
if (create_ret != 0 || !handle_) {
|
| 54 |
-
std::fprintf(stderr, "AX_ENGINE_CreateHandle failed: %s ret=0x%x\n",
|
| 55 |
-
path.c_str(), static_cast<unsigned>(create_ret));
|
| 56 |
-
return -1;
|
| 57 |
-
}
|
| 58 |
-
if (AX_ENGINE_CreateContext(handle_) != 0 ||
|
| 59 |
-
AX_ENGINE_GetIOInfo(handle_, &info_) != 0 || !info_) {
|
| 60 |
-
Release();
|
| 61 |
-
return -1;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
-
io_.nInputSize = info_->nInputSize;
|
| 65 |
-
io_.nOutputSize = info_->nOutputSize;
|
| 66 |
-
io_.pInputs = new AX_ENGINE_IO_BUFFER_T[io_.nInputSize]{};
|
| 67 |
-
io_.pOutputs = new AX_ENGINE_IO_BUFFER_T[io_.nOutputSize]{};
|
| 68 |
-
AX_S8 input_tag[] = "KWS-INPUT";
|
| 69 |
-
AX_S8 output_tag[] = "KWS-OUTPUT";
|
| 70 |
-
for (AX_U32 i = 0; i < io_.nInputSize; ++i) {
|
| 71 |
-
io_.pInputs[i].nSize = info_->pInputs[i].nSize;
|
| 72 |
-
if (AX_SYS_MemAlloc(&io_.pInputs[i].phyAddr,
|
| 73 |
-
&io_.pInputs[i].pVirAddr, io_.pInputs[i].nSize,
|
| 74 |
-
kAlignment, input_tag) != 0) {
|
| 75 |
-
Release();
|
| 76 |
-
return -1;
|
| 77 |
-
}
|
| 78 |
-
const std::string name = info_->pInputs[i].pName;
|
| 79 |
-
inputs_[name] = static_cast<int>(i);
|
| 80 |
-
input_names_.push_back(name);
|
| 81 |
-
}
|
| 82 |
-
for (AX_U32 i = 0; i < io_.nOutputSize; ++i) {
|
| 83 |
-
io_.pOutputs[i].nSize = info_->pOutputs[i].nSize;
|
| 84 |
-
if (AX_SYS_MemAlloc(&io_.pOutputs[i].phyAddr,
|
| 85 |
-
&io_.pOutputs[i].pVirAddr, io_.pOutputs[i].nSize,
|
| 86 |
-
kAlignment, output_tag) != 0) {
|
| 87 |
-
Release();
|
| 88 |
-
return -1;
|
| 89 |
-
}
|
| 90 |
-
const std::string name = info_->pOutputs[i].pName;
|
| 91 |
-
outputs_[name] = static_cast<int>(i);
|
| 92 |
-
output_names_.push_back(name);
|
| 93 |
-
}
|
| 94 |
-
initialized_ = true;
|
| 95 |
-
std::printf("Loaded %s (inputs=%u outputs=%u)\n", path.c_str(),
|
| 96 |
-
io_.nInputSize, io_.nOutputSize);
|
| 97 |
-
return 0;
|
| 98 |
-
}
|
| 99 |
-
|
| 100 |
-
void EngineWrapper::Release() {
|
| 101 |
-
FreeIo(&io_);
|
| 102 |
-
if (handle_) AX_ENGINE_DestroyHandle(handle_);
|
| 103 |
-
initialized_ = false;
|
| 104 |
-
handle_ = nullptr;
|
| 105 |
-
info_ = nullptr;
|
| 106 |
-
inputs_.clear();
|
| 107 |
-
outputs_.clear();
|
| 108 |
-
input_names_.clear();
|
| 109 |
-
output_names_.clear();
|
| 110 |
-
}
|
| 111 |
-
|
| 112 |
-
int EngineWrapper::InputIndex(const std::string &name) const {
|
| 113 |
-
const auto it = inputs_.find(name);
|
| 114 |
-
return it == inputs_.end() ? -1 : it->second;
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
int EngineWrapper::OutputIndex(const std::string &name) const {
|
| 118 |
-
const auto it = outputs_.find(name);
|
| 119 |
-
return it == outputs_.end() ? -1 : it->second;
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
int EngineWrapper::SetInputByName(const std::string &name, const void *data,
|
| 123 |
-
std::size_t size) {
|
| 124 |
-
const int index = InputIndex(name);
|
| 125 |
-
if (!initialized_ || index < 0 || !data) return -1;
|
| 126 |
-
const std::size_t expected = io_.pInputs[index].nSize;
|
| 127 |
-
if (size != 0 && size != expected) return -1;
|
| 128 |
-
std::memcpy(io_.pInputs[index].pVirAddr, data, expected);
|
| 129 |
-
return 0;
|
| 130 |
-
}
|
| 131 |
-
|
| 132 |
-
int EngineWrapper::ZeroInputByName(const std::string &name) {
|
| 133 |
-
const int index = InputIndex(name);
|
| 134 |
-
if (!initialized_ || index < 0) return -1;
|
| 135 |
-
std::memset(io_.pInputs[index].pVirAddr, 0, io_.pInputs[index].nSize);
|
| 136 |
-
return 0;
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
int EngineWrapper::RunSync() {
|
| 140 |
-
if (!initialized_) return -1;
|
| 141 |
-
for (AX_U32 i = 0; i < io_.nInputSize; ++i) {
|
| 142 |
-
const AX_S32 flush_ret = AX_SYS_MflushCache(
|
| 143 |
-
io_.pInputs[i].phyAddr, io_.pInputs[i].pVirAddr,
|
| 144 |
-
io_.pInputs[i].nSize);
|
| 145 |
-
if (flush_ret != 0) {
|
| 146 |
-
std::fprintf(stderr, "AX_SYS_MflushCache failed input=%u ret=0x%x\n", i,
|
| 147 |
-
static_cast<unsigned>(flush_ret));
|
| 148 |
-
return flush_ret;
|
| 149 |
-
}
|
| 150 |
-
}
|
| 151 |
-
const AX_S32 ret = AX_ENGINE_RunSync(handle_, &io_);
|
| 152 |
-
if (ret != 0) {
|
| 153 |
-
std::fprintf(stderr, "AX_ENGINE_RunSync failed ret=0x%x\n",
|
| 154 |
-
static_cast<unsigned>(ret));
|
| 155 |
-
return ret;
|
| 156 |
-
}
|
| 157 |
-
for (AX_U32 i = 0; i < io_.nOutputSize; ++i) {
|
| 158 |
-
const AX_S32 invalidate_ret = AX_SYS_MinvalidateCache(
|
| 159 |
-
io_.pOutputs[i].phyAddr, io_.pOutputs[i].pVirAddr,
|
| 160 |
-
io_.pOutputs[i].nSize);
|
| 161 |
-
if (invalidate_ret != 0) {
|
| 162 |
-
std::fprintf(stderr,
|
| 163 |
-
"AX_SYS_MinvalidateCache failed output=%u ret=0x%x\n", i,
|
| 164 |
-
static_cast<unsigned>(invalidate_ret));
|
| 165 |
-
return invalidate_ret;
|
| 166 |
-
}
|
| 167 |
-
}
|
| 168 |
-
return 0;
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
int EngineWrapper::GetOutputByName(const std::string &name, void *data,
|
| 172 |
-
std::size_t size) const {
|
| 173 |
-
const int index = OutputIndex(name);
|
| 174 |
-
if (!initialized_ || index < 0 || !data) return -1;
|
| 175 |
-
const std::size_t expected = io_.pOutputs[index].nSize;
|
| 176 |
-
if (size != 0 && size != expected) return -1;
|
| 177 |
-
std::memcpy(data, io_.pOutputs[index].pVirAddr, expected);
|
| 178 |
-
return 0;
|
| 179 |
-
}
|
| 180 |
-
|
| 181 |
-
int EngineWrapper::GetInputSizeByName(const std::string &name) const {
|
| 182 |
-
const int index = InputIndex(name);
|
| 183 |
-
return index < 0 ? -1 : static_cast<int>(io_.pInputs[index].nSize);
|
| 184 |
-
}
|
| 185 |
-
|
| 186 |
-
int EngineWrapper::GetOutputSizeByName(const std::string &name) const {
|
| 187 |
-
const int index = OutputIndex(name);
|
| 188 |
-
return index < 0 ? -1 : static_cast<int>(io_.pOutputs[index].nSize);
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
const std::string &EngineWrapper::InputName(std::size_t index) const {
|
| 192 |
-
return input_names_.at(index);
|
| 193 |
-
}
|
| 194 |
-
|
| 195 |
-
const std::string &EngineWrapper::OutputName(std::size_t index) const {
|
| 196 |
-
return output_names_.at(index);
|
| 197 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/src/engine_wrapper.hpp
DELETED
|
@@ -1,43 +0,0 @@
|
|
| 1 |
-
#pragma once
|
| 2 |
-
|
| 3 |
-
#include <cstddef>
|
| 4 |
-
#include <string>
|
| 5 |
-
#include <unordered_map>
|
| 6 |
-
#include <vector>
|
| 7 |
-
|
| 8 |
-
#include "ax_engine_api.h"
|
| 9 |
-
|
| 10 |
-
class EngineWrapper {
|
| 11 |
-
public:
|
| 12 |
-
EngineWrapper() = default;
|
| 13 |
-
~EngineWrapper();
|
| 14 |
-
|
| 15 |
-
EngineWrapper(const EngineWrapper &) = delete;
|
| 16 |
-
EngineWrapper &operator=(const EngineWrapper &) = delete;
|
| 17 |
-
|
| 18 |
-
int Init(const std::string &model_path);
|
| 19 |
-
void Release();
|
| 20 |
-
int SetInputByName(const std::string &name, const void *data,
|
| 21 |
-
std::size_t size = 0);
|
| 22 |
-
int ZeroInputByName(const std::string &name);
|
| 23 |
-
int RunSync();
|
| 24 |
-
int GetOutputByName(const std::string &name, void *data,
|
| 25 |
-
std::size_t size = 0) const;
|
| 26 |
-
int GetInputSizeByName(const std::string &name) const;
|
| 27 |
-
int GetOutputSizeByName(const std::string &name) const;
|
| 28 |
-
const std::string &InputName(std::size_t index) const;
|
| 29 |
-
const std::string &OutputName(std::size_t index) const;
|
| 30 |
-
|
| 31 |
-
private:
|
| 32 |
-
int InputIndex(const std::string &name) const;
|
| 33 |
-
int OutputIndex(const std::string &name) const;
|
| 34 |
-
|
| 35 |
-
bool initialized_ = false;
|
| 36 |
-
AX_ENGINE_HANDLE handle_ = nullptr;
|
| 37 |
-
AX_ENGINE_IO_INFO_T *info_ = nullptr;
|
| 38 |
-
AX_ENGINE_IO_T io_{};
|
| 39 |
-
std::unordered_map<std::string, int> inputs_;
|
| 40 |
-
std::unordered_map<std::string, int> outputs_;
|
| 41 |
-
std::vector<std::string> input_names_;
|
| 42 |
-
std::vector<std::string> output_names_;
|
| 43 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cpp/src/wav_reader.hpp
DELETED
|
@@ -1,80 +0,0 @@
|
|
| 1 |
-
#pragma once
|
| 2 |
-
|
| 3 |
-
#include <cstdint>
|
| 4 |
-
#include <cstring>
|
| 5 |
-
#include <fstream>
|
| 6 |
-
#include <stdexcept>
|
| 7 |
-
#include <string>
|
| 8 |
-
#include <vector>
|
| 9 |
-
|
| 10 |
-
struct PcmWav {
|
| 11 |
-
int sample_rate = 0;
|
| 12 |
-
std::vector<int16_t> samples;
|
| 13 |
-
};
|
| 14 |
-
|
| 15 |
-
inline uint16_t ReadLe16(std::istream &input) {
|
| 16 |
-
uint8_t bytes[2]{};
|
| 17 |
-
input.read(reinterpret_cast<char *>(bytes), 2);
|
| 18 |
-
return static_cast<uint16_t>(bytes[0]) |
|
| 19 |
-
(static_cast<uint16_t>(bytes[1]) << 8);
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
inline uint32_t ReadLe32(std::istream &input) {
|
| 23 |
-
uint8_t bytes[4]{};
|
| 24 |
-
input.read(reinterpret_cast<char *>(bytes), 4);
|
| 25 |
-
return static_cast<uint32_t>(bytes[0]) |
|
| 26 |
-
(static_cast<uint32_t>(bytes[1]) << 8) |
|
| 27 |
-
(static_cast<uint32_t>(bytes[2]) << 16) |
|
| 28 |
-
(static_cast<uint32_t>(bytes[3]) << 24);
|
| 29 |
-
}
|
| 30 |
-
|
| 31 |
-
inline PcmWav ReadPcmWav(const std::string &path) {
|
| 32 |
-
std::ifstream input(path, std::ios::binary);
|
| 33 |
-
if (!input) throw std::runtime_error("Cannot open WAV: " + path);
|
| 34 |
-
char riff[4]{};
|
| 35 |
-
char wave[4]{};
|
| 36 |
-
input.read(riff, 4);
|
| 37 |
-
ReadLe32(input);
|
| 38 |
-
input.read(wave, 4);
|
| 39 |
-
if (std::memcmp(riff, "RIFF", 4) != 0 ||
|
| 40 |
-
std::memcmp(wave, "WAVE", 4) != 0) {
|
| 41 |
-
throw std::runtime_error("Invalid RIFF/WAVE file: " + path);
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
uint16_t format = 0;
|
| 45 |
-
uint16_t channels = 0;
|
| 46 |
-
uint16_t bits = 0;
|
| 47 |
-
uint32_t sample_rate = 0;
|
| 48 |
-
std::vector<uint8_t> data;
|
| 49 |
-
while (input && (!format || data.empty())) {
|
| 50 |
-
char id[4]{};
|
| 51 |
-
input.read(id, 4);
|
| 52 |
-
if (!input) break;
|
| 53 |
-
const uint32_t size = ReadLe32(input);
|
| 54 |
-
if (std::memcmp(id, "fmt ", 4) == 0) {
|
| 55 |
-
if (size < 16) throw std::runtime_error("Invalid WAV fmt chunk");
|
| 56 |
-
format = ReadLe16(input);
|
| 57 |
-
channels = ReadLe16(input);
|
| 58 |
-
sample_rate = ReadLe32(input);
|
| 59 |
-
ReadLe32(input);
|
| 60 |
-
ReadLe16(input);
|
| 61 |
-
bits = ReadLe16(input);
|
| 62 |
-
input.seekg(size - 16, std::ios::cur);
|
| 63 |
-
} else if (std::memcmp(id, "data", 4) == 0) {
|
| 64 |
-
data.resize(size);
|
| 65 |
-
input.read(reinterpret_cast<char *>(data.data()), size);
|
| 66 |
-
} else {
|
| 67 |
-
input.seekg(size, std::ios::cur);
|
| 68 |
-
}
|
| 69 |
-
if (size & 1U) input.seekg(1, std::ios::cur);
|
| 70 |
-
}
|
| 71 |
-
if (format != 1 || channels != 1 || bits != 16 || sample_rate == 0 ||
|
| 72 |
-
data.empty() || data.size() % sizeof(int16_t) != 0) {
|
| 73 |
-
throw std::runtime_error("Expected mono 16-bit PCM WAV: " + path);
|
| 74 |
-
}
|
| 75 |
-
PcmWav result;
|
| 76 |
-
result.sample_rate = static_cast<int>(sample_rate);
|
| 77 |
-
result.samples.resize(data.size() / sizeof(int16_t));
|
| 78 |
-
std::memcpy(result.samples.data(), data.data(), data.size());
|
| 79 |
-
return result;
|
| 80 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/__pycache__/openwakeword_ax.cpython-312.pyc
DELETED
|
Binary file (16.9 kB)
|
|
|
scripts/__pycache__/openwakeword_ax650.cpython-312.pyc
DELETED
|
Binary file (16.8 kB)
|
|
|
scripts/__pycache__/runtime.cpython-312.pyc
DELETED
|
Binary file (9.76 kB)
|
|
|